CORS Security: Set Access-Control-Allow-Origin to Origin on Request Header

I am unsure of the finer points of the security with regards Access-Control-Allow-Origin and cookies etc.

I was trying to produce an API that allowed authentication from any website. As such I needed to set:

Access-Control-Allow-Origin to * and

Access-Control-Allow-Credentials to true

This is not allowed due to security constraints.

However what would be the problem with setting the Access-Control-Allow-Origin response header to the value of the Origin header of the request? Is that a massive security hole?!

e.g. (node)

// CORS
app.all('*', function (req, res, next) {
    res.header('Access-Control-Allow-Origin', req.headers.origin);
    res.header('Access-Control-Allow-Headers', 'origin, content-type, accept');
    res.header('Access-Control-Allow-Credentials', 'true');
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');

    if (req.method == 'OPTIONS') {
        res.status(200).end();
    }

    next();
});

What would be the problem with setting the Access-Control-Allow-Origin response header to the value of the Origin header of the request?

That's exactly the same as allowing the origin * and requires no extra effort from any would be attacker. However, you will be able to solve this in a similar way. If you had a pre-approved list of domains that you wanted to allow access, you could check the domain from within the sent Origin header, and if it matches an allowed one you could then reflect Origin inside Access-Control-Allow-Origin.

The security risk with * is that it'll allow any site to read content that may contain private user data.

As you are allowing any domain to contact your API with credentials (effectively Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true) you are also allowing other domains to possibly hijack data.

For example, while you victim is logged into your API an attacker forges an email to your victim to go and view a funny video on the attacker's domain www.evil.com. While the cat video is playing, the attackers domain makes an AJAX request to your API at www.example.com/Get_User_Profile_Details and reads the user's details including DOB, home address, phone number and other details. Implied Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true will allow this data to be retrieved by CORS when it is normally blocked by the Same Origin Policy.

So to guard against this you should only output the Access-Control-Allow-Credentials header for approved domains.

I was trying to produce an API that allowed authentication from any website.

If you do in fact require access from any website then you will need to be careful. You could store the Origin of the initial authentication request (i.e. the user entering their username and password) against the session ID. On every request you would need to check the Origin and see if it matches the Origin stored against the server side session. If so you output the Access-Control-Allow-Origin: https://www.foo.com header (assuming www.foo.com is where the user logged into) and if not you don't output Access-Control-Allow-Origin at all.

You may also find this post interesting.

I'm no expert in these things ...

I would suggest in principal it's a little than just having "*" as it would take some effort for the caller to put in whatever they'd want.

I would suggest if you really want to tie things down (to approved [API?] users) would be to hold the origin in configuration and check each time?

As I say, I'm no expert.

For the record Greg sits opposite me at work .. I could have chatted to him about my point of view, but I wanted SO points :-)