Source for file HTTP.php
Documentation is available at HTTP.php
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2012 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.01 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/3_01.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Martin Jansen <mj@php.net> |
// | Rui Hirokawa <hirokawa@php.net> |
// | David Costa <gurugeek@php.net> |
// +----------------------------------------------------------------------+
require_once "Auth/Auth.php";
define('AUTH_HTTP_NONCE_TIME_LEN', 16 );
define('AUTH_HTTP_NONCE_HASH_LEN', 32 );
* The PEAR::Auth_HTTP class provides methods for creating an
* HTTP authentication system based on RFC-2617 using PHP.
* Instead of generating an HTML driven form like PEAR::Auth
* does, this class sends header commands to the clients which
* cause them to present a login box like they are e.g. used
* in Apache's .htaccess mechanism.
* This class requires the PEAR::Auth package.
* @notes The HTTP Digest Authentication part is based on
* authentication class written by Tom Pike <tom.pike@xiven.com>
* @author Martin Jansen <mj@php.net>
* @author Rui Hirokawa <hirokawa@php.net>
* @author David Costa <gurugeek@php.net>
* Authorization method: 'basic' or 'digest'
* Name of the realm for Basic Authentication
var $realm = "protected area";
* Text to send if user hits cancel button
* flag to indicate the nonce was stale.
* opaque string for digest authentication
* authorization info returned by the client
* Holds a reference to the global server variable
* Holds a reference to the global post variable
* Holds a reference to the global cookie variable
* @param string Type of the storage driver
* @param mixed Additional options for the storage driver
* (example: if you are using DB as the storage
* driver, you have to pass the dsn string here)
function Auth_HTTP($storageDriver, $options = '')
/* set default values for options */
$this->options = array ('cryptType' => 'md5',
'qop' => 'auth-int,auth',
'digestRealm' => 'protected area',
'forceDigestOnly' => false ,
'sessionSharing' => false ,
if (!empty ($options['authType'])) {
foreach($options as $key => $value) {
if (!empty ($this->options['opaquekey'])) {
$this->Auth ($storageDriver, $options);
* Assign values from $PHP_AUTH_USER and $PHP_AUTH_PW or 'Authorization' header
* to internal variables and sets the session id based
$this->server = &$this->_importGlobalVariable ('server');
if (!empty ($this->server['PHP_AUTH_USER'])) {
$this->username = $this->server['PHP_AUTH_USER'];
if (!empty ($this->server['PHP_AUTH_PW'])) {
$this->password = $this->server['PHP_AUTH_PW'];
* Try to get authentication information from IIS
if (empty ($this->username) && empty ($this->password)) {
if (!empty ($this->server['HTTP_AUTHORIZATION'])) {
list ($this->username, $this->password) =
} elseif ($this->authType == 'digest') {
$this->digest_header = null;
if (!empty ($this->server['PHP_AUTH_DIGEST'])) {
$this->digest_header = $this->server['PHP_AUTH_DIGEST'];
$headers = getallheaders ();
$headers = getallheaders ();
if(isset ($headers['Authorization']) && !empty ($headers['Authorization'])) {
$this->digest_header = substr($headers['Authorization'],
strpos($headers['Authorization'],' ')+1 );
if($this->digest_header) {
$authtemp = explode(',', $this->digest_header);
foreach($authtemp as $key => $value) {
if(strpos($value,'=') !== false ) {
if (!isset ($auth['uri']) || !isset ($auth['realm'])) {
if ($this->selfURI() == $auth['uri']) {
$this->uri = $auth['uri'];
if (substr($headers['Authorization'],0 ,7 ) == 'Digest ') {
if (!isset ($auth['nonce']) || !isset ($auth['username']) ||
!isset ($auth['response']) || !isset ($auth['qop']) ||
!isset ($auth['nc']) || !isset ($auth['cnonce'])){
if ($auth['qop'] != 'auth' && $auth['qop'] != 'auth-int') {
$this->stale = $this->_judgeStale ($auth['nonce']);
$this->username = $auth['username'];
$this->password = $auth['response'];
$this->auth['nonce'] = $auth['nonce'];
$this->auth['qop'] = $auth['qop'];
$this->auth['nc'] = $auth['nc'];
$this->auth['cnonce'] = $auth['cnonce'];
if (isset ($auth['opaque'])) {
$this->auth['opaque'] = $auth['opaque'];
} elseif (substr($headers['Authorization'],0 ,6 ) == 'Basic ') {
if ($this->options['forceDigestOnly']) {
return; // Basic authentication is not allowed.
list ($username, $password) =
$this->username = $username;
$this->password = $password;
return PEAR ::throwError ('authType is invalid.');
if ($this->options['sessionSharing'] &&
isset ($this->username) && isset ($this->password)) {
session_id(md5('Auth_HTTP' . $this->username . $this->password));
* set sessionName for AUTH, so that the sessionName is different
$this->_sessionName = "_authhttp". md5($this->realm);
$this->storage->_auth_obj ->_sessionName = & $this->_sessionName;
* When the user has already entered a username,
* we have to validate it.
if (!empty ($this->username) && !empty ($this->password)) {
if (true === $this->storage->fetchData ($this->username, $this->password)) {
} else { /* digest authentication */
if (!$this->getAuth () || $this->getAuthData ('a1') == null ) {
* - only PEAR::DB is supported as container.
* - password should be stored in container as plain-text
* (if $options['cryptType'] == 'none') or
* A1 hashed form (md5('username:realm:password'))
* (if $options['cryptType'] == 'md5')
if (!DB ::isConnection ($dbs->db )) {
$dbs->_connect ($dbs->options ['dsn']);
$query = 'SELECT '. $dbs->options ['passwordcol']. " FROM ". $dbs->options ['table'].
' WHERE '. $dbs->options ['usernamecol']. " = '".
$dbs->db ->quoteString ($this->username). "' ";
$pwd = $dbs->db ->getOne ($query); // password stored in container.
return PEAR ::throwError ($pwd->getMessage (), $pwd->getCode ());
if ($this->options['cryptType'] == 'none') {
$a1 = md5($this->username. ':'. $this->options['digestRealm']. ':'. $pwd);
$this->setAuthData ('a1', $a1, true );
$a1 = $this->getAuthData ('a1');
$login_ok = $this->validateDigest ($this->password, $a1);
if (!$login_ok && is_callable($this->loginFailedCallback)) {
if (!empty ($this->username) && $login_ok) {
$this->setAuth ($this->username);
* If the login failed or the user entered no username,
* output the login screen again.
if (!empty ($this->username) && !$login_ok) {
$this->status = AUTH_WRONG_LOGIN;
if ((empty ($this->username) || !$login_ok) && $this->showLogin) {
$this->drawLogin ($this->storage->activeUser );
if (!empty ($this->username) && $login_ok && $this->authType == 'digest'
&& $this->auth['qop'] == 'auth') {
$this->authenticationInfo ();
* @param string $username Username
function drawLogin ($username = "")
* Send the header commands
header("WWW-Authenticate: Basic realm=\"". $this->realm. "\"");
header('HTTP/1.0 401 Unauthorized');
} else if ($this->authType == 'digest') {
$this->nonce = $this->_getNonce ();
$wwwauth = 'WWW-Authenticate: Digest ';
$wwwauth .= 'qop="'. $this->options['qop']. '", ';
$wwwauth .= 'algorithm='. $this->options['algorithm']. ', ';
$wwwauth .= 'realm="'. $this->options['digestRealm']. '", ';
$wwwauth .= 'nonce="'. $this->nonce. '", ';
$wwwauth .= 'stale=true, ';
$wwwauth .= 'opaque="'. $this->opaque. '"' ;
if (!$this->options['forceDigestOnly']) {
$wwwauth .= 'WWW-Authenticate: Basic realm="'. $this->realm. '"';
header('HTTP/1.0 401 Unauthorized');
* This code is only executed if the user hits the cancel
* button or if he enters wrong data 3 times.
echo 'Stale nonce value, please re-authenticate.';
* Set name of the current realm
* @param string $realm Name of the realm
* @param string $digestRealm Name of the realm for digest authentication
function setRealm($realm, $digestRealm = '')
if (!empty ($digestRealm)) {
$this->options['digestRealm'] = $digestRealm;
* Set the text to send if user hits the cancel button
* @param string $text Text to send
* judge if the client response is valid.
* @param string $response client response
* @param string $a1 password or hashed password stored in container
* @return bool true if success, false otherwise
function validateDigest ($response, $a1)
$this->server = &$this->_importGlobalVariable ('server');
$a2unhashed = $this->server['REQUEST_METHOD']. ":". $this->selfURI();
if($this->auth['qop'] == 'auth-int') {
if(isset ($GLOBALS["HTTP_RAW_POST_DATA"])) {
// In PHP < 4.3 get raw POST data from this variable
$body = $GLOBALS["HTTP_RAW_POST_DATA"];
} else if($lines = @file('php://input')) {
// In PHP >= 4.3 get raw POST data from this file
$this->post = &$this->_importGlobalVariable ('post');
foreach($this->post as $key => $value) {
if($body != '') $body .= '&';
$a2unhashed .= ':'. md5($body);
$this->auth['nonce']. ':'.
$this->auth['cnonce']. ':'.
$expectedResponse = md5($combined);
if(!isset ($this->auth['opaque']) || $this->auth['opaque'] == $this->opaque) {
if($response == $expectedResponse) { // password is valid
* judge if nonce from client is stale.
* @param string $nonce nonce value from client
function _judgeStale ($nonce)
if(!$this->_decodeNonce ($nonce, $time, $hash_cli)) {
* @param string $nonce nonce value from client
* @param string $time decoded time
* @param string $hash decoded hash
* @return bool false if nonce is invalid
function _decodeNonce ($nonce, &$time, &$hash)
$this->server = &$this->_importGlobalVariable ('server');
$hash = md5($time . $this->server['HTTP_USER_AGENT'] . $this->options['noncekey']);
if ($hash_cli != $hash) {
* return nonce to detect timeout
* @return string nonce value
$this->server = &$this->_importGlobalVariable ('server');
$hash = md5($time . $this->server['HTTP_USER_AGENT'] . $this->options['noncekey']);
// {{{ authenticationInfo()
* output HTTP Authentication-Info header
* @notes md5 hash of contents is required if 'qop' is 'auth-int'
* @param string MD5 hash of content
function authenticationInfo ($contentMD5 = '') {
if($this->getAuth () && ($this->getAuthData ('a1') != null )) {
$a1 = $this->getAuthData ('a1');
// Work out authorisation response
$a2unhashed = ":". $this->selfURI();
if($this->auth['qop'] == 'auth-int') {
$a2unhashed .= ':'. $contentMD5;
$this->auth['cnonce']. ':'.
// Send authentication info
$wwwauth = 'Authentication-Info: ';
$wwwauth .= 'nextnonce="'. $this->nextNonce. '", ';
$wwwauth .= 'qop='. $this->auth['qop']. ', ';
$wwwauth .= 'rspauth="'. md5($combined). '", ';
$wwwauth .= 'cnonce="'. $this->auth['cnonce']. '", ';
$wwwauth .= 'nc='. $this->auth['nc']. '';
* set authentication option
* @param mixed $name key of option
* @param mixed $value value of option
foreach($name as $key => $value) {
* get authentication option
* @param string $name key of option
* @return mixed option value
if ($name == 'CancelText') {
* @return string self URI
$this->server = &$this->_importGlobalVariable ('server');
// query string should be removed for MSIE
$uri = $this->server['REQUEST_URI'];
Documentation generated on Mon, 11 Mar 2019 15:47:23 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|