HTTP Header: CORS
Image from Joe Honton | A Series of Unhappy Events in Corslandia
What?β
Image from portswigger | Cross-origin resource sharing (CORS)
Cross-origin resource sharing (CORS) is a browser mechanism which enables controlled access to resources located outside of a given domain. It extends and adds flexibility to the same-origin policy (SOP). However, it also provides potential for cross-domain based attacks, if a website's CORS policy is poorly configured and implemented. CORS is not a protection against cross-origin attacks such as cross-site request forgery (CSRF). by portswigger | Cross-origin resource sharing (CORS)
The headerβ
In order to manage CORS we need to use several headers that have a common pattern Access-Control-*
Access-Control-Allow-Origin
: indicates whether the response can be shared with requesting code from the given origin. Valid values (single url,*
ornull
). See DocumentationAccess-Control-Allow-Credentials
: tells browsers whether to expose the response to frontend JavaScript code when the request's credentials mode (Request.credentials
) is include. See DocumentationAccess-Control-Allow-Methods
: response header specifies the method or methods allowed when accessing the resource in response to a preflight request. See Documentation
There are other headers like Access-Control-Allow-Headers
, Access-Control-Expose-Headers
, Access-Control-Max-Age
... used for CORS preflight requests
The codeβ
There is an official Middleware made by the Express team called CORS
Simple Usageβ
const express = require('express')
const cors = require('cors')
const app = express()
app.use(cors())
app.get('/products/:id', (req, res, next) => {
res.json({ msg: 'This is CORS-enabled for all origins!' })
})
app.listen(8080, () => {
console.log('CORS-enabled web server listening on port 80')
})
Configuring CORS w/ Dynamic Originβ
const express = require('express')
const cors = require('cors')
const app = express()
const whitelist = ['http://example1.com', 'http://example2.com']
const corsOptions = {
origin: (origin, cb) => {
whitelist.includes(origin)
? cb(null, true)
: cb(new Error('Not allowed by CORS'))
}
}
app.get('/products/:id', cors(corsOptions), (req, res, next) => {
res.json({ msg: 'This is CORS-enabled for a whitelisted domain.' })
})
app.listen(8080, () => {
console.log('CORS-enabled web server listening on port 80')
})
Don't forget to check the configuration features
Attacksβ
Most of the CORS attacks are based on generating a response header that allow the attackers to add their malicious domain in the Access-Control-Allow-Origin
.
In most of the cases this flaws are generated by misconfiguration.
Solutionβ
- Never use the Request
origin
value as the responseAccess-Control-Allow-Origin
value. - Avoid wildcard validation like
*-website.com
, as the malicious attacks can match the criteria (good-website.com
,evil-website.com
...). - Avoid to whitelist
null
as valid Origin value in the request header. - Avoid wildcards in internal networks