Section 2.5. Cross-Site Request Forgeries

2.5. Cross-Site Request Forgeries

A cross-site request forgery (CSRF) is a type of attack that allows an attacker to send arbitrary HTTP requests from a victim. The victim is an unknowing accomplicethe forged requests are sent by the victim, not the attacker. Thus, it is very difficult to determine when a request represents a CSRF attack. In fact, if you have not taken specific steps to mitigate the risk of CSRF attacks, your applications are most likely vulnerable.

Consider a sample application that allows users to buy itemseither pens or pencils. The interface includes the following form:

     <form action="buy.php" method="POST">     <p>     Item:     <select name="item">       <option name="pen">pen</option>       <option name="pencil">pencil</option>     </select><br />     Quantity: <input type="text" name="quantity" /><br />     <input type="submit" value="Buy" />     </p>     </form> 

An attacker can use your application as intended to do some basic profiling. For example, an attacker can visit this form to discover that the form elements are item and quantity. The attacker also learns that the expected values of item are pen and pencil.

The buy.php script processes this information:

     <?php     session_start();     $clean = array();     if (isset($_REQUEST['item'] && isset($_REQUEST['quantity']))     {       /* Filter Input ($_REQUEST['item'], $_REQUEST['quantity']) */       if (buy_item($clean['item'], $clean['quantity']))       {         echo '<p>Thanks for your purchase.</p>';       }       else       {         echo '<p>There was a problem with your order.</p>';       }     }     ?> 

An attacker can first use your form as intended to observe the behavior. For example, after purchasing a single pen, the attacker knows to expect a message of thanks when a purchase is successful. After noting this, the attacker can then try to see whether GET data can be used to perform the same action by visiting the following URL: 

If this is also successful, then the attacker now knows the format of a URL that causes an item to be purchased when visited by an authenticated user. This situation makes a CSRF attack very easy because the attacker only needs to cause a victim to visit this URL.

While there are several possible ways to launch a CSRF attack, using an embedded resource such as an image is the most common. To understand this particular approach, it is necessary to understand how a browser requests these resources.

When you visit (Figure 2-1), your browser first sends a request for the parent resourcethe one identified by the URL. The content in the response is what you will see if you view the source of the page (the HTML). Only after the browser has parsed this content is it aware of the imagethe Google logo. This image is identified in an HTML img tag, and the src attribute indicates the URL of the image. The browser sends an additional request for this image, and the only difference between this request and the previous one is the URL.

Figure 2-1. Google's web site, which has a single embedded image

A CSRF attack can use an img tag to leverage this behavior. Consider visiting a web site with the following image identified in the source:

     <img src="/books/3/99/1/html/2/" /> 

Because the buy.php script uses $_REQUEST instead of $_POST, any user who is already logged in at will buy 50 pencils whenever this URL is requested.

CSRF attacks are one of the reasons that using $_REQUEST is not recommended.

The complete attack is illustrated in Figure 2-2.

Figure 2-2. A CSRF attack launched with a simple image

When requesting an image, some browsers alter the value of the Accept header to give a higher priority to image types. Resist the urge to rely upon this behavior for protection.

You can take a few steps to mitigate the risk of CSRF attacks. Minor steps include using POST rather than GET in your HTML forms that perform actions, using $_POST instead of $_REQUEST in your form processing logic, and requiring verification for critical actions (convenience typically increases risk, and it's up to you to determine the appropriate balance).

Any form intended to perform an action should use the POST request method. Section 9.1.1 of RFC 2616 states the following:

"In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered 'safe.' This allows user agents to represent other methods, such as POST, PUT and DELETE, in a special way, so that the user is made aware of the fact that a possibly unsafe action is being requested."

The most important thing you can do is to try to force the use of your own forms. If a user sends a request that looks as though it is the result of a form submission, it makes sense to treat it with suspicion if the user has not recently requested the form that is supposedly being submitted. Consider the following replacement for the HTML form in the sample application:

     <?php     session_start();     $token = md5(uniqid(rand(), TRUE));     $_SESSION['token'] = $token;     $_SESSION['token_time'] = time();     ?>     <form action="buy.php" method="POST">     <input type="hidden" name="token" value="<?php echo $token; ?>" />     <p>     Item:     <select name="item">       <option name="pen">pen</option>       <option name="pencil">pencil</option>     </select><br />     Quantity: <input type="text" name="quantity" /><br />     <input type="submit" value="Buy" />     </p>     </form> 

With this simple modification, a CSRF attack must include a valid token in order to perfectly mimic the form submission. Because the token is stored in the user's session, it is also necessary that the attacker uses the token unique to the victim. This effectively limits any attack to a single user, and it requires that the attacker obtain a valid token that belongs to another userusing your own token is useless when forging requests from someone else.

The token can be checked with a simple conditional statement:

     <?php     if (isset($_SESSION['token']) &&         $_POST['token'] == $_SESSION['token'])     {       /* Valid Token */     }     ?> 

The validity of the token can also be limited to a small window of time, such as five minutes:

     <?php     $token_age = time() - $_SESSION['token_time'];     if ($token_age <= 300)     {       /* Less than five minutes has passed. */     }     ?> 

By including a token in your forms, you practically eliminate the risk of CSRF attacks. Take this approach for any form that performs an action.

While the exploit I describe uses an img tag, CSRF is a generic name that references any type of attack in which the attacker can forge HTTP requests from another user. There are known exploits for both GET and POST, so don't consider a strict use of POST to be adequate protection.

Essential PHP Security
Essential PHP Security
ISBN: 059600656X
EAN: 2147483647
Year: 2005
Pages: 110

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: