skip to content
usubeni fantasy logo Usubeni Fantasy

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

/ 7 min read

This Post is Available In: CN EN ES JA

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.

urlSame Origin
https://niconico.comBase
https://niconico.com/spiritYes
https://sub.niconico.com/spiritNo
http://niconico.com/spiritNo
https://niconico.com:8080/spiritNo

Restrictions

The reason you encounter cross-origin issues is precisely because of the various restrictions imposed by 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 referencing 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 of the request.

In fact, SOP can be divided into two cases:

  • It allows normal referencing of iframes, images, and other resources, but restricts operations on their contents.
  • It directly restricts AJAX requests, more precisely, restricts the manipulation of AJAX response results, which leads to the issue of CSRF 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, thereby preventing malicious behavior. This is one of the ways modern secure browsers protect users.

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

  • Making AJAX requests to other cross-origin APIs, which is the most common case and a nightmare for frontend beginners.
  • Communicating between iframes and parent pages (such as accessing DOM or variables), which has a relatively low occurrence rate and easy-to-understand solutions.
  • Manipulating cross-origin images (e.g., from <img> tags) - this issue arises when manipulating images in canvas.

Without SOP:

  • Confidential information in iframes can be freely accessed.
  • CSRF attacks can be carried out more recklessly.
  • APIs can be abused by third parties.

Bypassing cross-origin restrictions

Although SOP enhances user security, it also brings certain difficulties for programmers because there are legitimate cross-origin requirements in some business scenarios. Due to space limitations and the abundance of related articles online, I will not go into detail about bypassing 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 the cookie containing the login information is stored normally. Another website B, through some means, makes requests to website A’s API, and the cookie is automatically included in the request.

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

For GET requests, they can be easily sent to a cross-origin API by using <img> tags.

For POST requests, many examples use form submissions:

<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>

So SOP cannot be used as a method to prevent CSRF.

Looking back at the limitations of SOP, both of these examples are directly initiating requests using HTML tags, and browsers allow this because you cannot directly manipulate the results obtained using JavaScript.

SOP and AJAX

For AJAX requests, after obtaining the data, you can freely manipulate it using JavaScript. At this point, although the same-origin policy will prevent the response, the request will still be sent. This is because the browser executes the response interception, not the backend program. In fact, your request has already been sent to the server and returned a result, but due to security policies, the browser does not allow you to continue JavaScript operations, so you see the familiar error message blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource..

So, once again, it is emphasized that the same-origin policy cannot be used as a method to prevent CSRF.

However, there are exceptions that can prevent CSRF attacks. Browsers do not allow all requests to be successfully sent. The above situation is only applicable to simple requests, and the relevant knowledge will be explained in detail in the CORS section below.

Countermeasures against CSRF

SOP seems to have been taken advantage of by CSRF, but is it really useless?

No! Do you remember that SOP restricts the naming space of cookies? Although requests automatically carry cookies, attackers still cannot directly access the content of the cookie itself.

So, to counter CSRF attacks, there is a solution: the commonly used authentication method in front-end and back-end separation is to use token-based authentication, and do not store the token in cookies. Instead, manually add it to the request header when making requests.

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 fields. If they are correct, it means that the request was sent by the same domain that can access the cookie, which CSRF attacks cannot achieve. (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 has set up CORS-related configurations, the response headers returned to the browser will include Access-Control-Allow-Origin. If the value of this field 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 Request

  • 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 these two conditions are considered CORS simple requests. Simple requests are sent directly to the server, which can lead to CSRF attacks.

Preflight Request

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

For example, a POST request with application/json as the parameter is considered a non-simple request and will be intercepted during the preflight.

Similarly, a PUT request will also trigger a preflight request.

The exception mentioned above that can prevent CSRF attacks refers to the preflight request. Even if the preflight request for cross-origin is successful, the actual request cannot be sent, which ensures the prevention of CSRF attacks.

CORS and Cookies

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

This is why when making a CORS request with axios, withCredentials: true needs to be set.

Here is an example of CORS configuration in the Koa framework for Node.js backend:

/**
* 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 * for security reasons. It’s quite troublesome, but it’s for the sake of security…

Upcoming Topics

That’s all for now. If you have any questions, please feel free to ask in the comments. In the future, I may discuss topics such as XSS, CSP, and http/https.

References

评论组件加载中……