Understand CORS (cross domain resource sharing)

Key points of knowledge

  • The browser enforces the same origin policy and denies access to websites of different sites.
  • The same origin policy will not block requests to other sources, but will disable access to {js} responses.
  • The CORS header allows access to cross domain responses.
  • CORS needs to be cautious when working with Credentials.
  • CORS is a browser enforcement policy that does not affect other applications.

 

Case explanation

In order to reduce the amount of code, part of the code is demonstrated here, and the complete code can be obtained on Github.

Let's start with an example. Suppose we have a website with a URL of http://good.com:8000/public:

app.get('/public', function(req, res) {
  res.send(jsON.stringify({
    message: 'This is public'
  }));
})

We also have a simple login function. Users can enter a shared key and set a cookie to identify it as authenticated:

app.post('/login', function(req, res) {
  if(req.body.password === 'secret') {
    req.session.loggedIn = true
    res.send('You are now logged in!')
  } else {
    res.send('Wrong password.')
  }
})

We can obtain some private data through / private, and then we can further verify through the login status above.

app.get('/private', function(req, res) {
  if(req.session.loggedIn === true) {
    res.send(JSON.stringify({
      message: 'THIS IS PRIVATE'
    }))
  } else {
    res.send(JSON.stringify({
      message: 'Please login first'
    }))
  }
})

 

Request our API from other domains through AJAX

At present, our API is not specially designed, but it can allow others to obtain data from / public URL. Suppose our API is located in good COM: 300 / public, and our client is hosted in thirdparty COM, the client may run the following code:

fetch('http://good.com:3000/public')
  .then(response => response.text())
  .then((result) => {
    document.body.textContent = result
  })

But this doesn't work in our browser. Let's take a look at it through the control network http://thirdparty.com Request for:

The request was successful, but the result is not available. The reason can be found in the console:

ah We are missing the access control allow origin header. But why do we need it? What's its use?

 

Homologous strategy

The reason why we can't get the response result in JS is the homology strategy. The purpose of this policy is to ensure that one website cannot read the results of requests to another website and is enforced by the browser. For security reasons, today's web pages use cookie s for authentication. If reading is not limited, the malicious script code in web page B can imitate the real user at will.

For example: if we're in the "example Org, we don't want the website to send a request to our bank website to obtain our account balance and transactions.

Homology strategy can prevent this from happening.

In this case, the "source" is determined by

  • Protocol (e.g. http)
  • Domain name (e.g. example.com)
  • Port (e.g. 8000)

 

Description of CSRF (Cross Site Request Forgery)

Please note that there is a kind of attack called CSRF (Cross Site Request Forgery), which cannot be avoided by homology strategy.

In the CSRF attack, the attacker sends a request to the third-party page in the background, such as sending a POST request to our bank website. If we have an effective conversation with our bank, any website can send a request in the background, and the request will be executed unless our bank website has countermeasures against CSRF.

Note that although the homology policy has taken effect, our example request is from thirdparty Com successfully requested to good We just can't get results. Www. 68mn. But for CSRF, there is no need to obtain the results.

For example, there is an API # that sends e-mail through POST request. The returned content is what we need to care about. Clam attackers don't care about the result. They care about whether the e-mail has been sent successfully.

 

Enable CORS for our API

Now, we hope to allow JS on third-party sites (such as thirdparty.com) to access our API and get a response. To do this, we can enable the CORS header according to the error prompt:

app.get('/public', function(req, res) {
  res.set('Access-Control-Allow-Origin', '*')
  res.send(...)
})

Here, the access control allow origin header is set to *, which means that any host is allowed to access this URL and get the results of the response:

 

Non simple request and pre inspection

If the request is not a simple request, the browser will send a pre request first:

The browser first asks the server whether the domain name of the current web page is in the license list of the server, and what HTTP verbs and header information fields can be used. Only when you get a positive reply will the browser send a formal XMLHttpRequest request, otherwise an error will be reported.

The previous example is a simple request. A simple request is a GET or POST request with some allowed headers and flag header values. Now, to the third party COM # made some changes to enable it to GET data in JSON format.

fetch('http://good.com:3000/public', {
  headers: {
    'Content-Type': 'application/json'
  }
})
  .then(response => response.json())
  .then((result) => {
    document.body.textContent = result.message
  })

But this makes thirdparty Com crashed, and the network panel showed us the reason:

When the browser finds that this is a non simple request, it automatically sends a "pre check" request. The request method used for the "pre check" request is OPTIONS, which indicates that the request is used for inquiry. In the header information, the key field is Origin, which indicates the source of the request. In addition to the Origin field, the header information of the "pre check" request includes two special fields.

