- Defining Helper Functions
- Registration
- Logging In
- Logging Out
- Managing Passwords
- Improving the Security
Improving the Security
The user accounts system in this example demonstrates a good approach to security. For starters, users are forced to use both letters and numbers in their password and at least one letter in each case. You could improve the security by requiring at least one nonalphanumeric character and by increasing the minimum length. Security is inversely proportional to convenience, so you would only make these changes knowing that you'll annoy some of your potential customers. I also chose not to implement a "remember me" option because requiring that users log in each time they visit the site makes for better security (although, again, doing so is an inconvenience).
As another safeguard, the passwords are securely hashed before being sent to the database, where they're stored in binary format. And the only user account type that can be created through the registration process is the standard member; there's no way to trick the system into creating an administrative account.
Another, obvious and relatively easy way you can improve the security of the site is to implement SSL for the registration and login processes. To do so, change the link to the registration page to: 'https://' . BASE_URL . 'register.php' . That form will be loaded via HTTPS, and the form data will be posted back to the server via HTTPS, too. In fact, with nothing but relative links in the site, everything will be HTTPS until you create a link that returns to an HTTP connection.
Serving the login form over HTTPS is trickier, because the form is included by other pages. Your options are to serve every page over HTTPS, which isn't ideal, or to create a separate login page.
As mentioned before, you could implement an activation process as part of the registration, in which case the customer would be sent to PayPal after activating the account, not after first registering. You could also send an email when a password change is requested and only by clicking the link in that email would the user have their password reset. Right now, anyone can reset other users' passwords, which is a bother, even if it doesn't adversely affect the security.
Because this system relies upon a login to authenticate the user, much of its security depends upon using sessions and a cookie (for storing the session ID in the browser). Limiting the life of the cookie, changing the session name (which is also the cookie's name), and tweaking the other cookie parameters can all increase the site's security. You can even send the cookie only over SSL, but that would require using SSL for every page once the user logged in.
For the session itself, one recommendation that I made in Chapter 2, "Security Fundamentals," was to change the session storage directory if using a shared host. You can also shorten how quickly the session expires, as well as how quickly the session cookie expires.
These are all potential alterations you could make. One recommendation you should implement is changing the session ID for administrative users. By doing so, you can prevent a session fixation attack.
A session fixation attack is when a malicious user, Alice, starts her own session on your site, quite legitimately. She then gets administrator Bob to visit the site using that same session ID, normally by getting Bob to click a link with the session ID embedded. When Bob logs in to the site, that same session will now be associated with an administrative account, giving Alice administrative access through her browser and existing session. Preventing this is quite simple: Change the session ID using the session_regenerate_id() function. By doing so, when Bob logs in, his session ID will change, meaning Alice's legitimate session won't be updated to reflect Bob's administrative status.
To add this technique to the login.inc.php script, change the storing of data in the session to:
if ($row[2] == 'admin') { session_regenerate_id(true); $_SESSION['user_admin'] = true; } $_SESSION['user_id'] = $row[0]; $_SESSION['username'] = $row[1]; if ($row[3] == 1) $_SESSION['user_not_expired'] = true;
You'll need to call session_regenerate_id() before storing any session data, because by passing a value of true as the first argument to the function, any existing session data is also destroyed.
Finally, access to site content in this example will be determined by dates without times. With the sessions as written, the worst thing that could happen would be that a user whose account expires today is allowed to continue accessing site content for some minutes or hours into tomorrow. But even that is only true if the user keeps their session active. Not a huge concern, in my opinion.