<form action="login.php" enctype="application/x-www-form-urlencoded" method="post">
<div class="articlePara">
<label for="username">Your username:</label><br />
<input name="username" size="25" type="text" />
</div>
<div class="articlePara">
<label for="password">Your password:</label><br />
<input name="password" size="25" type="password" />
</div>
<div class="articlePara">
<input name="submit" type="submit" value="Login" />
</div>
</form>
Figure 1. The Login Form
CREATE TABLE accounts (
id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
password CHAR(32) NOT NULL,
last_login DATETIME NOT NULL
);
login.php. In a real-world situation the form and the login script would probably be one in the same, but for the purposes of this exercise, I'll treat them separately so as not to muddy the water in terms of explaining the authentication process. A heavily commented login.php script follows; take some time to review this code, after which I'll offer a summary of key lines:<?php
// Sanitize incoming username and password
$username = filter_var($_POST['username'], FILTER_SANITIZE_STRING);
$password = filter_var($_POST['password'], FILTER_SANITIZE_STRING);
// Connect to the MySQL server
$db = new mysqli("localhost", "root", "jason", "phpbuilder");
// Determine whether an account exists matching this username and password
$stmt = $db->prepare("SELECT id FROM accounts WHERE username = ? and password = md5(?)");
// Bind the input parameters to the prepared statement
$stmt->bind_param('ss', $username, $password);
// Execute the query
$stmt->execute();
// Store the result so we can determine how many rows have been returned
$stmt->store_result();
if ($stmt->num_rows == 1) {
// Bind the returned user ID to the $id variable
$stmt->bind_result($id);
$stmt->fetch();
// Update the account's last_login column
$stmt = $db->prepare("UPDATE accounts SET last_login = NOW() WHERE id = ?");
$stmt->bind_param('d', $id);
$stmt->execute();
// Redirect the user to the home page
header('Location: http://www.example.com');
}
?>
login.php script begins by sanitizing incoming form input using PHP's filter extension. Although the subsequent prepared statement will help to prevent any potentially malicious input from damaging or circumventing the authentication system, an additional layer of security never hurts.md5() function is used to first hash the incoming password. This is because the password was presumably hashed using the same md5() function at registration time. Storing passwords in plain text is never a good idea, and the md5() function is an easy way to store them in a format which can't be further exploited should an attacker somehow obtain your database.last_login column. Notice how another MySQL function is used (NOW()) to accomplish this task.login.php script to use PHP's session handling feature to start a new session and then assign the user's username to a session variable. I've bolded the lines added to the revised part of the login script:if ($stmt->num_rows == 1) {
// Bind the returned user ID to the $id variable
$stmt->bind_result($id);
$stmt->fetch();
// Update the account's last_login column
$stmt = $db->prepare("UPDATE accounts SET last_login = NOW() WHERE id = ?");
$stmt->bind_param('d', $id);
$stmt->execute();
session_start();
$_SESSION['username'] = $username;
// Redirect the user to the home page
header('Location: http://www.example.com');
}
username already exists, and if so provides a customized welcome message. If the variable doesn't exist, a registration and login link is provided:<?php session_start(); ?>
<?php if (isset($_SESSION['username'])) { ?>
<p>Welcome back, <?= $_SESSION['username']; ?>!</p>
<?php } else { ?>
<p>
<a href="register.php">Create an account</a> |
<a href="login.html">Login to your account</a>
</p>
<?php } ?>