I have a REST API. I want to use this API from multiple web applications. I want to only allow requests from Whitelisted clients. My first point of call would be to add the necessary headers to the REST API Web Server (allowed domains) all good. But does this only whitelist clients that use browsers?. Is there any way to actually prevent scripted access to the API? By scripted access I mean PHP, JAVA, .NET or some other server side language making a web call directly to the REST API url, and of course since the request is manually constructed they can add any headers they want (ORIGIN etc), so could fake a domain request. I don't even think you need a script, a composed request from fiddler would achieve the same result.

Is it possible allow only requests from browsers (that pass the CORS config) and deny all others.

Ultimately is a REST API that enables CORS intrinsically insecure. Even though CORS headers give you a mechanism for whitelisting clients it is based on HTTP and manually constructed HTTP requests can easily circumvent this.

Granted a CORS config will prevent arbitrary embedding in website you don't want, so is that all its good for.

It has been a topic that has come up within my team a few times. These days we are writing more and more Web (REST) API kind of applications, which is kinda good cause functionality can be reused in a new application simply, but thoughts about security have been raised. Not security in data exposure, but only allowing access to the API based on allowed clients, that we whitelist.

2 Answers
2

There is literally no possible way to prevent non-browser access to anything that is also exposed to browsers. As far as a server is concerned, a web browser is just a program that sends and receives HTTP (and related protocols) traffic. That's it. You can do that using curl, you can do that using any of the many sorts of WebRequest object available to different programming frameworks, you can do that by opening a simple TCP socket to the server and manually typing out the bytes to send (see the nc/ncat/netcat program). Any information that a browser normally provides - cookies, the User-Agent header, browser-specific HTTPS and URL quirks, HTTP Authorization, TLS client certificates, JavaScript parsing, etc. - can all be spoofed by some other program. That absolutely includes all the CORS headers and behaviors.

What you can do is require authentication. If the consumer of your web service is authenticated, who cares whether they are using a normal web browser, a screen reader for blind people, a mobile application, PHP web server, a telnet program on a Commodore 64, or tapping out the bits individually very quickly by semaphore?

In theory, cross-origin auth isn't that hard. You have to either send the Access-Control-Allow-Credentials: true CORS response header (which permits HTTP Authorization headers and cookies) or the Access-Control-Allow-Headers: <HEADERNAME> CORS response header (with a custom auth header). In either case, you also need to provide some way for a client to gain a valid authentication token (which could be via a web app, or a web service, or some other means; it could even be another CORS-enabled web service). Of course, you similarly have no way of preventing scripted access to the auth service, so anybody/anything with credentials can get a valid token and then use it.

What is your use case, here? Why are you trying to block scripted access? Why do you even expect this would work? I mean web browsers can run scripts, so there's nothing at all preventing a web browser from loading a page that contains JavaScript to access your web service, retrieve the results, and send them to a desktop or server application.

EDIT:

Ultimately is a REST API that enables CORS intrinsically insecure

Either you don't understand what "secure" means, or you don't understand what web servers do. Web servers parse incoming HTTP requests (HTTP being a text-based protocol typically sent on TCP port 80, or wrapped inside a SSL/TLS stream sent to TCP port 443) and, based on the contents of the HTTP request, send back an HTTP response. Web servers are "inherently insecure" in the sense that, if you expose sensitive information on one, anybody who can connect to it over the network and can send the right string of bytes can get back that data. Of course, that's how approximately every network server for every protocol in the world operates, so...

As a side note, this isn't a CORS problem. If you don't allow CORS at all, it will make absolutely no difference to the type of "attacker" you're envisioning. CORS is a way to open a hole in a critical part of the browser security model (called "same-origin policy"), but it is utterly irrelevant to things that are not browsers.

Yeah Ultimately you have just reiterated what I already knew. Use Case is this: We have a geolocation server (i.e. give it an address, returns the long/lat). This is a server we have installed and maintain. We don't want abuse of this server, and it is really only intended for use by our apps (web and other). The question is how do we ensure (or at least minimise) vetted use
– OJaySep 13 '16 at 22:08

@OJay - "how do we ensure (or at least minimise) vetted use" - authentication+authorization, if you do not want the entire internet to see a page (or a rest api) you need to authenticate the users who can see it. And simply not send it to the users that do not authenticate correctly.
– grochmalSep 13 '16 at 22:17

@OJay: Pretty much can't be done, especially if you do want it to be accessible to a browser. You could embed an authentication token or cryptographic key in an actual app and try some silly obfuscation tricks to make reverse engineering harder, but at the end of the day somebody can work out how to spoof that authentication and use it themselves. In a browser you don't even have that option (I mean, you could do even sillier tricks with obfuscated JS, but JS reverse engineering is child's play).
– CBHackingSep 13 '16 at 22:27

You can try things like restricting the service to certain IP addresses, if your clients are going to be using fixed IPs and nothing at that IP can be used as a proxy...
– CBHackingSep 13 '16 at 22:29

This is pretty similar to CBHacking's answer. I just added some CSRF addo.

I want to only allow requests from Whitelisted clients.

If you are on the web then you are already dead in boots there. The only real way of whitelisting clients is to use:

Public key cryptography to sign the entire OS (and probably hardware too) of the device running the client.

Public key cryptography to establish a secure channel between the client and the server (although HTTPS may be good enough if your CA certificates are part of what you signed above).

Seriously, don't try to do that, it is a fools errand.

Ultimately is a REST API that enables CORS intrinsically insecure.

By the contrary. But CORS is about browser security, it is about the expectations of the user in front of a browser window. In other words if your REST API at http://rest.com uses this:

Access-Control-Allow-Origin: http://example.com

Then a person browsing example.com with a browser may perform a request (containing Origin: http://example.com) which will fulfill the requirement.

But a rogue webpage cannot do it from the browser.

If you do not want the entire internet to see a webpage (or a REST API) you need to authenticate the users who can see it. And simply not send it to the users that do not authenticate correctly.

To prevent spoofed calls you need to defend from CSRF, not access from a certain client. You will perform authentication against the REST API and then send user data only to authorized users.

Now this requires CSRF protection because CSRF is not really covered by CORS (that is not strictly true since the Origin header can be used as a CSRF protection but it is a rather poor protection). Use OWASPs guidelines and produce synchronized CSRF prevention tokens, this will (decently) prevent rogue scripts that can sniff a user's traffic to inject their own requests.

DOH, should have figured the question was probably about CSRF. I mean, it still might not be, but that's probably the minimum-number-of-misunderstandings path between what was asked and what the asker is actually worried about.
– CBHackingSep 13 '16 at 22:20

@CBHacking - Heh, true. But to be fair with OP, I actually find this misunderstanding quite often when consulting. i.e. Q: How do I secure an unauthenticated REST API? A: You don't, it's unauthenticated.
– grochmalSep 13 '16 at 22:32

@grochmal I think that one line just reinforced it nicely for me. Thank you. TLDR; Make it authenticated!
– OJaySep 13 '16 at 22:38

I suppose what I was trying to highlight was that CORS has this concept of Allowed-Domains (i.e. Access-Control-Allow-Origin) which on the initial look appear to be a mechanism for white listing requests. I was wrong
– OJaySep 13 '16 at 22:49