|
|
Глава 34. HTTP-Аутентификация в PHP
HTTP-Аутентификация в PHP возможна только в том случае, когда он выполняется
как модуль веб-сервера Apache, и, соответственно, недоступна для CGI-версии.
Ваш скрипт может использовать функцию header() для того,
чтобы отправить браузеру клиента сообщение "Authentication Required",
что в свою очередь приведет к появлению диалогового окна для ввода имени
пользователя и пароля. После того как клиент ввел свое имя и пароль,
скрипт будет вызван повторно, но уже с
предопределенными переменными
PHP_AUTH_USER, PHP_AUTH_PW
и AUTH_TYPE, которые соответственно содержат
имя пользователя, пароль и тип аутентификации. Эти переменные могут быть
найдены в массиве $_SERVER и
$HTTP_SERVER_VARS. В настоящее время поддерживается только
"Basic"-аутентификация. Также вы можете ознакомится с более детальным
описанием функции header().
Пример фрагмента скрипта, который вынуждает клиента авторизироваться для
просмотра страницы:
Пример 34-1. Пример HTTP-аутентификации |
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Текст, отправляемый в том случае,
если пользователь нажал кнопку Cancel';
exit;
} else {
echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
echo "<p>Вы ввели пароль {$_SERVER['PHP_AUTH_PW']}.</p>";
}
?>
|
|
Замечание касательно совместимости:
Будьте особенно внимательны при указании HTTP-заголовков. Для того, чтобы
гарантировать максимальную совместимость с наибольшим количеством
различных клиентов, слово "Basic" должно быть написано с большой буквы "B",
регион (realm) должен быть взят в двойный (не одинарные!) кавычки,
и ровно один пробел должен предшествовать коду 401 в заголовке
HTTP/1.0 401.
Вместо простого отображения на экране переменных PHP_AUTH_USER
и PHP_AUTH_PW, вам, возможно, понадобится
проверить их корректность. Используйте для этого запрос к базе
данных или поиск пользователя в dbm-файле.
Вы можете пронаблюдать особенности работы браузера Internet Explorer.
Он очень требователен к параметру передаваемых заголовков. Указание
заголовка WWW-Authenticate перед отправкой статуса
HTTP/1.0 401 является небольшой хитростью.
Начиная с PHP 4.3.0, для того, чтобы предотвратить написание
кем-либо скрипта, раскрывающего пароль к странице, которая использует
внешнюю аутентификацию, переменные PHP_AUTH
не устанавливаются в случае, если данная страница использует
внешнюю аутентификацию и установлен безопасный режим. Несмотря на это,
переменная REMOTE_USER может использоваться для
аутентификации пользователя, прошедшего внешнюю аутентификацию.
Таким образом, вы всегда можете воспользоваться переменной
$_SERVER['REMOTE_USER'].
Замечание касательно конфигурации:
PHP использует указание директивы AuthType для
указания того, используется внешняя аутентификация или нет.
Следует заметить, что все вышесказанное не предотвращает похищения паролей к
страницам, требующим авторизацию, кем-либо, кто контролирует страницы без
авторизации, расположенные на том же сервере.
И Netscape Navigator и Internet Explorer очищают кеш аутентификации
текущего окна для заданного региона (realm) при получении от сервера.
Это может использоваться для реализации принудительного выхода пользователя
и повторного отображения диалогового окна для ввода имени пользователя и
пароля. Некоторые разработчики используют это для ограничения авторизации по
времени или для предоставления кнопки "Выход".
Пример 34-2. Пример HTTP-аутентификации с принудительным вводом новой пары логин/пароль |
<?php
function authenticate() {
header('WWW-Authenticate: Basic realm="Test Authentication System"');
header('HTTP/1.0 401 Unauthorized');
echo "Вы должны ввести корректный логин и пароль для получения доступа к ресурсу \n";
exit;
}
if (!isset($_SERVER['PHP_AUTH_USER']) ||
($_POST['SeenBefore'] == 1 && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authenticate();
}
else {
echo "<p>Добро пожаловать: {$_SERVER['PHP_AUTH_USER']}<br />";
echo "Предыдущий логин: {$_REQUEST['OldAuth']}";
echo "<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>\n";
echo "<input type='hidden' name='SeenBefore' value='1' />\n";
echo "<input type='hidden' name='OldAuth' value='{$_SERVER['PHP_AUTH_USER']}' />\n";
echo "<input type='submit' value='Авторизоваться повторно' />\n";
echo "</form></p>\n";
}
?>
|
|
Это поведение не регламентируется стандартами HTTP Basic-аутентификации,
следовательно, вы не должны зависеть от этого. Как показали тесты,
браузер Lynx не очищает кеш авторизации при получении от сервера
статуса 401, и, нажав последовательно "Back", а затем "Forward" возможно
открыть такую страницу, при условии, что требуемые атрибуты авториазации не изменились.
Однако, пользователь может нажать клавишу '_' для очистки кеша аутентификации.
Также следует заметить, что до версии PHP 4.3.3, HTTP-аутентификация
не работала на серверах под управлением Microsoft IIS, если PHP был установлен
как CGI-модуль, в силу некоторых ограничений IIS. Для того, чтобы
добиться корректной работы в PHP 4.3.3+, вы должны отредактировать
конфигурационную настройку IIS под названием "Directory Security".
Щелкните на надписи "Edit" и установите опцию "Anonymous Access",
все остальные поля должны остаться неотмеченными.
Еще одно ограничение, если вы используете IIS посредством ISAPI: переменные
PHP_AUTH_* не определены, но в то же время доступна
переменная HTTP_AUTHORIZATION. Пример кода, который вы могли бы
использовать: list($user, $pw) = explode(':',
base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
Замечание касательно IIS::
Для того, чтобы HTTP-аутентификация корректно работала в IIS, в конфигурации
PHP опция cgi.rfc2616_headers должна
быть установлена значением 0 (значение по умолчанию).
Замечание:
В случае, если используется защищенный режим,
UID текущего скрипта будет добавлен в realm-часть заголовка
WWW-Authenticate.
add a note
User Contributed Notes
HTTP-Аутентификация в PHP
mt at shrewsbury dot org dot uk
12-Oct-2007 02:28
On my servers here, the standard rewrite spell
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]
to set $_SERVER[REMOTE_USER] with digest authentication results in the entire digest being bundled into $_SERVER[REMOTE_USER]
I have used this :
RewriteCond %{HTTP:Authorization} username=\"([^\"]+)\"
RewriteRule .* - [E=REMOTE_USER:%1,L]
And it seems to work successfully.
fordiman at gmail dot com
01-Aug-2007 05:07
@Whatabrain:
"[E=REMOTE_USER:%{HTTP:Authorization},L] ... didn't work. I couldn't see the variable."
Check $_SERVER['REMOTE_USER'] and $_SERVER['REDIRECT_REMOTE_USER']. It'll be there.
gbelyh at gmail dot com
26-Jul-2007 11:48
Back to the autherisation in CGI mode. this is the full working example:
# Create the .htaccess file with following contents:
# also you can use the condition (search at this page)
RewriteEngine on
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]
# In the beginning the script checking the authorization place the code:
$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"],6)) ;
$userpass = explode(":", $userpass);
if ( count($userpass) == 2 ){
#this part work not for all.
#print_r($userpass);die; #<- this can help find out right username and password
list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
$_SERVER['PHP_AUTH_PW'] = $password;
}
tonwyatt at yahoo dot com
24-Jul-2007 01:27
Here is my attempt to create a digest authentication class that will log the user in and out without using a cookie,session,db,or file. At the core is this simple code to parse the digest string into variables works for several browsers.
<?php
public function explodethedigest($instring) {
$quote = '"';
$equal = '=';
$comma = ',';
$space = ' ';
$a = explode( $comma, $instring);
$ax = explode($space, $a[0]);
$b = explode( $equal, $ax[1], 2);
$c = explode( $equal, $a[1], 2);
$d = explode( $equal, $a[2], 2);
$e = explode( $equal, $a[3], 2);
$f = explode( $equal, $a[4], 2);
$g = explode( $equal, $a[5], 2);
$h = explode( $equal, $a[6], 2);
$i = explode( $equal, $a[7], 2);
$j = explode( $equal, $a[8], 2);
$k = explode( $equal, $a[9], 2);
$l = explode( $equal, $a[10], 2);
$parts = array(trim($b[0])=>trim($b[1], '"'), trim($c[0])=>trim($c[1], '"'), trim($d[0])=>trim($d[1], '"'), trim($e[0])=>trim($e[1], '"'), trim($f[0])=>trim($f[1], '"'), trim($g[0])=>trim($g[1], '"'), trim($h[0])=>trim($h[1], '"'), trim($i[0])=>trim($i[1], '"'), trim($j[0])=>trim($j[1], '"'), trim($k[0])=>trim($k[1], '"'), trim($l[0])=>trim($l[1], '"'));
return $parts;
}
?>
Give it a try at http://tokko.kicks-ass.net/tests/ta1.php Log in with user test password pass or user guest password guest. Go to page two for links to the code. Comments, ideas, suggestions, or critique welcome.
Jack Bates
18-Jul-2007 01:01
In writing the HTTP auth module for the Gallery project, we discovered the following tricks for logging out with HTTP authentication:
Because most web browsers cache HTTP auth credentials, the Gallery logout link didn't work as expected after logging in with HTTP auth. Gallery correctly logged out the active user but the web browser simply logged in again with the next request.
To work around this, the HTTP auth module listens for the Gallery::Logout event and delegates to the httpauth.TryLogout page if necessary: http://gallery.svn.sourceforge.net/viewvc/gallery
/trunk/gallery2/modules/httpauth/TryLogout.inc?view=markup
The TryLogout page tries clearing the browser's authentication cache by as many tricks possible:
* Ask browser to authenticate with bogus authtype:
GalleryUtilities::setResponseHeader('HTTP/1.0 401 Unauthorized', false);
GalleryUtilities::setResponseHeader('WWW-Authenticate: Bogus', false);
* Redirect with random username and password. This won't actually clear the browser's authentication cache but will replace it with an invalid username and password. Since Gallery ignores invalid HTTP auth credentials, this effectively logs the user out.
* Clear Internet Explorer's authentication cache with JavaScript:
try {ldelim}
{* http://msdn.microsoft.com/workshop/author
/dhtml/reference/constants/clearauthenticationcache.asp *}
document.execCommand("ClearAuthenticationCache");
{rdelim} catch (exception) {ldelim}
{rdelim}
The TryLogout page redirects to the FinishLogout page for two resons:
1. To replace the browser's authentication cache with an invalid username and password
2. To check that the user was indeed logged out. If the user was logged out, the FinishLogout page redirects back to the Gallery application. Otherwise it displays a warning advising the user to manually clear their authentication cache (Clear Private Data in Firefox).
The TryLogout page redirects to the FinishLogout page using JavaScript and falls back on a manual link. It can't use a 302 Found status because the page needs to load for the Internet Explorer JavaScript to execute and because we can't put an invalid username and password in a Location: header.
http://codex.gallery2.org/Gallery2:Modules:httpauth
rovok at web dot de
03-Apr-2007 12:05
People are encouraged NOT to use register_globals, but Example 34.2. of german PHP documentation (http://de.php.net/manual/de/features.http-auth.php) uses register_globals in their example, assumed that the example is the whole script.
There is a <form> which has an <input> with type = "hidden", a name = "SeenBefore" and a value = "1". The Form is submitted by POST, so $SeenBefore should better be accessed by $_POST['SeenBefore'] instead of $SeenBefore.
Dutchdavey
16-Mar-2007 08:28
My sincere thanks to: webmaster at kratia dot com 21-Feb-2007 01:53
The principle is to not allow an invalid PHP_AUTH_USER to exist.
The following easy peasy example using Oracle is based on his simple genius:
///////////////////////////////////////////////////////////////
//
// do_html_header
//
// This function outputs the html header for the page.
//
//////////////////////////////////////////////////////////////////
function initialize_session()
{
$err=error_reporting(0);
$connection=oci_connect($_SERVER['PHP_AUTH_USER'],
$_SERVER['PHP_AUTH_PW'],$databasename) ;
error_reporting($err);
if (!$connection)
{
header('WWW-Authenticate: Basic Realm="ZEIP1"');
header('HTTP/1.0 401 Unauthorized');
echo "Login Cancelled';
exit;
}
..
Normal Code..
..
}
Nicolas Merlet - admin(at)merletn.org
05-Mar-2007 09:37
Be careful using http digest authentication (see above, example 34.2) if you have to use the 'setlocale' function *before* validating response with the 'http_digest_parse' function, because there's a conflict with \w in the pattern of 'preg_match_all' function :
In fact, as \w is supposed to be any letter or digit or the underscore character, you must not forgot that this may vary depending on your locale configuration (eg. it accepts accented letters in french)...
Due to this different pattern interpretation by the 'preg_match_all' function, the 'http_digest_parse' function will always return a false result if you have modified your locale (I mean if your locale accepts some extended characters, see http://fr.php.net/manual/en/reference.pcre.pattern.syntax.php for further information).
IMHO, I suggest you not to use setlocale before having your authentication completed...
PS : Here's a non-compatible setlocale declaration...
setlocale ( LC_ALL, 'fr_FR', 'fr', 'FR', 'french', 'fra', 'france', 'French', 'fr_FR.ISO8859-1' ) ;
webmaster at kratia dot com
20-Feb-2007 04:53
This is the simplest form I found to do a Basic authorization with retries.
<?php
$valid_passwords = array ("mario" => "carbonell");
$valid_users = array_keys($valid_passwords);
$user = $_SERVER['PHP_AUTH_USER'];
$pass = $_SERVER['PHP_AUTH_PW'];
$validated = (in_array($user, $valid_users)) && ($pass == $valid_passwords[$user]);
if (!$validated) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
die ("Not authorized");
}
echo "<p>Welcome $user.</p>";
echo "<p>Congratulation, you are into the system.</p>";
?>
mg at evolution515 dot net
06-Feb-2007 04:20
Example for digest doesn't work (at least for me):
use this fix:
--------------
preg_match_all('@(\w+)=(?:(([\'"])(.+?)\3|([A-Za-z0-9/]+)))@', $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
$data[$m[1]] = $m[4] ? $m[4] : $m[5];
unset($needed_parts[$m[1]]);
}
It's also better to but to put the Auth-Digest-Header in a function and call it on unsuccessful authentification again. Otherwise users only have the chance to submit their username/password just one time.
bleuciell at aol dot com
29-Dec-2006 12:51
For admin , i repair a fault , all is good now
Sorry for my english
It's a piece of code , to give a piece of reflexion about simple auth , we can also cryp login and pass in db , time is here for non-replay , the code isn't finish , but it work , only for reflexion about auth mechanism
<?php
function ky( $txt,$crypt) { $key = md5($crypt); $cpt = 0; $var = "";
for ( $Ctr = 0; $Ctr < strlen($txt); $Ctr++) { if ($cpt == strlen($crypt)) $cpt = 0;
$var.= substr($txt,$Ctr,1) ^ substr($crypt,$cpt,1); $cpt++; } return $var; }
$key = "";$list = 'abcdefghijklmnopqrstuvwxyz0123456789';
for($i = 0; $i< 200; $i++) { $key .= $list{mt_rand() % strlen($list)}; }
function cryp($txt,$key){ srand((double)microtime()*735412); $crypt = crypt(rand(0,3895234));$cpt = 0;$var= "";
for ( $Ctr=0; $Ctr < strlen($txt); $Ctr++ ) { if ($cpt == strlen($crypt))$cpt = 0;
$var.= substr($crypt,$cpt,1).( substr($txt,$Ctr,1) ^ substr($crypt,$cpt,1) ); $cpt++; } return base64_encode(ky($var,$key) ); }
function dcryp($txt,$key){ $txt=ky(base64_decode($txt),$key);$var= "";
for ( $Ctr = 0; $Ctr < strlen($txt); $Ctr++ ) { $md5 = substr($txt,$Ctr,1);$Ctr++; $var.= (substr($txt,$Ctr,1) ^ $md5); }return $var;}
$time= time(); $user = cryp('bubu',$key); $pwd = cryp('bubu-'.$time.'',$key);
function pwd($j,$key){ $x = dcryp($j,$key); $x = explode('-',$x); return $x[0];}
function pwd2($j,$key){ $x = dcryp($j,$key); $x = explode('-',$x); return $x[1];}
function auth(){$realm="Authentification PHPindex";
Header("WWW-Authenticate: Basic realm='".$realm."'");Header("HTTP/1.0 401 Unauthorized");
echo "Vous ne pouvez acc
|
|