OpenID i PHP

W PHP dość łatwo zaimplementować mechanizm uwierzytelniania bazujący na openID. Na openidenabled.com znajdziemy bibliotekę o dość szerokich możliwościach. Na phpclasses.org znajdziemy znacznie prostszą klasę "Simple OpenID PHP Class", z której skorzystamy w tym artykule.

Simple OpenID PHP Class

Oto przykładowy, bazowy kod obsługi openID za pomocą tej kasy. Zapisz go jako openid-example.php w katalogu z plikiem klasy i podając ścieżkę w kodzie:
<?php
include 'class.openid.php';

if ($_POST['openid_action'] == "login"){ // Get identity from user and redirect browser to OpenID Server
	$openid = new SimpleOpenID;
	$openid->SetIdentity($_POST['openid_url']);
	$openid->SetTrustRoot('http://' . $_SERVER["HTTP_HOST"] .'/ŚCIEŻKA/DO/openid-example.php');
	$openid->SetRequiredFields(array('email','fullname'));
	$openid->SetOptionalFields(array('dob','gender','postcode','country','language','timezone'));
	if ($openid->GetOpenIDServer()){
		$openid->SetApprovedURL('http://' . $_SERVER["HTTP_HOST"] .'/ŚCIEŻKA/DO/openid-example.php');  	// Send Response from OpenID server to this script
		$openid->Redirect(); 	// This will redirect user to OpenID Server
	}else{
		$error = $openid->GetError();
		echo "ERROR CODE: " . $error['code'] . "<br>";
		echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
	}
	exit;
}
else if($_GET['openid_mode'] == 'id_res'){ 	// Perform HTTP Request to OpenID server to validate key
	$openid = new SimpleOpenID;
	$openid->SetIdentity($_GET['openid_identity']);
	$openid_validation_result = $openid->ValidateWithServer();
	if ($openid_validation_result == true){ 		// OK HERE KEY IS VALID
		echo "ZALOGOWANY POPRAWNIE";
	}else if($openid->IsError() == true){			// ON THE WAY, WE GOT SOME ERROR
		$error = $openid->GetError();
		echo "ERROR CODE: " . $error['code'] . "<br>";
		echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
	}else{											// Signature Verification Failed
		echo "INVALID AUTHORIZATION";
	}
}else if ($_GET['openid_mode'] == 'cancel'){ // User Canceled your Request
	echo "USER CANCELED REQUEST";
}
?>

<form action="openid-example.php" method="post" onsubmit="this.login.disabled=true;">
<input type="hidden" name="openid_action" value="login"><input type="text" name="openid_url" class="openid_login"><input type="submit" name="login" value="login">
</form>
Wystarczy w formularzy podać nasze openID i wysłać formularz. Jeżeli wszystko przebiegnie pomyślnie powinniśmy zobaczyć "ZALOGOWANY POPRAWNIE". Powyższy kod zajmuje się obsługą openID ale nie loguje nas po poprawnej operacji uwierzytelniania z OpenID. Musimy dodać np. cookie by "zapisać" stan zalogowania użytkownika.

A więc dodajemy ciasteczka:
<?php
// tajny łańcuch zabezpieczający hasze przed podmianą
define('SECRET_KEY', 'małpa w czerwonym jest ojcem dziecka anety krawczyk');
include 'class.openid.php';

// wylogowanie - usuń cookie i przekieruj
IF(isset($_GET['logout']))
	{
	setcookie("openid_cookie", NULL, time()-172800, '/', '', '0');
	header('Location: openid-example.php');
	}

// jeżeli jest cookie to user zalogowany
IF(isset($_COOKIE['openid_cookie']))
	{
	$array = unserialize(base64_decode($_COOKIE['openid_cookie']));
	IF(is_array($array) and $array['hash'] =  sha1(SECRET_KEY.$array['id']))
		{
		echo '<h1>Zalogowany przez openID jako <a href="'.$array['id'].'">'.$array['id'].'</a></h1><a href="openid-example.php?logout=ok">wyloguj</a>';
		}
	else
		{
		echo 'Błędne Cookie';
		}
	}
