REST authentication and exposing the API key - restful-authentication

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.

Related

JWT and Session: how JWT should be properly used instead of Session

I am working on a project with PHP and angular. For the user sign in, we're using JWT. Still can't understand why we should use JWT instead of Sessions if each time the user browse a component we need to send the token to server code to check if the user still signed in or not.
Username and password will be sent to server code, where the authentication process will happen, and then generate a token and send it back to angular then save at the local storage.
Any comment on how JWT should be properly used.
EDIT
My question is about the process of checking the JWT when user surf the site and go from component into another.
If you use session for your application... Then while horizontal scaling sharing the session data becomes a burden ....you either need a specialised server .. Jwt are stateless and have no such requirement. It contain following data
Header - information about the signing algorithm, the type of payload (JWT) and so on in JSON format
Signature - well... the signature
Payload - the actual data (or claims if you like) in JSON format
Your JWT already is a proof of your authentication. So you have to send it with each request but you can simplify the authentication logic on server-side.
While on the login you will have to check the credentials you can rely on the JWT's signature and expiryDate. If the signature is still correct the token is valid and you do not have to authenticate anymore.
So regarding your horizontal authentication.
If the called service needs to be authenticated you have to check the JWT for validity on each request (normally works reasonably fast). If there are open api calls you can of course ignore the JWT on server side.
At the end of the day there is no difference to your "session" which will also send some "secret" key which maps your session context. Therefore, it will also be validated.
For some backends you can also use the JWT as your session key to get both worlds involved.
Example:
lets say you have two api roots:
api/secured/*
api/open/*
(Note that the secured and open are only here for demonstrative purposes)
The secured part will contain all the services you want to be authenticated.
The open part can contain insensitive data as well as your login services:
api/open/login -> returns your token
api/open/token/* -> refresh, check re-issue whatever you might need
So now lets say the user accesses your site. You will want to provde an authentication error if he tries to access any api/secured/* URL without a proper JWT.
In this case you can then redirect him to your login and create a token after authenticating him.
Now when he calls an api/secured/* URL your client implementation has to provide the JWT (Cookie, Request header, etc...).
Depending on your framework, language etc. you can now provide an interceptor/filter/handler on server side which will check:
If the JWT is present
if the signature is valid (otherwise the token was faked)
if the JWT is still valid (expiryDate)
Then you can act accordingly.
So to sum up:
There is no need to "authenticate" unless you want to create a new token.
In all other cases it is enough to check the validity of your JWT

Passing a CSRF token

I'm trying to pass a CSRF token in a form that is submitted via a javascript (not jquery) AJAX function. The accepted wisdom appears to be to include the token as a hidden input in the actual form. As I see it the problem with this is that the contents of a hidden input can easily be seen using a browsers inspect facility. So is there a more secure way to pass the token?
It's not an issue that the CSRF token is accessible in the source code because it's not meant to be hidden to the client browser. And when I say "hidden" here I am not talking about the HTML property "hidden" of a form, but the disclosure of the token, should it be by analyzing page source, script execution or network traffic (really hidden).
You must understand why CSRF tokens are useful. If an attacker creates a malicious webpage, hosted under an external domain, which POST or GET to your website, then he can expect an authenticated user (a victim - with an open session and sesion ID in cookie) to perform the POST/GET: the victim's browser will detect the target URL, add the cookies/session ID to the GET/POST headers, and perform the action on behalf of the authenticated user (eg: "destroy my account").
If you create a random CSRF token in the source, the attacker cannot read it (because he cannot load the page on behalf of the victim's browser and read its content, thanks to Cross-Domain content segregation) and hence cannot build a malicious page which will perform a GET or POST.
As for other methods, other websites use a unique static CSRF token (generated per user on session init), which is saved in cookies and included in the forms of the sites through JS. The result is the same, the token will be part of the form submit (and so accessible to the client's browser). You just avoid producing CSRF tokens for each form and the server can easily compare the token with the client's session data rather than performing side-channel token management.

Token based authentication in REST APIs

I trying to implement a token based authentication approach:
Every successful login creates new token.
If user selects "keep me logged in" or the user is using a mobile device, the token is persisted in a Redis database without an expiration date. Otherwise, the token will expire in 20 minutes.
Once user is authenticated, the token is checked from each subsequent request in my Redis database.
I'm wondering how I can identify devices. In case of mobile devices, I can use a device identifier. But how can I identify a browser?
Example: The user logs in using Chrome and selects "keep me logged in". A token is generated and persisted with the browser name in Redis. If the user logs in from Firefox, saves the token and "Firefox" in the database. I save the token in Redis whereas token is created on successful authentication. Is it fine to persist only the token and the browser where the token is being used? Or do I need to persist the IP as well?
Additional question: How to avoid attackers to steal the token from a cookie?
How token-based authentication works
In a few words, an authentication scheme based on tokens follow these steps:
The client sends their credentials (username and password) to the server.
The server authenticates the credentials and generates a token.
The server stores the previously generated token in some storage along with the user identifier and an expiration date.
The server sends the generated token to the client.
In every request, the client sends the token to the server.
The server, in each request, extracts the token from the incoming request. With the token, the server looks up the user details to perform authentication and authorization.
If the token is valid, the server accepts the request.
If the token is invalid, the server refuses the request.
The server can provide an endpoint to refresh tokens.
How to send credentials to the server
In a REST applications, each request from client to server must contain all the necessary information to be understood by the server. With it, you are not depending on any session context stored on the server and you do not break the stateless constraint of the REST architecture defined by Roy T. Fielding in his dissertation:
5.1.3 Stateless
[...] each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. [...]
When accessing protected resources that require authentication, each request must contain all necessary data to be properly authenticated/authorized. It means the authentication will be performed for each request.
Have a look at this quote from the RFC 7235 regarding considerations for new authentication schemes:
5.1.2. Considerations for New Authentication Schemes
There are certain aspects of the HTTP Authentication Framework that
put constraints on how new authentication schemes can work:
HTTP authentication is presumed to be stateless: all of the
information necessary to authenticate a request MUST be provided
in the request, rather than be dependent on the server remembering
prior requests. [...]
And authentication data (credentials) should belong to the standard HTTP Authorization header. From the RFC 7235:
4.2. Authorization
The Authorization header field allows a user agent to authenticate
itself with an origin server -- usually, but not necessarily, after
receiving a 401 (Unauthorized) response. Its value consists of
credentials containing the authentication information of the user
agent for the realm of the resource being requested.
Authorization = credentials
[...]
Please note that the name of this HTTP header is unfortunate because it carries authentication data instead of authorization. Anyways, this is the standard header for sending credentials.
When performing a token based authentication, tokens are your credentials. In this approach, your hard credentials (username and password) are exchanged for a token that is sent in each request.
What a token looks like
An authentication token is a piece of data generated by the server which identifies a user. Basically, tokens can be opaque (which reveals no details other than the value itself, like a random string) or can be self-contained (like JSON Web Token):
Random string: A token can be issued by generating a random string and persisting it to a database with an expiration date and with a user identifier associated to it.
JSON Web Token (JWT): Defined by the RFC 7519, it's a standard method for representing claims securely between two parties. JWT is a self-contained token and enables you to store a user identifier, an expiration date and whatever you want (but don't store passwords) in a payload, which is a JSON encoded as Base64. The payload can be read by the client and the integrity of the token can be easily checked by verifying its signature on the server. You won't need to persist JWT tokens if you don't need to track them. Althought, by persisting the tokens, you will have the possibility of invalidating and revoking the access of them. To keep the track of JWT tokens, instead of persisting the whole token, you could persist the token identifier (the jti claim) and some metadata (the user you issued the token for, the expiration date, etc) if you need. To find some great resources to work with JWT, have a look at http://jwt.io.
Tip: Always consider removing old tokens in order to prevent your database from growing indefinitely.
How to accept a token
You should never accept expired tokens or tokens which were not issued by your application. If you are using JWT, you must check the token signature.
Please note, once you issue a token and give it to your client, you have no control over what the client will do with the token. No control. Seriously.
It's a common practice to check the User-Agent header field to tell which browser is being used to access your API. However, it's worth mention that HTTP headers can be easily spoofed and you should never trust your client. Browsers don't have unique identifier, but you can get a good level of fingerprinting if you want.
I don't know about your security requirements, but you always can try the following in your server to enhance the security of your API:
Check which browser the user was using when the token was issued. If the browser is different in the following requests, just refuse the token.
Get the client remote address (that is, the client IP address) when the token was issued and use a third party API to lookup the client location. If the following requests comes an address from other country, for example, refuse the token. To lookup the location by IP address, you can try free APIs such as MaxMind GeoLite2 or IPInfoDB. Mind that hitting a third party API for each request your API receives is not a good idea and can cause a severe damage to the performance. But you can minimize the impact with a cache, by storing the client remote address and its location. There are a few cache engines available nowadays. To mention a few: Guava, Infinispan, Ehcache and Spring.
When sending sensitive data over the wire, your best friend is HTTPS and it protects your application against the man-in-the-middle attack.
By the way, have I mentioned you should never trust your client?
Once server is receives the request from the client, it contains the User-Agent. This attribute will help us to identify the client.
Please refer this link: How do I detect what browser is used to access my site?

How should I load images if I use token-based authentication

I have a client-side application on domain client-domain.example and a server-side application on domain server-domain.example. There is an API on the server-side. The client-side application sends AJAX requests to the server-side application. I use token-based authentication, so the client-side application sends token in headers with each AJAX request, for example: "Authorization: Bearer {some token}". It works fine with AJAX requests, when I need to get or post some data.
But the server-side API also keeps files. For example images. The files are private, only authenticated users can get them. And I need to show this images on the client-side in <img> tag. I can't get them using <img src="http://server-domain.example/path/to/image"> because in this case browser will not send Authorization header to the server-side.
What is the adopted solution? How client applications load images from server-side API?
There are three methods to solve it, the best approach to solve it is using the signed URLs
1. signed URL (can be insecure)
The first method simply creates a route without authentication (anonymous access) with a signature hash parameter that indicates if the resource can be loaded or not.
<img src="http://server-domain.example/path/to/image?guid=f6fc84c9f21c24907d6bee6eec38cabab5fa9a7be8c4a7827fe9e56f2">
When the server receives the request it must validate the guid if the expiration time has not been reached and, of course, check if the guid has a valid signature.
This approach is used by several files/documents servers like Dropbox, S3, CDN providers, etc.
See the technique in some companies.
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html#private-content-overview-choosing-duration
https://client.cdn77.example/support/knowledgebase/cdn-resource/how-do-i-set-up-signed-urls
SECURITY:
the guid can not be just UUID of the image/user, because this doesn't provide any protection.
the guid can not be the same token you use for authentication (for example, you can't use auth-JWT tokens), because the user can share the link - and the user will share his tokens (see also (2)).
as mentioned above: guid should have a server-side mechanism of validation (date/signature/...) and should not provide more permissions than "access to the requested file"
2 Query String with JWT (most probably a security breach)
The second method is to pass the token by querystring with the image URL.
This method is not recommendable because it exposes clearly the URL and many servers sometimes write and expose public logs of URL accessed. The bad notice is that the JWT exposed normally the user can get control a lot of features further the image load.
<img src="http://server-domain.example/path/to/image?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c">
When the server receives the request you must validate the token by querystring and response with the content.
SECURITY NOTES: worse than (1) - because now authentication info (JWT auth) is exposed in the URL and can be cached/logged by servers OR accessed by any server in the middle OR the user can simply share the "image link" with their colleagues.
But if JWT is NOT an access token, but a one-time token generated specifically for accessing that particular file in a form of JWT then it provides the same level of security as (1).
3. cookies
The third method creates an authenticated cookie to validate the access of the image.
This method is not recommendable because is out of API pattern (webapi/token based authentication in general).
When the server receives the request you need to validate if the validate cookie is valid.
SECURITY NOTES: if you can provide security for your cookies and XSS and CSRF — are not just letters for you then it is a solution. But keep in mind: cookies are sent by the browser automatically with each request. Much more information about possible threats and solutions: Where to store JWT in browser? How to protect against CSRF?
My solution to basically this exact same problem, based on Jeferson Tenorio's answer below (option 1), was to sign the URL to my API call with an encryption of the image and the user's JWT token, e.g. path/to/image?token=xxxx. In laravel this is easily accomplished with encrypt($your_object) and decrypt($token) (https://laravel.com/docs/5.7/encryption), and then I used the extracted token to verify the user had access to the file in question. But there are probably many other libraries capable of handling this.
I would be curious if there are any security concerns, but from my perspective the JWT is never exposed via plain text and the encryption relies on a secret key that malicious actors shouldn't have access to, so it seems like it should be fairly secure. My only real complaint is that the token is quite long using this method which does not make for presentable URLs.

Why do we need JWT when we have client sessions?

I understand that JWT are stateless tokens that store signed information about the client's claim and are passed to a server via the Authorization HTTP header.
My question is, why do we need JWT when we already have client sessions (https://github.com/mozilla/node-client-sessions)? Client sessions are conceptually the same. They're cookies that contained signed information which when verified means the cookie hasn't been tempered with. Also, client sessions are stored in a cookie and passed via the Cookie HTTP header. It's the same thing only using different words. Am I wrong?
So, why is JWT even around? I could understand that maybe the point is to standardize the way authentication tokens work, but we got along fine without a session ID based standard (each implementation did things their own way). Also, why would the JWT not use cookies as a means of transfer. With cookies, you wouldn't need explicitly send the correct header for every request (simplifying Ajax requests).
Am I missing something?
JWT tokens are signed JSON formatted documents that assert claims about a user (or any principal). If you trust the issuer of the token, you trust the claims in the token and can make authorization decisions based on this.
JWT tokens are often used for calling external Web APIs. These APIs do not necessarily live on the same domain as your website and therefore cannot use the same cookies as your site. JWT tokens are used in REST services as they do not need any session info stored on the server. Using JWT tokens is also not vulnarable to CSRF attacks.

Resources