restsecurityhttpwebservercors

CORS - localhost as allowed origin in production


Occasionally when troubleshooting bugs in production, it would be convenient to be able to hit our production REST server from my local dev environment. But i'm concerned that adding localhost to allowed origins would be a security risk. Searches have yielded conflicting information. Are my concerns valid? Why or why not?


Solution

  • I'm assuming you have

    Access-Control-Allow-Credentials: true
    Access-Control-Allow-Origin: https://localhost
    

    The risk is that any services running on a user's machine could effectively bypass the Same Origin Policy for your site.

    So if you have a REST URL such as

    https://example.com/User/GetUserDetails
    

    A malicious or compromised service running on the user's computer could make that request via the user's browser and then grab details about the user, because their authentication cookie will be passed with the request.

    Now, you could argue that a malicious service running on the user's computer could just grab the authentication cookie from their browser directly and then make the request itself. However, if the service has some flaws of its own (say XSS), this could allow another site to compromise the user via your REST service (evil.example.org --XSS-> localhost -CORS-> example.com/User/GetUserDetails).

    Another scenario that could put you at risk if the user is running a local reverse proxy to access something. This would enable the target site to compromise the user through yours, should that target site be malicious or be compromised. This is because the user would be accessing the target site with a domain of localhost.

    Worked example

    I entered this as a comment initially, but I thought it might be beneficial to add it to my answer.

    Say there's a web service running locally under a service account (i.e. not in the context of the user's account or any account with privileges), or say if the user creates an SSH tunnel to a web server (e.g. ssh server.example.com -N -L 127.0.0.1:8000:127.0.0.1:8000) in order to access a remote server locally via localhost. Let's call this service BoogleLocal.

    Say said service is compromised via a supply chain attack... e.g. the vendor has been hacked and the attacker has published a version of BoogleLocal that targets your popular production service, https://x.example.com. The localhost app (or the one on the SSH server) updates automatically and gets the new, compromised, code. The malicious code on BoogleLocal accesses https://x.example.com/get_otp client-side (so in the context of the website user).

    Because https://localhost:8000 is allowed (this is known as the attacker has inspected the headers of this popular site), the code can get the One Time Password from x.example.com (as the victim is logged in) and send it to themselves, allowing them to login to x.example.com and fully compromise the user's account on there. This is a simplified example as it is one step, but more advanced hacks are now possible such as getting the CSRF token and using it for other requests (e.g. update username/email).

    If you really need to do this I suggest you have a special developer account for your REST service that when accessed adds the Access-Control-Allow-Origin: https://localhost header to your requests only. That way, you are not putting other users at risk because you know you are only running the front-end server only at https://localhost so you cannot be compromised by your open CORS setting.

    Another way may be to use something like noonewouldusethis2859282.localhost for your local copy of the front-end. Then you can safely add the Access-Control-Allow-Origin: https://noonewouldusethis2859282.localhost header because nobody else would use this and would be safe from CORS attacks. You should only output this value when the request's Origin header matches your domain of choice.