23

I have an app that has a REST api. I want it so that the only requests that can be made to the REST api are ones originating from the app itself. How can I do that? I am using a node.js+express server too.

EDIT: the app is fully a public web app.

omega
  • 40,311
  • 81
  • 251
  • 474
  • 2
    Your app is a web app, right (not mobile)? Anyway, there's no way you can ensure 100% it (a user can easily simulate your requests outside your app). CORS protection is a technique to improve this kind of security inside the browser, but nothing prevents the user to user override the `Origin` header using a tool like `curl`, for instance. Indeed, CORS protection goal is more to protect the user, not the server. For API/resource protection, you should provide something like token authentication (oauth maybe), SSL. – mrlew Jan 11 '17 at 17:05
  • 1
    How does oauth work, couldn't someone just take the client key from the app, and make their own requests with that? – omega Jan 11 '17 at 17:14
  • I was thinking checking the ip and domain where the request came from, check if its the same domain where the app is hosted. Would that be enough? – omega Jan 11 '17 at 17:15
  • 2
    in Ajax requests, the ip will be the user IP. That's the problem. – mrlew Jan 11 '17 at 17:16

3 Answers3

8

Simply define the header in your request, what this does is, it allows requests only from a certain domain, and instantly rejects any other domain.

response.set('Access-Control-Allow-Origin', 'domain.tld');

EDIT: IF you're really keen against web scraping stuff, you could make a function to double check client's origin.

function checkOrigin (origin) {
   if (origin === "your.domain.tld") {
     return true;
   } else {
     return false;
   }
}
/* Handling it in response */
if (checkOrigin(response.headers.origin)) {
  // Let client get the thing from API
} else {
  response.write("Send them error that they're not allowed to use the API");
  response.end();
}

Above example should work for the default HTTP/HTTPS module, and should also work for Express, if I'm not mistaken.

EDIT 2: To back my claim up that it should also work for Express, I found this quotation at their documentation;

The req (request) and res (response) are the exact same objects that Node provides, so you can invoke req.pipe(), req.on('data', callback), and anything else you would do without Express involved.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Cernodile
  • 139
  • 7
2

I would recommend using an API key from the client. CORS filters are too easy to circumvent.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
R. Gulbrandsen
  • 3,648
  • 1
  • 22
  • 35
  • 1
    How does the client even get the api key, as the whole thing is a website. So the website is public. – omega Jan 11 '17 at 16:56
  • 1
    It's quite normal approach to the problem. If you intend to use most of the public APIs then your client has to have an API key to access it. This is to restrict the number of requests as well as which applications that can access it. APIs like google maps, steam and flickr all uses it :) – R. Gulbrandsen Jan 11 '17 at 17:13
  • 14
    But then couldn't someone just take that public client api key and use it on their own requests? – omega Jan 11 '17 at 17:16
  • @omega could, but at that point you likely have rate monitoring or some stats to check. If you see suspicious activity, warn the user, rate limit, or revoke access using that key. – ron Jan 29 '19 at 13:16
  • So I have heard. It is hackable in many ways. – Sohan Arafat Aug 02 '22 at 01:51
-1

A simple approach for securing a How to implement a secure REST API with node.js

Overview from above post:

Because users can CREATE resources (aka POST/PUT actions) you need to secure your api. You can use oauth or you can build your own solution but keep in mind that all the solutions can be broken if the password it's really easy to discover. The basic idea is to authenticate users using the username, password and a token, aka the apitoken. This apitoken can be generated using node-uuid and the password can be hashed using pbkdf2

Then, you need to save the session somewhere. If you save it in memory in a plain object, if you kill the server and reboot it again the session will be destroyed. Also, this is not scalable. If you use haproxy to load balance between machines or if you simply use workers, this session state will be stored in a single process so if the same user is redirected to another process/machine it will need to authenticate again. Therefore you need to store the session in a common place. This is typically done using redis.

When the user is authenticated (username+password+apitoken) generate another token for the session, aka accesstoken. Again, with node-uuid. Send to the user the accesstoken and the userid. The userid (key) and the accesstoken (value) are stored in redis with and expire time, e.g. 1h.

Now, every time the user does any operation using the rest api it will need to send the userid and the accesstoken.

Community
  • 1
  • 1
Cole Roberts
  • 954
  • 3
  • 15
  • 25
  • 4
    Is there a way to do this where the user doesn't have to login with username/password. – omega Jan 11 '17 at 18:25