CSRF (Cross-site request forgery, also known as "one click attack" or session riding, usually abbreviated as CSRF or XSRF, is a type of malicious use of websites.
I. CSRF attack principles
The CSRF attack principle is relatively simple, as shown in 1. Specifically, Web A is A website with the CSRF vulnerability, Web B is A malicious website built by attackers, and User C is the legal User of Web.
Figure 1 CSRF attack Principle
1. User C opens the browser, accesses trusted website A, and enters the user name and password to log on to website;
2. After the user information is verified, website A generates Cookie information and returns it to the browser. At this time, the user successfully logs on to website A and can send A request to website A normally;
3. Before you exit website A, open a tab page in the same browser to access website B;
4. After website B receives A user request, it returns some aggressive code and sends A request requesting access to website;
5. After receiving the attack code, the browser sends A request to website A without the user's knowledge based on the request of website B. Website A does not know that the request was actually initiated by B. Therefore, the request will be processed with the C permission based on the Cookie information of user C, resulting in the execution of malicious code from website B.
Ii. CSRF vulnerability defense
CSRF vulnerability defense can be implemented in three aspects: server-side defense, user-side defense, and security device defense.
1. server-side defense
. 1.1 verify the HTTP Referer Field
According to the HTTP protocol, a field in the HTTP header is called Referer, which records the source address of the HTTP request. Generally, a request to access a security restricted page must come from the same website. For example, a bank transfer is through the user access http://bank.test/test? Page = 10 & userID = 101 & money = 10000 page complete. You must first log on to bank. test and then click the button on the page to trigger the transfer event. When a user submits a request, the Referer value of the transfer request is the URL of the page where the transfer button is located (in this example, it is usually the address starting with the bank. test domain name ). If an attacker wants to launch a CSRF attack on a bank website, he can only construct a request on his website. When a user sends a request to a bank through the attacker's website, the Referer of the request points to the attacker's website. Therefore, to defend against CSRF attacks, a bank website only needs to verify its Referer value for each transfer request. the domain name starting with "test" indicates that the request is from the bank website and is legal. If the Referer is another website, the request may be rejected due to a CSRF attack.
1.2 add and verify the token in the request address
The reason why CSRF attacks are successful is that attackers can forge user requests and all user authentication information in the request is stored in cookies, therefore, attackers can directly use their cookies to pass security authentication without knowing the authentication information. We can see that the key to defending against CSRF attacks is to put information that cannot be forged by attackers in the request, and the information does not exist in cookies. In view of this, system developers can add a random token in the form of parameters in the HTTP request, and create an interceptor on the server to verify the token, if the request does not contain a token or the content of the token is incorrect, the request may be rejected due to a CSRF attack.
1.3 customize and verify attributes in the HTTP Header
The custom attribute method also uses token for verification. Unlike the previous method, token is not placed in the HTTP request as a parameter, instead, it is placed in the Custom Attributes of the HTTP header. Through the XMLHttpRequest class, you can add the HTTP header attribute csrftoken to all requests of this class at a time and put the token value into it. This solves the inconvenience caused by adding a token to the previous request. At the same time, the address of the request through this class will not be recorded in the address bar of the browser, do not worry that the token will be leaked to other websites through Referer.
2. Other defense methods
1. CSRF attacks are conditional. When a user accesses a malicious link, the authenticated cookie is still valid. Therefore, when the user closes the page, the authentication cookie should be cleared in time, it is particularly important for browsers that support the TAB mode (new TAB to open web pages.
2. use as few as possible or do not use the request () variable to obtain the specified request parameter. form () or request. querystring (), which is helpful to prevent CSRF vulnerability attacks. This method only cannot completely defend against CSRF attacks, but increases the difficulty of attacks to a certain extent.
Sample Code:
Java sample code
The following uses Java as an example to illustrate the above three methods with code. No matter what method is used, the interceptor on the server is necessary. It will check whether the incoming request meets the requirements and decide whether to continue or discard the request based on the results. In Java, the interceptor is implemented by the Filter. We can write a Filter and configure it in web. xml to block requests to access all resources that require CSRF protection.
The Referer verification code for the request in the filter is as follows:
Listing 1. Verifying Referer in Filter
// Obtain the Referer value String referer = request. getHeader ("Referer") from the HTTP header; // determine whether the Referer starts with bank. example if (referer! = Null) & (referer. trim (). startsWith ("bank. example ") {chain. doFilter (request, response);} else {request. getRequestDispatcher ("error. jsp "). forward (request, response );}
The above code first obtains the Referer value and then judges it. When it is not empty, it uses the bank. if it starts with example, the request will continue. Otherwise, it may be a CSRF attack and it will go to error. jsp page.
If you want to further verify the token value in the request, the Code is as follows:
Listing 2. Verifying the token in the filter request
HttpServletRequest req = (HttpServletRequest) request; HttpSession s = req. getSession (); // obtain the csrftoken attribute String token en = (String) s from the session. getAttribute ("csrftoken"); if (token en = null) {// generate a new token and put it into the session token en = generateToken (); s. setAttribute ("csrftoken", Token en); chain. doFilter (request, response);} else {// obtain csrftoken String xhrToken = req from the HTTP header. getHeader ("csrftoken"); // Request Parameters Obtain csrftoken String pToken = req. getParameter ("csrftoken"); if (token en! = Null & xhrToken! = Null & sToken. equals (xhrToken) {chain. doFilter (request, response);} else if (sToken! = Null & pToken! = Null & sToken. equals (pToken) {chain. doFilter (request, response);} else {request. getRequestDispatcher ("error. jsp "). forward (request, response );}}
First, check whether there is a csrftoken In the session. If there is no csrftoken, it is regarded as the first access and the session is newly created. Then a new token is generated, stored in the session, and the request is executed. If csrftoken already exists in the session, it indicates that the user has established an active session with the server. In this case, check whether the token is included in the request, because the request may come from regular access or XMLHttpRequest asynchronous access, we try to obtain the csrftoken parameter from the request and the csrftoken custom attribute from the HTTP header respectively and compare it with the value in the session, if there is a valid token, the request is valid and can be executed. Otherwise, the request is redirected to the error page. There are many methods to generate token, and any random algorithm can be used. The Java UUID class is also a good choice.
In addition to using filter on the server to verify the token value, we also need to append this token to each request on the client, this is to use js to append the csrftoken code to the link and form request address in html. the token is defined as a global variable and its value can be obtained from the session.
Listing 3. attaching a token to a request on the client
Function appendToken () {updateForms (); updateTags ();} function updateForms () {// obtain all form elements on the page var forms = document. getElementsByTagName ('form'); for (I = 0; I <forms. length; I ++) {var url = forms [I]. action; // if the form's action value is null, no csrftoken if (url = null | url = "") continue is attached. // The input element is dynamically generated, var e = document. createElement ("input"); e. name = "csrftoken"; e. value = token; e. Type = "hidden"; forms [I]. appendChild (e) ;}} function updateTags () {var all = document. getElementsByTagName ('A'); var len = all. length; // traverse all a elements for (var I = 0; I <len; I ++) {var e = all [I]; updateTag (e, 'href ', token) ;}} function updateTag (element, attr, token) {var location = element. getAttribute (attr); if (location! = Null & location! = ''') {Var fragmentIndex = location. indexOf ('#'); var fragment = null; if (fragmentIndex! =-1) {// The url contains only equivalent pages of the anchor mark fragment = location. substring (fragmentIndex); location = location. substring (0, fragmentIndex);} var index = location. indexOf ('? '); If (index! =-1) {// The url contains other parameters: location = location + '& csrftoken =' + token ;} else {// no other parameter location = location + 'in the url '? Csrftoken = '+ token;} if (fragment! = Null) {location + = fragment;} element. setAttribute (attr, location );}}
In the client html, the token must be added in two aspects: form and link. This code first traverses all the forms, adds a hidden field at the end of the form, and puts the csrftoken in it. Then, the Code traverses all links and marks a, and adds the csrftoken parameter to its href attribute. Note that for a. href, this attribute may already have parameters or an anchor. Therefore, the csrftoken must be added in different formats.
If your website uses XMLHttpRequest, you also need to customize the csrftoken attribute in the HTTP header. Use dojo. xhr to add the custom attribute code to XMLHttpRequest as follows:
Listing 4. Custom Attributes in the HTTP Header
Var plainXhr = dojo. xhr; // rewrite dojo. xhr method dojo. xhr = function (method, args, hasBody) {// ensure that the header object contains args. headers = args. header | |{}; tokenValue = '<% = request. getSession (false ). getAttribute ("csrftoken") %> '; var token = dojo. getObject ("tokenValue"); // put the csrftoken attribute in the header args. headers ["csrftoken"] = (token )? Token: ""; return plainXhr (method, args, hasBody );};
Dojo is rewritten here. the xhr method first ensures that dojo. an HTTP header exists in xhr, and then in args. add the csrftoken field to headers and extract the token value from the session and put it into the field.
PHP code example:
Please refer to the following simple application, which allows users to buy pens or pencils. The Interface contains 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>
The following buy. php program processes the form submission 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>'; } }?>
Attackers will first use this form to observe its actions. For example, after buying a pencil, the attacker knows that a thank-you message will appear after the purchase is successful. With this in mind, attackers will try to access the following URL to submit data in the GET mode for the same purpose:
Http://store.example.org/buy.php? Item = pen & quantity = 1
If successful, attackers can now obtain the URL format that can be purchased when a legitimate user accesses the website. In this case, Cross-Site Request Forgery is very easy, because the attacker only needs to cause the victim to access the URL.
See the changed code for the ghost application:
session_start(); $token = md5(uniqid(rand(), TRUE)); $_SESSION['token'] = $token; $_SESSION['token_time'] = time();?>
Form:
<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 these simple modifications, a cross-site Request Forgery attack must include a valid Verification code to completely mimic form submission. Because the verification code is stored in the user's session, attackers must use different verification codes for each victim. This effectively limits any attacks on a user and requires the attacker to obtain the valid Verification Code of another user. Using your own verification code to forge another user's request is invalid.
This verification code can be simply checked using a conditional expression:
<?php if (isset($_SESSION['token']) && $_POST['token'] == $_SESSION['token']) { /* Valid Token */ }?>
You can also add a valid time limit to the Verification code, such as 5 minutes:
<?php $token_age = time() - $_SESSION['token_time']; if ($token_age <= 300) { /* Less than five minutes has passed. */ }?>
By including the verification code in your form, you have actually eliminated the risk of cross-site request forgery. You can use this process in any form that requires an operation.