I've recently started using modern front end technologies like React/Angular and as a result have started using tools like JSON Server to recreate dummy restful db interactions.
My understanding is that most rest api's authenticate via some kind of token and secret that is either passed as part of the url or as a header. This seems fine for retrieving data, but is it not risky exposing these login credentials in a front end language like JS when writing is possible?
My thinking is that all it would take is a simple view source for somebody to steal my token/secret and potentially start populating my db with data.
In the problem that you describe the client (browser) has the login credentials because the server provide them. There is no "exposing" as the credentials are already exposed. Exposing your credentials to every client means that there is no security.
When we talk about security we consider as a client the browser not the real person that operates the browser. As you said, the real person can access all the browser's data.
To secure your API the secret key must be kept secret. This means that each client has a different key and uses it to get their data/services from your RESTfull server.
In a simple senario this key can be used/managed like the session id.
The client should first pass through an authorization process (login maybe) and then a temporary key can be generated for the client's session.
Generally, a key is converted to rights. If every client by default has the key, everyone has the default rights, so you may also remove the key and set the default rights to every request.
A client that you don't want to have full access to your db should have a key that gives him limited access to your db.
On the other hand, if the client provides the key, this is secure. For example a php code on a server that uses the secret key for accessing your API.
I've spent a couple weeks trying to wrap my head around JWT objects. The premise makes sense but where I get confused is the security aspect. If I am a Javascript Client (e.g. Firebase) and want to send a secure request to an api using Open Auth, I would encrypt my message with a key. However, since the client source may be viewed how can I secure my Key so malicious requests don't go through. Am I missing something. Is there a way to secure the key?
Joel, I think you got the directions wrong ;)
One would use JWT within the OAuth protocol to achieve what some people might call "Stateless Authentication", meaning that the auth server would issue a signed token (for e.g. a client application or a user) after successful authentication (of the client or user) without storing info about/ of it, which would be required when using opaque token.
The signed token could be used by your JS client to e.g. call a certain REST-API endpoint (on a so-called resource server) that would verify the signature of the token and authorize your request or not, based on the content (the claims) of the JWT.
Both, your client application as well as the resource server are able to introspect the token and verify its signature because they either have a shared secret with the auth server (who used the secret to sign the token in the first place) or know the public key that corresponds to the private key the auth server used to sign the token (as Florent mentioned in his comment).
JWTs can also be encrypted, which is useful if the resource server or the auth server require sensitive information but don't want to store/ access the data. You would not be able to introspect it as long as you don't have the used encryption secret.
... long story short, the OAuth protocol describes client auth against a resource or an auth server. JWT can be used to transfer auth prove (as a Bearer token within the Authorization header). However, the idea of using JWT in the OAuth flow is not to "send a secure request to an api".
The encryption process is performed using the public key of the recipient.
Your client has no private key to generate and manage.
If you want to receive and decrypt such JWT, then your client has to create a key pair (private and public) for the session only and then exchange the public key with the server.
When building an api server, I prefer the client do the encryption process on their own server, and send the encrypted data after that. Everything is under https.
If the encryption somehow must be done on the web client side, I prefer the key to be very short-lived & time based, and both the api server and client have the agreed special algorithm to generate that key again. Therefore, if the key is hacked somehow, the attacker can not benefit in long term.
I've been reading up on REST and there are a lot of questions on SO about it, as well as on a lot of other sites and blogs. Though I've never seen this specific question asked...for some reason, I can't wrap my mind around this concept...
If I'm building a RESTful API, and I want to secure it, one of the methods I've seen is to use a security token. When I've used other APIs, there's been a token and a shared secret...makes sense. What I don't understand is, requests to a rest service operation are being made through javascript (XHR/Ajax), what is to prevent someone from sniffing that out with something simple like FireBug (or "view source" in the browser) and copying the API key, and then impersonating that person using the key and secret?
We're exposing an API that partners can only use on domains that they have registered with us. Its content is partly public (but preferably only to be shown on the domains we know), but is mostly private to our users. So:
To determine what is shown, our user must be logged in with us, but this is handled separately.
To determine where the data is shown, a public API key is used to limit access to domains we know, and above all to ensure the private user data is not vulnerable to CSRF.
This API key is indeed visible to anyone, we do not authenticate our partner in any other way, and we don't need REFERER. Still, it is secure:
When our get-csrf-token.js?apiKey=abc123 is requested:
Look up the key abc123 in the database and get a list of valid domains for that key.
Look for the CSRF validation cookie. If it does not exist, generate a secure random value and put it in a HTTP-only session cookie. If the cookie did exist, get the existing random value.
Create a CSRF token from the API key and the random value from the cookie, and sign it. (Rather than keeping a list of tokens on the server, we're signing the values. Both values will be readable in the signed token, that's fine.)
Set the response to not be cached, add the cookie, and return a script like:
var apiConfig = apiConfig || {};
if(document.domain === 'example.com'
|| document.domain === 'www.example.com') {
apiConfig.csrfToken = 'API key, random value, signature';
// Invoke a callback if the partner wants us to
if(typeof apiConfig.fnInit !== 'undefined') {
apiConfig.fnInit();
}
} else {
alert('This site is not authorised for this API key.');
}
Notes:
The above does not prevent a server side script from faking a request, but only ensures that the domain matches if requested by a browser.
The same origin policy for JavaScript ensures that a browser cannot use XHR (Ajax) to load and then inspect the JavaScript source. Instead, a regular browser can only load it using <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123"> (or a dynamic equivalent), and will then run the code. Of course, your server should not support Cross-Origin Resource Sharing nor JSONP for the generated JavaScript.
A browser script can change the value of document.domain before loading the above script. But the same origin policy only allows for shortening the domain by removing prefixes, like rewriting subdomain.example.com to just example.com, or myblog.wordpress.com to wordpress.com, or in some browsers even bbc.co.uk to co.uk.
If the JavaScript file is fetched using some server side script then the server will also get the cookie. However, a third party server cannot make a user’s browser associate that cookie to our domain. Hence, a CSRF token and validation cookie that have been fetched using a server side script, can only be used by subsequent server side calls, not in a browser. However, such server side calls will never include the user cookie, and hence can only fetch public data. This is the same data a server side script could scrape from the partner's website directly.
When a user logs in, set some user cookie in whatever way you like. (The user might already have logged in before the JavaScript was requested.)
All subsequent API requests to the server (including GET and JSONP requests) must include the CSRF token, the CSRF validation cookie, and (if logged on) the user cookie. The server can now determine if the request is to be trusted:
The presence of a valid CSRF token ensures the JavaScript was loaded from the expected domain, if loaded by a browser.
The presence of the CSRF token without the validation cookie indicates forgery.
The presence of both the CSRF token and the CSRF validation cookie does not ensure anything: this could either be a forged server side request, or a valid request from a browser. (It could not be a request from a browser made from an unsupported domain.)
The presence of the user cookie ensures the user is logged on, but does not ensure the user is a member of the given partner, nor that the user is viewing the correct website.
The presence of the user cookie without the CSRF validation cookie indicates forgery.
The presence of the user cookie ensures the current request is made through a browser. (Assuming a user would not enter their credentials on an unknown website, and assuming we don’t care about users using their own credentials to make some server side request.) If we also have the CSRF validation cookie, then that CSRF validation cookie was also received using a browser. Next, if we also have a CSRF token with a valid signature, and the random number in the CSRF validation cookie matches the one in that CSRF token, then the JavaScript for that token was also received during that very same earlier request during which the CSRF cookie was set, hence also using a browser. This then also implies the above JavaScript code was executed before the token was set, and that at that time the domain was valid for the given API key.
So: the server can now safely use the API key from the signed token.
If at any point the server does not trust the request, then a 403 Forbidden is returned. The widget can respond to that by showing a warning to the user.
It's not required to sign the CSRF validation cookie, as we're comparing it to the signed CSRF token. Not signing the cookie makes each HTTP request shorter, and the server validation a bit faster.
The generated CSRF token is valid indefinitely, but only in combination with the validation cookie, so effectively until the browser is closed.
We could limit the lifetime of the token's signature. We could delete the CSRF validation cookie when the user logs out, to meet the OWASP recommendation. And to not share the per-user random number between multiple partners, one could add the API key to the cookie name. But even then one cannot easily refresh the CSRF validation cookie when a new token is requested, as users might be browsing the same site in multiple windows, sharing a single cookie (which, when refreshing, would be updated in all windows, after which the JavaScript token in the other windows would no longer match that single cookie).
For those who use OAuth, see also OAuth and Client-Side Widgets, from which I got the JavaScript idea. For server side use of the API, in which we cannot rely on the JavaScript code to limit the domain, we're using secret keys instead of the public API keys.
api secret is not passed explicitly, secret is used to generate a sign of current request, at the server side, the server generate the sign following the same process, if the two sign matches, then the request is authenticated successfully -- so only the sign is passed through the request, not the secret.
This question has an accepted answer but just to clarify, shared secret authentication works like this:
Client has public key, this can be shared with anyone, doesn't
matter, so you can embed it in javascript. This is used to identify the user on the server.
Server has secret key and this secret MUST be protected. Therefore,
shared key authentication requires that you can protect your secret
key. So a public javascript client that connects directly to another
service is not possible because you need a server middleman to
protect the secret.
Server signs request using some algorithm that includes the secret
key (the secret key is sort of like a salt) and preferably a timestamp then sends the request to the service. The timestamp is to prevent "replay" attacks. A signature of a request is only valid for around n seconds. You can check that on the server by getting the timestamp header that should contain the value of the timestamp that was included in the signature. If that timestamp is expired, the request fails.
The service gets the request which contains not only the signature
but also all the fields that were signed in plain text.
The service then signs the request in the same way using the shared
secret key and compares the signatures.
I will try to answer the the question in it's original context. So question is "Is the secret (API) key safe to be placed with in JavaScript.
In my opinion it is very unsafe as it defeats the purpose of authentication between the systems. Since the key will be exposed to the user, user may retrieve information he/she is not authorized to. Because in a typical rest communication authentication is only based on the API Key.
A solution in my opinion is that the JavaScript call essentially pass the request to an internal server component who is responsible from making a rest call. The internal server component let's say a Servlet will read the API key from a secured source such as permission based file system, insert into the HTTP header and make the external rest call.
I hope this helps.
I supose you mean session key not API key. That problem is inherited from the http protocol and known as Session hijacking. The normal "workaround" is, as on any web site, to change to https.
To run the REST service secure you must enable https, and probably client authentification. But after all, this is beyond the REST idea. REST never talks about security.
What you want to do on the server side is generate an expiring session id that is sent back to the client on login or signup.
The client can then use that session id as a shared secret to sign subsequent requests.
The session id is only passed once and this MUST be over SSL.
See example here
Use a nonce and timestamp when signing the request to prevent session hijacking.
I'm currently working on a mobile app that is built primarily with HTML and css, then run with phonegap. Part of the app requires that the user logs in and sync data with a backend. I want it done in such a way that once the user is logged in on their device they will remain logged in until they manually log out; they should only have to enter their information once. I've found some decent information when working with native code, but not so much that applies to my situation.
Right now, I'm not sure how to properly do this in a way that is secure. My first idea was to handle the login normally, then pass back a secret code that is stored both in local storage on the device, as well as in the database under the user that was authenticated with it. On subsequent requests it would pass this and allow access to the user that had a matching secret code. My question is whether this is secure enough to be practical?
I've also done a bit of research and it appears JWT is similar to what I'm looking for? My two concerns for this approach are:
How do I maintain the persistent login with this method.
What prevents someone from spoofing a connection? From what I know there's a secret string that is passed with each request to verify it. But, since the code in my app would be visible to someone who knew how to access it what is to prevent them from learning what the secret is and spoofing a connection?
I assume my questions with JWT are due to me misunderstanding some fundamentals of how it works. If it satisfies what I'm looking to do I'd much rather use a more standardized process like JWT as opposed to writing my own solution.
Am I on the right track with what I'm thinking above or am I way off base?
Your idea is correct and this is how it's usually done. It is a combination of cookie and session concepts. A 'session' is started on the server once the user logs in. The session is identified by a string (e.g. md5 format) and passed back to the client. The string is saved in a cookie on the client, and since cookie info is sent in each HTTP request, the server can assign that request to the session, thus considering your user as logged in. The signout process later basically consists of removing your cookie, and / or sending a request to the server to remove the session object.
In most HTTP server side frameworks there is an API used for sessions, so you don't have to reinvent the wheel. And yes, it is secure enough, as you don't usually base your security on this layer of transport, but rather on a lower layer by introducing https.
I'm now two weeks into learning and building an AngularJS+ PHPsystem and I'm still struggling with authentication. I've been reading a lot of posts about AngularJSand not one of them seem to consider the security aspect of authentication. I also had an interesting response when I asked about the security of AngularJS storages on another post, and got two great links to Stormpath's blogs which cover areas of security when dealing with tokens.
Most tutorials and examples about AngularJS seem to be taking a JWT approach and sending that token to your REST API via HTTP headers, but given that the token is stored in Javascript this can expose it to multiple attack types. One of them being MITM. To be secure against this type of attack the solution is to set a cookie with HttpOnly and Secure flags. Now the token gets passed on every request, it's not being stored by Javascript and it's secure. However, this raises the question at the point where you authenticate the user: How is this any different than using sessions when you're only dealing with HTTP requests originating from the same server?
When checking if a user has already logged in we usually check if a $_SESSION variable exists, let's say uid. Now on a token based approach we send the token in HTTP headers and read that token, then validate it and get user information. In AngularJSwe then get the successful response and return a promise.
Sessions have the advantage of being handled by the server. They create a session and they handle it's destruction automatically if it still lingers there. When dealing with a token based authentication you have to take care of it's expiration, refreshing and destruction with a scheduled script if the user has not destroyed it himself. This seems like too much work.
The idea of using tokens is to allow for a server to be completely stateless. The server just provides a login service, that upon successful login returns a temporary token, and it immediately forgets the token, it does not store it anywhere (database, memory).
Then the client sends the token at each subsequent request. The token has the property that it's self-validating: it includes the validity, the username and a cryptographic signature.
Such signature proves that the token is valid to the server, even if the server had thrown away the token completely.
This way the server does not have to take care of expiration/destruction of tokens: it can inspect incoming tokens and validate them inspecting only the token (thanks to the signature).
And this is the advantage of JSON Web Tokens: they allow for a completely stateless server that does not have to manage authentication token lifecycle.