OWASP A10 – Unvalidated Redirects and Forwards with PHP

This is going to be a pretty short post. There are no directly associated ASVS requirements for OWASP A10. The closest ASVS requirement is 4.2: “Verify that users can only access URLs for which they possess specific authorization,” which will be covered in this post. The risk here is that an unvalidated redirect that accepts user-supplied data could be leveraged to forward an unsuspecting victim to a malicious site. The victim will trust the initial link because it will appear to be valid. The following controls should be implemented to reduce the risk to your web applications:

  1. Do not use redirects or forward in your application unless it is absolutely necessary.
  2. If redirects are necessary, do not allow user-supplied data within the redirection.
  3. If the user must select an option that impacts the location of the redirection, then a) implement the input validation techniques that were in our XSS prevention series (found here, here, here, and here), b) map the values submitted by users to valid URL’s (expanded upon below), and c) validate the user has permission to access the URL.

Mapping user-supplied values to valid URL’s is the best method for preventing a redirection vulnerability in your web application. Map the user to a security group/role and then map URL’s to the proper groups/roles. This can be performed in the web application configuration or within a database table.

An example table:

ID URL ACL
0 /secure/admin 0
2 http://www.example.com 4
3 subdomain.example.com 1

First, validate the data. In this case, you would have a regular expression in your validation routine that only allows numbers. This could be further improved by supplying a maximum size for the supplied value. If the redirect request parameter passes validation, then supply the data to a function that pulls the URL from a table for the redirect and validates the access.

Using our input validation function, we would call:

  // Where urlValue is a number mapped to a URL
  // but supplied by the user.
  dataValidator("number", $urlValue, 2);

 
This would enforce the requirement that the supplied parameter is a number, and is only two digits long. The above would output the data, which would be supplied to a function that supplies the correct URL:

  // Assign the URL mapped result to a variable
  // to be included in the redirect.  In this 
  // example, $username would be pulled from a 
  // session object:
  $redirURL = getMappedURL(dataValidator("number", $urlValue, 2), $username);
  header("Location: " . $redirURL);

  // Make sure processing stops after redirect
  die();

 
The function for pulling the correct URL might look something like (simplistic ACL approach):

  function getMappedURL($mapValue, $username) {
    // Omitting DB connection code here.  SELECT the URL value
    // associated to the number parameter supplied by the user 
    // from the database.  Validate that the user has access by
    // mapping the ACL on the URL to the GroupID associated with
    // the user.  Use a prepared statement, which will
    // be explained in our series on OWASP A1.  Return the 
    // associated URL.
    $urlMap = $db->prepare("SELECT URL FROM urlmap 
                      INNER JOIN (Users) ON urlmap.ACL = Users.GroupID 
                      WHERE ID = ? AND Users.Username = ?");
    $urlMap->bindValue(1, $mapValue, PDO::PARAM_INT);
    $urlMap->bindValue(2, $username, PDO::PARAM_STR);
    $urlMap->execute();
    $mappedValue = $urlMap->fetch();
    
    // If the query successfully returns the redirect URL
    // then return the value.  Otherwise, return an error
    // page.
    if (sizeof($mappedValue) > 1) {
      return $mappedValue['URL'];
    } else {
      return "RedirError.php";
    }
  }

 
There you have it. Another fairly simple development solution to a potentially dangerous security vulnerability. Enjoy until next time.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: