I quick question!
I have a webapp that only fetches data from an API so i dont have i backend part. My question is where do you keep your API key? Accordingly to React docs you should not store API keys in the app so how do you manage this when you have an webapp that consumes an API and you have to use the API key in your get requests?
Let's do a bit of explanation, so you connect the dots and design it more robustly. There are two (three) places where to store it in the end:
frontend (your React application)
backend (your server)
third-party service
TL;DR: use non-frontend solution + rate limiting to a registered user and have the registration step secured properly (+ captcha).
Frontend
Storing anything on the frontend side is mostly a bad idea, unless you're completely sure you can allow such data to be exposed - constants, names, icons, maybe some URLs so you don't have it hardcoded in the JS files.
Your "compiled" ReactJS (or any other framework) when built is just a slightly mangled (minified/transpiled/etc etc) JavaScript, but for that to work the client has to retrieve it and have it executed in the browser. Therefore before the ReactJS application even starts, there are 1+ requests downloading the JavaScript code and other parts of the application depending on the framework.
Those you can see in the network monitoring tab in any modern browser or simply use Wireshark (unless encrypted, then it's a little bit annoying) or a local proxy if using a less sane browser.
After retrieval you can simply try Ctrl+F or any online deminifier/deobfuscator if you don't know how to do it yourself and you can retrieve the key.
Implications when retrieved
I can impersonate you for the service that issues the API key
I can lock your key/account by calling too often (just for fun or to retrieve some info)
I can use your web for scraping while not needing to pay for the API key (if paid) or to register to such service vendor
If it's per-request API key and there's some limitation that would make it cost you money, I can just run some silly while (true) { callYourApi() } via a service to make me anonymous just to make it cost you
Depending on the API key and how serious you intend to approach this problem, you might utilize the .env file for development purposes only. Though you should never ever store an API key in the frontend unless you explicitly have to store it in there (e.g. maps) because it's mostly a very stupid idea and allows anyone to misuse it.
Backend
Your server, if properly configured and secured, will store the key anywhere which isn't accessible by simply path traversing (if in a file) or scraping (if you attempt to retrieve the key to execute on the frontend part).
Therefore the most sane and secure way would be to retrieve the data (of any service) by having either a custom API or a scheduled script collecting the data, which when your frontend gets called will be able to retrieve as pre-rendered or already fetched, thus no key needed for that case.
However! There's a trick to that. If you design your custom API as /api/<key>=123 or /api/<param> and you use that parameter for the original API to filter on frontend, the attacker couldn't care less for the API key because you've already created an API for free and made it public and unsecure.
So GET /yourapi/<my data> and API key for free without even needing to have one displayed.
How to do it safely? Two simple approaches:
pre-rendering data to HTML
You then fetch with frontend and just display - but this one can be scraped, it's just a bit annoying if more complex, but that's it. Server-side rendering sounds nice, but doesn't really work for this case. It should be mostly used to make the frontend fast or to template the views, but never for security purposes as the silver bullet solution (because it doesn't work that way).
rate limiting + CORS + account management
with rate limiting you make sure that a user (preferably you have that API called only after a user is logged in) can call that API only e.g. 10 times within 1 hour and with CORS you make sure it's callable only by your frontend.
It's not a silver bullet either, anybody with a little bit of brain can simply scrape your API locally thus go around CORS, but the rate limit will still hit hard, if you forbid registering more than 1 user from a single IP or if you require a phone number for verification. And add some annoying captcha, so it's problematic to automate for some people.
Still it can be attacked and misused, but it's painful unless you allow the same phone number (or any other ID less comfortable to get / requiring effort to get) to be used multiple times, so it'll make the most incompetent people go away... and the remaining ones, well, they'd play with your website anyway, so have a proper security assessment / harden your server if you maintain it alone.
Third-party
It's like 2., but you don't maintain the "low-level" server part, because the third-party is then managing it for you, you just need to specify conditions under which it'll be called. This applies to Firebase or Supabase which kind of behaves like a separate backend, but can have multiple modules (for FB, 1, 2).
Thus you'd use Firebase functions (or other alternatives), where you'd have your key e.g. even hardcoded and the client (browser) wouldn't have any access to that, add a limit, cors, perhaps some user registration limit and you're kind of done.
Note: Any domain, IP, region, phone number restrictions can be bypassed, so do not rely on them. It's just a mean to require effort when misusing your website for something different than you intended.
domain: curl http(s)://yourweb/path -H "Host: spoofed-domain"
region or IP: proxy, VPN, Tor, I2P, just somebody else's computer/server + ssh, some random WiFi
phone number: can go to a local shop and buy 10 fresh ones if I wanted to
It's more of a recommendation for them to keep your API keys server-side, and let your web app communicate with your server. Otherwise malicious users could easily steal your API key and use it for whatever.
If you think it isn't much of a security risk if your key gets (scratch that, is) compromised, that's fine then, you can just keep it in your webapp. Really depends on your use case and what that API key is for.
The only way to do this without exposing your API keys in your client app is to create a backend and serve the client app from the backend app as stated by Kelvin Schoofs and Peter Badida answers above (or use a third party service such as AWS Credential Vault). I suggest you use Node Express library for a backend as this will handle a lot of the boiler plate code for you. There are plenty of tutorials online for this.
Using a dotenv file as suggested by a few other users will only hide your API code from version sharing tools like Git (because you can ignore the dotenv in gitignore). It is very important that you understand the process of dotenv with a react app. Any user who opens the Dev console in their browser can view your exposed API keys in the static HTML.
Create a dotenv file and store all secret and API keys. Make sure to use REACT_APP_ before every variable.
DOCS: https://create-react-app.dev/docs/adding-custom-environment-variables/
dotenv package: https://www.npmjs.com/package/dotenv
Related
So I've got a web API for my (javascript) web app. The app is wrapped in Cordova and distributed on iOS and Android.
I would prefer to keep the data access limited (keep third parties from using my API). But how would I do this? I do not require (nor want) the user to create an account so the app itself must somehow verify that its the app sending the request, and not someone else.
But how?
I would prefer to keep the data access limited (keep third parties from using my API). But how would I do this?
...
the app itself must somehow verify that its the app sending the request, and not someone else.
...
But how?
What you're asking for is not possible. It's also the wrong problem to solve.
Further reading: Client Authenticity is not the Server's Problem
You can't be 100% sure but you can make the reverse engineers work for it.
use tls ao they can't easilly watch your protocol with a sniffer
dont just use a single api key, make the key a function of a variety of values spread through the app along with actual user data
consider using the tls key sent by the server as part of the api key function so everything myateriously breaks if they mitm the tls
use meaningless field names in the api
push out updates frequently that change the api system
I have a JavaScript application.
It's built with jQuery.
It uses $.get() to pull JSON data from a server, and uses the data to load a puzzle.
I want to distribute the JavaScript application to clients, and make it easy for them to install.
I'd like it to simply give them a JavaScript block they can drop into their page, and it will interact with my API.
I'm not passing sensitive data, any my API is protecting the database from SQL injection, etc.
I just want to try to prevent unauthorized use of my API, and I can't think of a way to do that with JavaScript, since anyone with a DOM inspector can scrape any credentials from any variables or can monitor any server traffic POST or GET data...
Would it be possible to authenticate the referrer on the other side?
I know that's not bulletproof, but it's not sensitive data. I just want to reduce the unauthorized use as much as possible..
Any ideas?
note: I know obfuscating an API key or something is futile, I'm wondering what other controls I could put in place other than a traditional key to identify the caller to the API.. I have full control over the API itself so I could do anything on that side of things...
JavaScript authentication has holes
With JavaScript, just about any authentication system is going to have holes, simply because the code runs directly in the browser and can be seen by anyone (as can the network calls). So there are a couple of things you can try, depending on your situation.
IP whitelisting
If you are distributing this application to a small subset of clients, and you know exactly where they will be accessing it from, you could use IP whitelisting. This really is the only way to completely secure the API. However this method is very cumbersome, since with every new client you have to update the API whitelist, and considering what you're talking about here probably not what you're looking for (but I mention it just because it is a possibility).
Access tokens
Another method is access tokens. This is a common method used by sites such as Facebook. There are two methods to do this. One is to just give each client a secret key. You can have the same secret key for everyone, but this is not very secure. Having a different secret key for everyone allows you to not only track usage, but also revoke access privs if necessary.
The first method for access tokens is to just give it inside the JS client. However this means that anyone who looks at the source will be able to access your key, and make requests using it.
The second method is to have the secret key stored somewhere on the SERVER of the website where your client runs. This server can then make a server-to-server call using that key to obtain a temporary session token. People will still be able to access the temporary session token via the front-end, but they will have to access this site first in order to get it (this allows you to pass off responsibility for handling this to the website operator) and the token will eventually expire. However this means there needs to be some server-side code, and the app won't just be a drag and drop thing.
For the method given above you can also look into things like OAuth, to avoid re-inventing the wheel.
Whitelist hard-cap
Another possible thing using IPs is to set a hard-cap on either how often or how much per day a specific IP can hit the whitelist. Though you may run into problems with users who REALLY like the puzzles, this will prevent some of the potential abuse.
I am designing a new service that would enable 'customers' to register and pay a per-use type fee for particular searches they perform. This service would be exposed using a RESTFul and SOAP interface. Typically the web service would integrate with the customer's website and then be exposed to the 'public' where anyone would be able to use the customer's website and take advantage of my web service features (which the customer would pay for but have full control of moderating the requests so they don't get charged too much).
I want to design the service that optimises the integration to make it as simple as possible. The web service API will change so creating an internal proxy to expose the web service to the public in some cases is too much of a detractor for customers. So the issue as I see it is creating a web service that balances authentication, security and integration.
Ideal
Not use OAuth
Avoid forcing the customer to create an internal proxy which re-exposes the same web service API I have already.
Be secure (token username/pass whatever and ssl)
Embed a javascript library in customer website - This would be a client Javascript library to make integration steps even easier.
The Javascript library would need to be secure enough so that the public wouldn't be able to simply grab credentials and re-purpose it themselves
Not be too hacky, if possible, so the web service doesn't have to be re-built if Firefox 87 comes out (to be released in as many minutes) and decides to fubar it.
It seems that some kinda of 3-way authentication process is needed for this to work, i.e. authenticates a particular client (in the public), the web service (the customer) and the web service.
Has anyone implemented something kind of similar and how did they tackle a situation like this?
I also understand there is a balance between what can be done, and what would violate cross-domain security, so perhaps the entire web service might be exposed by another GET only interface which would return JSONP data.
/** Addendum **/
I have since discovered a web service that does what I'm looking after. However, I am not confident I understand the implementation details entirely. So perhaps someone could also elaborate on my thinking.
The web service I discovered seems to host the Javascript on the service side. The customer would then integrate their website with the service side by including the Javascript in a script tag, but supplies a key to do so i.e.
Somehow if I add the script to my website it doesn't work. So somewhere along the line the token must be registered to a particular customer domain, and the 'client-lib.js' is actually a servlet or something similar which can somehow detect that the user from the 'public' coming in has actually originated from the 'customer' domain.
Is my thinking right? Is there some kind of http header that can be used this way? Is that safe?
Cheers
First of all - let me provide you a link to another SO question which I answered yesterday - as it gives a pretty extensive answer to a similar question-set.
I am assuming that you are going to charge the owner of the site from which the search is made, and not care so much who the individual user is who makes the search. If that's incorrect, please clarify and I will update my answer accordingly.
Clearly, in any such case, the first and foremost thing you need to do is to make sure you know which client this is on each request. And - as you said, you also want to make sure you're protecting yourself from cross-site attacks and people stealing your user's keys.
What you might consider would be the following:
Create a private key on your side - which only your service knows.
Whenever a new consumer site creates an account with you, create a new shared key which only you and they will know. I suggest creating this key by using your private key as a password, and encrypting some kind of identifier which will let you identify this particular user.
As part of your registration process, make the consumer site tell you what URI they will be using your scripts on.
Now - the way that you both do your tracking and authentication becomes fairly simple.
You mentioned providing a JS library which won't need to update every time FF updates. I suggest building that library using jQuery, or another, similarly supported cross-browser JS foundational library - and letting that wrap your AJAX.
When the client site requests your script, however, have them provide you something like:
http://www.yourdomain.com/scripts/library.js?key={shared key}
On your side, when you receive this request, check the following:
When you decrypt their shared key using your private key, you should not get gibberish. If you do - it's because their key has been altered in some way - and is not valid. This should result in a 401: Unauthorized error.
Once you decrypt the key and know which client site this is (because that's what the key contains) - check to make sure that the request is coming from the same URI that client registered with. This now protects you from someone stealing their key and injecting it into a different website.
As long as the above matches, let them download the file.
Now - when you serve the JS file, you can do so in a way that injects the key into that file - and therefore it has access to their shared key. On each of your AJAX requests, include that key so that you can identify which client this request is coming from again. In a RESTful environment, there shouldn't really be sessions - so you need this level of authentication on each post. I suggest including it as a cookie.
On your server-side - simply repeat the checks of their key on each subsequent request - and voila - you've built yourself some fairly tight security without a lot of overhead.
That said - if you expect a lot of traffic - you may want to come back to this and explore more deep security processes in the future, as rolling your own security matrix can leave unexpected holes. However - it is a good start and will get you off the ground.
Feel free to ask any questions if you need, and I will try to update my answer accordingly.
The best way to go about it is something like this (taking that you want to use javascript hosted on your server and make the include part as simple as it can be):
*user registers on your website and he receives a token for his domain
*the user can include a js file pointing to your server
the js file will be something like:
<script type="text/javascript" src="http://your.server.com/js.php?token=###&widget=xxx"></script>
or
<script type="text/javascript" src="http://your.server.com/js.js?token=###&widget=xxx"></script>
if you will use a .htaccess to redirect
*in the php file check if the token matches the requests domain, if yes echo out the js lib, if not throw a error or something
*in the js you will need to build some ajax calls to your service and stuff to manipulate the HTML (create a widget holder,show some data, etc.)
*also all the calls should have the token, and again you can use the same logic to check if token==server address
EDIT:
The REFERER is sent by the client's browser as part of the HTTP protocol, and is therefore unreliable indeed.
If you want to verify if a request is coming from your site, well you can't, but you can verify the user has been to your site and/or is authenticated. Cookies are sent in AJAX requests so you can rely on that. But this means you need to use something like oAuth
If you want to use this method, you should still check the referrer as well to prevent CSRF en.wikipedia.org/wiki/Cross-site_request_forgery
Ideally you should use a unique token per session per user (per request if you're paranoid) to prevent CSRF attacks. Checking the referrer is just security by obfuscation and not quite a real solution.
This is probably a generic security question, but I thought I'd ask in the realm of what I'm developing.
The scenario is: A web service (WCF Web Api) that uses an API Key to validate and tell me who the user is, and a mix of jQuery and application on the front ends.
On the one hand, the traffic can be https so it cannot be inspected, but if I use the same key per user (say a guid), and I am using it in both then there's the chance it could be taken and someone could impersonate the user.
If I implement something akin to OAuth, then a user and a per-app key is generated, and that could work - but still for the jQuery side I would need the app API key in the javascript.
This would only be a problem if someone was on the actual computer and did a view-source.
What should I do?
md5 or encrypt the key somehow?
Put the key in a session variable, then when using ajax retrieve it?
Get over it, it's not that big a deal/problem.
I'm sure it's probably a common problem - so any pointers would be welcome.
To make this clearer - this is my API I have written that I am querying against, not a google, etc. So I can do per session tokens, etc, I'm just trying to work out the best way to secure the client side tokens/keys that I would use.
I'm being a bit overly cautious here, but just using this to learn.
(I suggest tagging this post "security".)
First, you should be clear about what you're protecting against. Can you trust the client at all? A crafty user could stick a Greasemonkey script on your page and call exactly the code that your UI calls to send requests. Hiding everything in a Javascript closure only means you need a debugger; it doesn't make an attack impossible. Firebug can trace HTTPS requests. Also consider a compromised client: is there a keylogger installed? Is the entire system secretly running virtualized so that an attacker can inspect any part of memory at any time at their leisure? Security when you're as exposed as a webapp is is really tricky.
Nonetheless, here are a few things for you to consider:
Consider not actually using keys but rather HMAC hashes of, e.g., a token you give immediately upon authentication.
DOM storage can be a bit harder to poke at than cookies.
Have a look at Google's implementation of OAuth 2 for an example security model. Basically you use tokens that are only valid for a limited time (and perhaps for a single IP address). That way even if the token is intercepted or cloned, it's only valid for a short length of time. Of course you need to be careful about what you do when the token runs out; could an attacker just do the same thing your code does and get a new valid token?
Don't neglect server-side security: even if your client should have checked before submitting the request, check again on the server if the user actually has permission to do what they're asking. In fact, this advice may obviate most of the above.
It depends on how the API key is used. API keys like that provided by Google are tied to the URL of the site originating the request; if you try and use the key on a site with an alternate URL then the service throws and error thus removing the need to protect the key on the client side.
Some basic API's however are tied to a client and can be used across multiple domains, so in this instance I have previously gone with the practice of wrapping this API in server side code and placing some restrictions on how the client can communicate with the local service and protecting the service.
My overall recommendation however would be to apply restrictions on the Web API around how keys can be used and thus removes the complications and necessity of trying to protect them on the client.
How about using jQuery to call server side code that handles communication with the API. If you are using MVC you can call a controller action that can contain the code and API key to hit your service and return a partial view (or even JSON) to your UX. If you are using web forms you could create an aspx page that will do the API communication in the code behind and then write content to the response stream for your UX to consume. Then your UX code can just contain some $.post() or $.load() calls to your server side code and both your API key and endpoint would be protected.
Generally in cases like this though you proxy requests through the server using 'AJAX' which verifies the browser making requests is authorized to do so. If you want to call the service directly from JavaScript, then you need some kind of token system like JSON Web Tokens (JWT) and you'll have to work out cross-domain issues if the service is located somewhere other than the current domain.
see http://blogs.msdn.com/b/rjacobs/archive/2010/06/14/how-to-do-api-key-verification-for-rest-services-in-net-4.aspx for more information
(How to do API Key Verification for REST Services in .NET 4)
Does anybody know of a way of checking on the API side if a XMLHttpRequest has been made from my own web-application (ie. from the JS I have written) or from a third-party application...
The problem, to me, seems to be that because the JS is run on the client and thus accessible to anyone I have no way of secretly communicating to the API server who I am. I think this is useful because otherwise I cannot prioritize requests from my own application over third-party clients in case of high usage.
I could obviously send some non-documented parameters but these can be spoofed.
Anybody with some ideas?
I would have your web server application generate a token that it would pass to your clients either in JavaScript or a hidden field which they in turn would use to call your API. Those with valid tokens get priority, missing or invalid tokes wouldn't. The web server application can create & register the token in your system in a way that limits its usefulness to others trying to reuse it (e.g., time limited).
If you do approve of third party clients accessing your API, perhaps you could provide them with a slightly different, rate-limited interface and document it well (so that it would be easier to use and thus actually be used by third-party clients).
One way to do this would be to have two different API URLs, for example:
/api?client=ThirdPartyAppName&... for third-party apps (you would encourage use of this URL)
/api?token=<number generated from hidden fields from the HTML page using obfuscated code>&... for your own JS
Note that as you mention, it is not possible to put a complete stop to reverse engineering of your own code. Although it can take longer, even compiled, binary code written in such languages as C++ can be reverse engineered, and that threatens any approach relying on secrecy.
A couple of ideas come to mind. I understand that secrets never last, so I agree that's not a good option.
You could run another instance on a different unadvertised port
You could do it over SSL and use certs to identify the client
A simple but less secure way would be to use cookies
You could go by IP address, but that could be an administrative nightmare