Dan Q's PHP Cookery Class

An intro to PHP for Web Revivalists

Appendix B: Security

Security principles you need to be aware of when making your website "mildly dynamic" with PHP are:

Don't trust user input! (XSS)

Any data that comes from a user, whether you find it in $_GET, $_POST, $_COOKIE, or even e.g. their browser's name and version number ($_SERVER['HTTP_USER_AGENT']) can not be trusted. It can always be tampered with by a malicious user. So:

Before you change anything, ensure the user intended it (XSRF)

A whole class of attacks are based on the idea that you can trick a user into doing something without meaning to. E.g. if your guestbook takes what comes from the <form> and adds it as an entry, what's to stop my site from embedding a form that submits to your guestbook? (If that's not something you care about, that's fine, but you should ask the question each time: a button that deletes a user's account might be much more-serious!)

The simplest way to prevent this kind of attack is to give the user a random "token" when they visit the form, and then check for the token when they submit it. Because two users get different tokens, an attacker's token can't be used to trick a user into submitting a form. Here's how you might do that:

  1. On your form page, create a token and store it in a session variable (so it's available on the next page too), e.g.:
  2. Put that token into a hidden field on your form, e.g.:
    <input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>">
  3. When the form is submitted, check that the token is present and matches the one you stored in the session, e.g.:
    if( $_POST['token'] !== $_SESSION['token'] ) { die( 'XRSF: Invalid token. Please go back and try again.' ); }

Don't give users the files they ask for without checking first (path traversal)

In an example in lesson 5, we allowed users to choose a fruit file on the server. We kept this safe by only allowing users to choose from a pre-approved list of fruit files, by sending the ID number of the one they wanted and looking it up again by ID number once they did.

Furthermore, we used a simple <img> tag to display the fruit image, so the user can only see files that they would normally be able to, over the Web. We didn't have PHP read the file for them (PHP could potentially have permission to read any file on your server: even the ones not intended to be shared on the Web like your logs).

If we needed to allow users to specify the file name themselves, we'd need to check that the requested file is within the allowed directory. Here's how we might do that:

  1. Run realpath() on both the the path the user requested and the path within which they're allowed to request files.
  2. Ensure that the start of the requested path matches the entire allowed path.

You can see an example of this in the sanitize_file_path() function used on this site, which rejects any paths that are outside of the application directory of this site. To do this, it first runs realpath() on both the "allowed" path and the "requested" path, which turns them into full and absolute paths without e.g. any ../ sequences. Then it uses strpos() to ensure that the requested path begins with (i.e. is within) the allowed path; if not, it throws a 403 (Forbidden) error.

Green potion You found the green potion! Click on it to collect it! (What is this?)

How can cookies be secured?

Lesson 6 demonstrated the use of the setcookie() function to set a cookie, which can then be retrieved on subsequent requests via $_COOKIE. But cookies can be tampered by users, so you don't want to trust them blindly.

(Note that PHP's built-in session cookies (e.g. $_SESSION) are secured by default, so you don't need to worry about them.)

There are two major approaches to securing cookies:

Making (non-session) cookies tamper-proof goes beyond the scope of this cookery class!