SSShooter

SSShooter

Write like you're running out of time.

Front-end Network Security Required Course 1 SOP, CSRF, and CORS

This article mainly involves three key terms:

  • Same-origin policy (SOP)
  • Cross-site request forgery (CSRF)
  • Cross-Origin Resource Sharing (CORS)

Same-origin policy (SOP)#

Same-origin#

First, let's explain what same-origin means: if the protocol, domain, and port are the same, it is considered the same origin.

Limitations#

The reason you encounter cross-origin issues is because of the various limitations of SOP. But specifically, what does SOP restrict?

If you say SOP restricts "access to non-same-origin resources," that's not entirely correct. The simplest example is when you reference images, CSS, JS files, and other resources, cross-origin requests are allowed.

If you say SOP prohibits "cross-origin requests," that's also not entirely correct. In essence, SOP does not prohibit cross-origin requests, but intercepts the response after the request.

In fact, SOP can be divided into two cases:

  • You can normally reference iframes, images, and other resources, but you are restricted from manipulating their content.
  • Directly restricts AJAX requests, more precisely, restricts the manipulation of AJAX response results, which leads to CSRF as mentioned later.

However, fundamentally, these two cases are the same: in any case, for non-same-origin resources, the browser can "directly use" them, but programmers and users cannot manipulate this data, thus preventing certain malicious behavior. This is one of the ways modern secure browsers protect users.

Here are three examples that you may encounter in practical applications:

  • Using AJAX to request other cross-origin APIs, the most common situation and a nightmare for front-end beginners.
  • Communicating between iframes and parent pages (such as accessing DOM or variables), which has a relatively low occurrence rate and easy solutions.
  • Manipulating cross-origin images (such as those from <img>) when working with canvas.

Without SOP:

  • Confidential information in iframes can be easily accessed.
  • More CSRF attacks can be carried out.
  • APIs can be abused by third parties.

Bypassing Cross-Origin Restrictions#

Although SOP makes users more secure, it also brings some trouble to programmers because there are sometimes cross-origin requirements in business scenarios. Due to space limitations and the availability of many related articles online, I will not go into detail about the solutions to bypass cross-origin restrictions here. Instead, I will provide a few keywords:

For AJAX:

  • Use JSONP
  • Configure CORS on the backend
  • Use backend reverse proxy
  • Use WebSocket

For iframes:

  • Use location.hash or window.name for information exchange
  • Use postMessage

Cross-Site Request Forgery (CSRF)#

Overview#

CSRF (Cross-Site Request Forgery) is a common type of attack. It refers to a situation where a user is logged into website A, and website B, in some way, calls the interface of website A to perform operations. The interface of website A will automatically include the user's cookie in the request.

As mentioned earlier, SOP allows the loading of resources through HTML tags, and SOP does not prevent interface requests but intercepts the response. CSRF takes advantage of these two characteristics.

For GET requests, you can simply put them in an <img> tag to make a cross-origin request without being noticed.

For POST requests, many examples use form submission:

<form action="<nowiki>http://bank.com/transfer.do</nowiki>" method="POST">
  <input type="hidden" name="acct" value="MARIA" />
  <input type="hidden" name="amount" value="100000" />
  <input type="submit" value="View my pictures" />
</form>

Therefore, SOP cannot be used as a method to prevent CSRF.

Reviewing the limitations of SOP, both of these examples directly use HTML tags to make requests. The browser allows this because you cannot directly manipulate the obtained results using JavaScript.

SOP and AJAX#

For AJAX requests, you can freely manipulate the data after receiving it. Although the same-origin policy prevents the response from being accessed, the request has already been sent to the server and the response has been returned. However, due to security policies, the browser does not allow you to continue manipulating the response using JavaScript, resulting in the familiar error message: blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource..

So, to emphasize again, the same-origin policy cannot be used as a method to prevent CSRF.

However, there are exceptions that can prevent CSRF. The browser does not allow all requests to be successfully sent. The above situation only applies to simple requests, and the relevant knowledge will be explained in detail in the next section on CORS.

Countermeasures for CSRF#

Although CSRF takes advantage of SOP, does that mean SOP is useless?

