Sunday 10 July 2011

PHP - Implementing Secure Login with PHP, JavaScript, and Sessions (without SSL)

login.php

<?php 
//
// LOGIN PAGE
//
//   Server-side:
//     1. Start a session
//     2. Clear the session
//     3. Generate a random challenge string
//     4. Save the challenge string in the session
//     5. Expose the challenge string to the page via a hidden input field
//
//  Client-side:
//     1. When the completes the form and clicks on Login button
//     2. Validate the form (i.e. verify that all the fields have been filled out)
//     3. Set the hidden response field to HEX(MD5(server-generated-challenge + user-supplied-password))
//     4. Submit the form

session_start();
session_unset();
srand();
$challenge = "";
for ($i = 0; $i < 80; $i++) {
    $challenge .= dechex(rand(0, 15));
}
$_SESSION[challenge] = $challenge;
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head>
        <title>Login</title>
        <script type="text/javascript" src="http://pajhome.org.uk/crypt/md5/md5.js"></script>
        <script type="text/javascript">
            function login() {
                var loginForm = document.getElementById("loginForm");
                if (loginForm.username.value == "") {
                    alert("Please enter your user name.");
                    return false;
                }
                if (loginForm.password.value == "") {
                    alert("Please enter your password.");
                    return false;
                }
                var submitForm = document.getElementById("submitForm");
                submitForm.username.value = loginForm.username.value;
                submitForm.response.value =
                    hex_md5(loginForm.challenge.value+loginForm.password.value);
                submitForm.submit();
            }
        </script>
    </head>
    <body>
        <h1>Please Login</h1>
        <form id="loginForm" action="#" method="post">
            <table>
                <?php if (isset($_REQUEST[error])) { ?>
                <tr>
                    <td>Error</td>
                    <td style="color: red;"><?php echo $_REQUEST[error]; ?></td>
                </tr>
                <?php } ?>
                <tr>
                    <td>User Name:</td>
                    <td><input type="text" name="username"/></td>
                </tr>
                <tr>
                    <td>Password:</td>
                    <td><input type="password" name="password"/></td>
                </tr>
                <tr>
                    <td>&nbsp;</td>
                    <td>
                        <input type="hidden" name="challenge" value="<?php echo $challenge; ?>"/>
                        <input type="button" name="submit" value="Login" onclick="login();"/>
                    </td>
                </tr>
            </table>
        </form>
        <form id="submitForm" action="authenticate.php" method="post">
            <div>
                <input type="hidden" name="username"/>
                <input type="hidden" name="response"/>
            </div>
        </form>
    </body>
</html>

authenticate.php

<?php 

//
// AUTHENTICATE PAGE
//
//   Server-side:
//     1. Get the challenge from the user session
//     2. Get the password for the supplied user (local lookup)
//     3. Compute expected_response = MD5(challenge+password)
//     4. If expected_response == supplied response:
//        4.1. Mark session as authenticated and forward to secret.php
//        4.2. Otherwise, authentication failed. Go back to login.php
$userDB = array("john" => "abc123",
"bob"  => "secret",
"anna" => "passwd")
function getPasswordForUser($username) {
// get password from a simple associative array
// but this could be easily rewritten to fetch user info from a real DB
global $userDB;     return $userDB[$username];
} 
function validate($challenge, $response, $password) {
return md5($challenge . $password) == $response;
} 
function authenticate() {
if (isset($_SESSION[challenge]) &&
isset($_REQUEST[username]) &&
isset($_REQUEST[response])) {
$password = getPasswordForUser($_REQUEST[username]);
if (validate($_SESSION[challenge], $_REQUEST[response], $password)) {
$_SESSION[authenticated] = "yes";
$_SESSION[username] = $_REQUEST[username];;
unset($_SESSION[challenge]);
} else {
header("Location:login.php?error=".urlencode("Failed authentication"));
exit;
}
} else {
header("Location:login.php?error=".urlencode("Session expired"));
exit;
}
}
session_start();
authenticate();
header("Location:secret.php");
exit();
?>

common.php

<?php

//
// COMMON PAGE
//
//   Defines require_authentication() function:
//     If the user is not authenticated, forward to the login page
//     

session_start();
function is_authenticated() {
return isset($_SESSION[authenticated]) &amp;&amp;
$_SESSION[authenticated] == "yes";
}
function require_authentication() {
if (!is_authenticated()) {
header("Location:login.php?error=".urlencode("Not authenticated"));
exit;
}
}
?>

No comments:

Post a Comment