// brak cookie - logowanie za pomocą openID
else
	{
	if ($_POST['openid_action'] == "login")
		{ // Get identity from user and redirect browser to OpenID Server
			$openid = new SimpleOpenID;
			$openid->SetIdentity($_POST['openid_url']);
			$openid->SetTrustRoot('http://' . $_SERVER["HTTP_HOST"] .'/ŚCIEŻKA/DO/openid-example.php');
			$openid->SetRequiredFields(array('email','fullname'));
			$openid->SetOptionalFields(array('dob','gender','postcode','country','language','timezone'));
			if ($openid->GetOpenIDServer()){
				$openid->SetApprovedURL('http://' . $_SERVER["HTTP_HOST"] .'/ŚCIEŻKA/DO/openid-example.php');  	// Send Response from OpenID server to this script
				$openid->Redirect(); 	// This will redirect user to OpenID Server
			}else{
				$error = $openid->GetError();
				echo "ERROR CODE: " . $error['code'] . "<br>";
				echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
			}
			exit;
		}
	else if($_GET['openid_mode'] == 'id_res')
		{ 	// Perform HTTP Request to OpenID server to validate key
			$openid = new SimpleOpenID;
			$openid->SetIdentity($_GET['openid_identity']);
			$openid_validation_result = $openid->ValidateWithServer();
			if ($openid_validation_result == true){ 		// OK HERE KEY IS VALID
				// dane poprawne - tworzymy cookie i przekierowujemy
				$array = array('id' => $_GET['openid_identity'], 'hash' => sha1(SECRET_KEY.$_GET['openid_identity']));
				setcookie("openid_cookie", base64_encode(serialize($array)), time()+172800, '/', '', '0');
				echo 'ZALOGOWANY POPRAWNIE<br />Przenoszę na stronę główną<META HTTP-EQUIV="Refresh" CONTENT="1; URL=openid-example.php">';
				exit();
			}else if($openid->IsError() == true){			// ON THE WAY, WE GOT SOME ERROR
				$error = $openid->GetError();
				echo "ERROR CODE: " . $error['code'] . "<br>";
				echo "ERROR DESCRIPTION: " . $error['description'] . "<br>";
			}else{											// Signature Verification Failed
				echo "INVALID AUTHORIZATION";
			}
		}else if ($_GET['openid_mode'] == 'cancel')
		{ // User Canceled your Request
		echo "USER CANCELED REQUEST";
		}
	echo '<form action="openid-example.php" method="post" onsubmit="this.login.disabled=true;">
<input type="hidden" name="openid_action" value="login"><input type="text" name="openid_url" class="openid_login"><input type="submit" name="login" value="login">
</form>';
	}
Co zmieniliśmy ? - teraz w przypadku uwierzytelnienia za pomocą openID tworzone jest cookie "openid_cookie" zawierające hasz i nasz login openID. Ot cała filozofia. Obecny w skrypcie SECRET_KEY przeciwdziała możliwości utworzenia przez atakującego własnego cookie. Nie problem otrzymać hasz sha1('dowolna wartość') ale dodając znany tylko autorowi kodu dodatkowy łańcuch uniemożliwia stworzenie hasza (i spreparowania cookie) przez atakującego. Powyższy kod jest tylko przykładem, wymaga kilku usprawnień jak wyświetlanie, odpowiednią obsługę błędów no i ogólne dopasowanie go do własnej aplikacji. Można też skorzystać z bazy danych i oprócz hasza sprawdzać np. IP użytkownika (czy obecne jest takie samo jak to przy logowaniu)

Zobacz Także

Wordpress i openID
RkBlog

PHP w Akcji, 14 July 2008

Comment article
Comment article RkBlog main page Search RSS Contact