A good friend of mine asked if it was possible to log out of a Basic Authentication session. My first knee-jerk response was that Basic Authentication has no log out function and you should close the browser to safely log out of the session. After some days silence he came back with a script he’d found on the php.net site. The script used sessions to break the Basic Authentication behavior of the browser. It wasn’t a very successful script because it only worked in a limited set of browsers but it got me thinking about a better solution.
The best place to start with these challenges is to read the appropriate standard to learn what the original implementers had in mind with their solution. The standard for Basic Authentication is RFC 2617. The standard states:
The realm directive (case-insensitive) is required for all authentication schemes that issue a challenge. The realm value (case-sensitive), in combination with the canonical root URL (the absoluteURI for the server whose abs_path is empty; see section 5.1.2 of [2]) of the server being accessed, defines the protection space.
So there’s two items that define the Basic Authentication protection space:
- realm: a server provided, unique string identifier
- abs_path: the url
By making both items unique and changing them at log out time you basically break the basic authentication protection space and effectively log out the user.
The solution requires the use of mod_rewrite to direct traffic for non-existent resources to the index.php file. Please place a .htaccess file with the following contents in the folder that you wish to protect:
.htaccess:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [NC,L]
Place an index.php file in the same folder (/auth in this example) with the following content:
<?php
session_start();
if (empty($_SESSION['session_id'])) {
session_regenerate_id();
$_SESSION['session_id'] = session_id();
header("Location: /auth/" . $_SESSION['session_id'] . "/", TRUE, 301);
}
$url_action = (empty($_REQUEST['action'])) ? 'HomePage' : $_REQUEST['action'];
if (isset($url_action)) {
if (is_callable($url_action)) {
call_user_func($url_action);
} else {
print 'Function does not exist, request terminated';
}
}
function HomePage() {
print '<h1>Homepage</h1>';
print '<p><a href="?action=LogIn">LogIn</a></p>;
print '<p><a href="?action=LogOut">LogOut</a></p>';
print '<p><a href="?action=SecureContent">Secure Content</a></p>';
}
function LogIn($url='') {
$session_id = $_SESSION['session_id'];
while (!IsAuthenticated()) {
header('WWW-Authenticate: Basic realm="' . $session_id . '"');
header('HTTP/1.1 401 Unauthorized');
die('Authorization Required');
}
if (!empty($url)) {
return TRUE;
} else {
header("Location: /auth/" . $_SESSION['session_id'] . "/", TRUE, 301);
}
}
function LogOut() {
session_destroy();
session_unset($_SESSION['session_id']);
header("Location: /auth/", TRUE, 301);
}
function SecureContent() {
if (LogIn("SecureContent")) {
print '<h1>Secure Content</h1>';
print '<p>This is secure content</p>';
print '<p><a href="/auth/' . $_SESSION['session_id'] . '/?action=HomePage">Home Page</a></p>';
} else {
print '<h1>Not Authorized</h1>';
}
}
function IsAuthenticated() {
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
$httpd_username = filter_var($_SERVER['PHP_AUTH_USER'], FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
$httpd_password = filter_var($_SERVER['PHP_AUTH_PW'], FILTER_SANITIZE_STRING, FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
if ($httpd_username == "test" && $httpd_password == "test") {
return TRUE;
} else {
return FALSE;
}
}
return FALSE;
}
?>
Both files need to be put in a folder named /auth for these examples to work. You can change the folder name in the index.php example to suit your own environment.
A the start of the script a unique id is generated through PHP session mechanism and this session id is inserted into the url (hence the requirement for mod_rewrite to catch these urls). A redirect takes place to this new url and the homepage is shown. If you click on login a 401 header is generated using a realm that uses the same session id. If you click logout the current session id is destroyed and you are redirected to the start of the script where a new session id is generated. If you click on a protected item without a login the login function is called.
No related posts.
Comments
No one has said anything yet.
Leave a Comment