No! Do you remember that SOP restricts the naming space of cookies? Although the request automatically includes cookies, the attacker still cannot directly access the content of the cookie.

Therefore, there are two approaches to counter CSRF: the commonly used authentication method in front-end and back-end separation, which is token-based authentication, and the token is not stored in cookies but manually added to the request header.

Another method is to include the content of the cookie in the request through query, body, or header. When the request reaches the server, it does not check the information transmitted by the cookie, but only looks at the custom field. If it is correct, it means that the request is sent by the same domain that can access the cookie, and CSRF cannot achieve this. (This method is used for front-end and back-end separation, while for server-side rendering, the token can be directly written into the DOM.)

Here is an example code:

var csrftoken = Cookies.get('csrfToken')

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return /^(GET|HEAD|OPTIONS|TRACE)$/.test(method)
}
$.ajaxSetup({
  beforeSend: function(xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader('x-csrf-token', csrftoken)
    }
  },
})

Cross-Origin Resource Sharing (CORS)#

Cross-origin is a browser restriction, and Cross-Origin Resource Sharing (CORS) is the result of coordination between the server and the browser.

If the server sets up CORS-related configurations, the Access-Control-Allow-Origin header will be added to the response headers sent to the browser. If the value of this header matches the current origin, the browser will unlock the cross-origin restriction.

HTTP/1.1 200 OK
Date: Sun, 24 Apr 2016 12:43:39 GMT
Server: Apache
Access-Control-Allow-Origin: http://www.acceptmeplease.com
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: application/xml
Content-Length: 423

For CORS, there are two types of requests.

Simple Requests#

  • The request method is GET, POST, or HEAD.
  • The Content-Type is set to application/x-www-form-urlencoded, multipart/form-data, or text/plain.

Requests that meet the above two conditions are considered CORS simple requests. Simple requests are sent directly to the server and can lead to CSRF attacks.

Preflight Requests#

Requests that do not meet the requirements of simple requests need to send a preflight request. The browser will send an OPTIONS request to the server before the actual request to check if the current origin meets the CORS requirements. Only after the verification is passed will the actual request be sent.

For example, a POST request that uses application/json to send parameters is not a simple request and will be intercepted during the preflight.

Another example is a PUT request, which will also send a preflight request.

The aforementioned exceptions that can prevent CSRF refer to preflight requests. Even if the preflight request for cross-origin is successful, the actual request cannot be sent, which ensures that CSRF cannot succeed.

CORS and Cookies#

Unlike the same origin, CORS requests for cross-origin do not send cookies and HTTP authentication information by default. Both the front-end and back-end need to set up the request to include cookies.

This is why when making CORS requests, axios needs to set withCredentials: true.

Here is an example of setting CORS in the backend using the Koa framework in Node.js:

/**
 * CORS middleware
 *
 * @param {Object} [options]
 *  - {String|Function(ctx)} origin `Access-Control-Allow-Origin`, default is request Origin header
 *  - {String|Array} allowMethods `Access-Control-Allow-Methods`, default is 'GET,HEAD,PUT,POST,DELETE,PATCH'
 *  - {String|Array} exposeHeaders `Access-Control-Expose-Headers`
 *  - {String|Array} allowHeaders `Access-Control-Allow-Headers`
 *  - {String|Number} maxAge `Access-Control-Max-Age` in seconds
 *  - {Boolean} credentials `Access-Control-Allow-Credentials`
 *  - {Boolean} keepHeadersOnError Add set headers to `err.header` if an error is thrown
 * @return {Function} cors middleware
 * @api public
 */

By the way, when Access-Control-Allow-Credentials is set to true, Access-Control-Allow-Origin cannot be set to * due to security reasons. It's quite troublesome...

Upcoming Topics#

That's all for now. If you have any questions, please leave them in the comments. In the future, I may write about XSS, CSP, and HTTP/HTTPS-related topics.

Original article: https://ssshooter.com/2019-11-08-csrf-n-cors/

References#

Highly recommended Whitepaper: The Definitive Guide to Same-origin Policy

egg.js Network Security

CSRF owasp

Cross-Origin Resource Sharing (CORS) in detail

Browser Same-origin Policy and Its Bypass Methods

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.