(1) Access-Control-Request-Method

This field is required to list which HTTP methods will be used in the browser's CORS request. The above example is GET.

(2) Access-Control-Request-Headers

This field is a comma separated string that specifies the additional header information field that the browser CORS request will send

This mechanism allows the web server to decide whether to allow the actual request. The browser sets the header information of access control request headers and access control request method to tell the server what request is needed, and the server responds with the corresponding header information.

Our server has not responded to these header information, so we need to add them:

app.get('/public', function(req, res) {
  res.set('Access-Control-Allow-Origin', '*')
  res.set('Access-Control-Allow-Methods', 'GET, OPTIONS')
  res.set('Access-Control-Allow-Headers', 'Content-Type')
  res.send(JSON.stringify({
    message: 'This is public info'
  }))
})

Now, thirdparty Com can get a response again.

 

Credentials and CORS

Now, suppose we have logged in to good COM and can use sensitive information to access the / private # URL. By setting CORS, you can make other websites, such as evil Com for these sensitive information, take a look at:

fetch('http://good.com:3000/private')
  .then(response => response.text())
  .then((result) => {
    let output = document.createElement('div')
    output.textContent = result
    document.body.appendChild(output)
  })

Whether or not you have logged in to good COM, you will see "Please login first".

The reason is that when the request comes from another source, it comes from good Com cookie will not be sent, in this case, evil com. We can ask the browser to send cookies, even if it is a cross domain source:

fetch('http://good.com:3000/private', {
  credentials: 'include'
})
  .then(response => response.text())
  .then((result) => {
    let output = document.createElement('div')
    output.textContent = result
    document.body.appendChild(output)
  })

But again, it doesn't work in browsers. In fact, it's also a good thing.

For example, any website can issue an authenticated request, but it will not send the actual cookie and cannot get a response.

So we don't want evil Com can access this private data - but if we want thirdparty COM / private, what should I do?

In this case, you need to set the access control allow credentials header to true:

app.get('/private', function(req, res) {
  res.set('Access-Control-Allow-Origin', '*')
  res.set('Access-Control-Allow-Credentials', 'true')
  if(req.session.loggedIn === true) {
    res.send('THIS IS THE SECRET')
  } else {
    res.send('Please login first')
  }
})

But this still doesn't work, and allowing each authenticated cross source request is a dangerous practice.

When we want to allow thirdparty When visiting / private, you can specify this source in the header:

app.get('/private', function(req, res) {
  res.set('Access-Control-Allow-Origin', 'http://thirdparty.com:8000')
  res.set('Access-Control-Allow-Credentials', 'true')
  if(req.session.loggedIn === true) {
    res.send('THIS IS THE SECRET')
  } else {
    res.send('Please login first')
  }
})

Now? http://thirdparty:8000 You can also access private data, and evil COM is locked.

Complete website search resources https://www.renrenfan.com.cn Guangzhou VI design companyhttps://www.houdianzi.com

Allow multiple sources

Now, we have allowed a source to use authentication data for cross source requests. But what if there are multiple third-party sources?

In this case, you can use the white list:

const ALLOWED_ORIGINS = [
  'http://anotherthirdparty.com:8000',
  'http://thirdparty.com:8000'
]
app.get('/private', function(req, res) {
  if(ALLOWED_ORIGINS.indexOf(req.headers.origin) > -1) {
    res.set('Access-Control-Allow-Credentials', 'true')
    res.set('Access-Control-Allow-Origin', req.headers.origin)
  } else { // allow other origins to make unauthenticated CORS requests
    res.set('Access-Control-Allow-Origin', '*')        
  }

  // let caches know that the response depends on the origin
  res.set('vary', 'Origin');

  if(req.session.loggedIn === true) {
    res.send('THIS IS THE SECRET')
  } else {
    res.send('Please login first')
  }
})

Remind again: don't send req directly headers. Origin as the CORS original header. This will allow any website to access requests to authenticate our website.

There may be exceptions to this rule, but at least think twice before implementing CORS with credentials without a whitelist.

 

summary

In this article, we studied the homology strategy and how to use CORS to allow cross source requests when needed.

This requires server and client settings, and a pre check request will appear according to the request.

Care should be taken when handling authenticated cross domain requests. Whitelists can help allow multiple sources without risking disclosing sensitive data that is protected after authentication.

Tags: Javascript

Posted by seoreferrals on Wed, 04 May 2022 09:09:49 +0300