Where Does access Fit in to SMTP AUTH and STARTTLS?
Chapter 8. Securing Web Services
8.5 Special Topics
8.5.1 Authentication
Your web site may have some restricted content, such as premium pages for registered customers or administrative functions for web site maintainers. Use authentication to establish the identity of the visitor. 8.5.1.1 Basic authentication
The simplest authentication method in Apache is basic authentication . This requires a password file on the web server and a require directive in a config file:
<Location /auth_demo_dir> AuthName "My Authorization" AuthType Basic
# Note: Keep the password files in their own directory
AuthUserFile /usr/local/apache/auth_dir/auth_demo_password " Order deny, allow
Require valid-user </Location>
I suggest storing password files in their own directories, outside the document root. You may use
subdirectories to segregate files by user or virtual host. This is more manageable than .htaccess files all over the site, and it keeps Apache running faster.
You can specify any matching user, a list of users, or a list of groups: require valid-user
require user user1 user2 ... require group group1 group2 ...
Where are the names and passwords stored? The simplest, specified by AuthUserFile in the example, is a flat text file on the server. To create the password file initially, type the following:
htpasswd -c /usr/local/apache/auth_dir/auth_demo_password To add entries to the password file:
htpasswd /usr/local/apache/auth_dir/auth_demo_password -u raoul ... (prompt for password for raoul) ...
When a visitor attempts to access /auth_demo_dir on this site, a dialog box pops up and prompts him for his name and password. These will be sent with the HTTP stream to the web server. Apache will read the password file /etc/httpd/authfiles/auth_demo_password, get the encrypted password for the user raoul, and see if they match.
Don’t put the password file anywhere under your DocumentRoot! Use one or more separate directories, with read-write permissions for the Apache UID group and none for others.
An authentication method connects with a particular storage implementation (DBM, DB, MySQL, LDAP) by matching Apache modules and configuration directives. For example, mod_auth_mysql is configured with the table and column names in a customer table in a MySQL database. After the name and password are sent to Apache from the browser, mod_auth_mysql queries the database and Apache allows access if the query succeeds and the username and password were found.
Browsers typically cache this authentication information and send it to the web server as part of each HTTP request header for the same realm (a string specified to identify this resource). What if the user changes her password during her session? Or what if the server wants to log the client off after some period of inactivity? In either case, the cached credentials could become invalid, but the browser still holds them tight.
Unfortunately, HTTP has no way for a server to expire credentials in the client. It may be necessary to clear all browser caches (memory and disk) to clear the authentication data, forcing the server to request reauthentication and causing the client to open a new dialogue box. Sessions and cookies are often used to limit login times.
One problem with basic authentication is that it is not encrypted. A sniffer can and will pick up the name and password. You can use SSL for the initial authentication (a URL starting with https://) and then use normal (http://) URLs thereafter, with the session ID in a cookie or appended to the URL. This gives some privacy during login and better performance afterwards.
Direct authentication with a scripting language gives more flexibility than the built-in browser dialogue box. The script writes the proper HTTP server headers to the client, and it processes the reply as though it came from the standard dialogue box.
8.5.1.2 Digest authentication
The second HTTP client authentication method, digest authentication , is more secure, since it uses an MD5 hash of data rather than clear-text passwords. RFC 2617 documents basic and digest authentication. The Apache server and Mozilla implement the standard correctly. Microsoft did not, so digest authentication in IE 5 and IIS 5 does not currently interoperate with other web servers and browsers.
8.5.1.3 Safer authentication
It’s surprisingly tricky to create secure client authentication. User input can be forged, HTTP referrals are unreliable, and even the client’s apparent IP can change from one access to the next if the user is behind a proxy farm. It would be beneficial to have a method that’s usable within and across sites. For cross-site authentication, the authenticating server must convey its approval or disapproval in a way that can’t be easily forged and that will work even if the servers aren’t homogeneous and local.
A simple adaptation of these ideas follows. It uses a public variable with unique values to prevent a replay attack. A timestamp is useful since it can also be used to expire old logins. This value is combined with a constant string that is known only by the cooperating web servers to produce another string. That string is run through a one-way hash function. The timestamp and hashed string are sent from the authenticating web server (A) to the target web server (B).
Let’s walk through the process. First, the client form gets the username and password and submits them to Server A:
# Client form
<form method="get" action="https://a.test.com/auth.php"> User: <input type="text" name="user">
Password: <input type="password" name="password"> <input type="submit">
</form>
On Server A, get the timestamp, combine it with the secret string, hash the result, and redirect to Server B: <?
// a.test.com/auth.php $time_arg = Date( );
$secret_string = "babaloo";
$hash_arg = md5($time_arg . $secret_string); $url = "http://b.test.com/login.php" .
"?" .
"&h=" . urlencode($hash_arg); header("Location: $url");
?>
On Server B, confirm the input from Server A: <?
// b.test.com/login.php // Get the CGI variables: $time_arg = $_GET[’t’]; $hash_arg = $_GET[’h’];
// Servers A and B both know the secret string, // the variable(s) it is combined with, and their // order:
$secret_string = "babaloo";
$hash_calc = md5($time_arg . $secret_string); if ($hash_calc == $hash_arg)
{
// Check $time_arg against the current time.
// If it’s too old, this input may have come from a // bookmarked URL, or may be a replay attack; reject it. // If it’s recent and the strings match, proceed with the login...
} else {
// Otherwise, reject with some error message. }
?>
This is a better-than-nothing method, simplified beyond recognition from the following sources, which should be consulted for greater detail and security:
• Example 16-2 in Web Security, Privacy and Commerce by Simson Garfinkel and Gene Spafford (O’Reilly).
• Dos and Donts of Client Authentication on the Web
(http://www.lcs.mit.edu/publications/pubs/pdf/MIT-LCS-TR-818.pdf) describes how a team at MIT cracked the authentication schemes of a number of commercial sites, including the Wall Street Journal. Visit http://cookies.lcs.mit.edu/ for links to the Perl source code of their Kooky
Authentication Scheme.