I have just learned about JWT for authentification. Storing the JWT token in localStorage/sessionStorage is exposed to XSS. Storing it in a cookie is vulnerable to CSRF. I have been researching this and I thought of this solution, but I'm not sure how secure is this and if I should be using it in production.
The soulution is to get the JWT token from server, store it in cookie. Generate a CSRF token (that will be stored in JWT) that will be sent with each HTML page, either in a hidden HTML field of as a global JS variable (). That CSRF token will be sent with every request using JS/AJAX. This way we can rule out CSRF first then verify the JWT token.
I'm not sure whether a new token should be sent with each loaded page, or a single token should be held per session. The 1st case would mean that only the last loaded page/form would be able to submit (which could be problematic if the user is having multiple tabs of other pages open).
Is this a secure solution that might be used in production ?
Also, what other solutions would be viable to reach the same goal ?
Here is my understanding of the issue.
JWT Token - stored in httponly/secure cookie - this ensures any Javascript does not have access to it
CSRF Token - stored by JS - on login, attach the claims as a header (including your CSRF value)
When the JS gets the header, it can store the claim in a cookie, localstorage, or session storage. Because no JS has access to the JWT token in the cookie, by attaching the CSRF with every request and making sure it matches what's in the JWT, you're ensuring that it's your JS sending the request and that it was your backend that issued the token.
A hidden field isn't a great solution because you'll have to add it to each request. Depending on what framework you're using, you should be able to load the CSRF token into whatever is responsible for sending the token as a header on each request when the page is refreshed. The benefit to having all the claims in local storage or a cookie is you can preserve the users front end state. Having the exp claim means you can tell when the user no longer has a valid token in the secure jwt token cookie and you can redirect to a login page.
sessionstorage - is specific to the tab only
localstorage - is specific to the domain across tabs
cookie - is specific to the domain across tabs
Add CSFR to AJAX request:
$.ajax({
type:"POST",
beforeSend: function (request)
{
request.setRequestHeader("CSRF-TOKEN", csrfToken);
},
url: "entities",
data: "json=" + escape(JSON.stringify(createRequestObject)),
processData: false,
success: function(msg) {
$("#results").append("The result =" + StringifyPretty(msg));
}
});
Even though you're not using Angular, this still applies. You should attach the token as a header with the JS and it's fine to get that value from a cookie.
From the Angular JS Documentation
When performing XHR requests, the $http service reads a token from a cookie (by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN). Since only JavaScript that runs on your domain can read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.
Related
I want to redirect from one webapp to another webapp ( on different domains) and need to pass token in headers ( any other suggestion for passing sensitive information without server side involvement will also be of great help).
Things I tried:-
Adding all data in URL params, redirecting to URL, storing them in session storage and then window.location.href = window.location.orgin.
(Problem: my token is visible in params till the time redirection happens ( also this method is to be avoided as per the requirement)
Passing token in cookies with window.open(url,"_blank")
window.document.cookie = "token=loremipsum12345u89
(Problem: not able to set https cookies and in encrypted manner)
Any direction for the same problem would be helpful.
I'm designing a CSRF defense in a Grails 2.4.5 application.
I would like to use the Synchronizer Tokens pattern, and here is the design I intend, simplified:
With a new session, on the server, generate a long unique csrf token.
Store the csrf token in the session.
Add the csrf token to the response header
On the server, for every incoming request that will potentially change application state (post, update, delete...) validate that the referer/origin is the same as the target
On the server, for every incoming request that will potentially change application state (post, update, delete...) check the presence and validity of the csrf token in the header.
In a grails app, I imagine this taking place in the Spring Security filterchain.
If the token is not present or does not match, return an error and log a potential CSRF attack, otherwise allow the request to proceed.
Does this sound correct?
My concern is that it's not sufficient. Officially, the synchronizer token is added to every form and every post, put, or delete URL. Yet, the application doesn't use form tags, so it seems to be onerous to add it to every single JavaScript-generated form in the application, not to mention requiring extensive testing to make sure no existing functionality is broken.
Thanks!
I'm a bit frustrated with managing my JWT token during login, submits and redirects. Before I get started here's my technology stack just in case:
JQuery/Html -> Node.Js -> Java Restful Services -> MySQL.
My java Restful services manages creating the JWT Token returning it to the Node.js layer which decides what to do with it and pass it on the the client. This all works wonderfully.
To get the JWT token I'm making an ajax based authentication request to the Node middle tier, which authenticates and returns the token which is summarily crammed into localstorage on the client.
Now I have no desire what so ever to make the entire site load off a single page through ajax, it's a complex site and doing that is just dumb! I need to forward and navigate to sub pages while carrying along the JWT token.
Here's the question (finally)... How do send along the JWT token to the middle tier (node.js) without attaching it as a request or post parameter because that's a big no no? I can't seem to find a way to stuff it in the header associated with Bearer.
You need to store the token at client side using for example a cookie or localStorage
Ajax requests
Cookies: A cookie is sent automatically when making a request to the server, so you do not need to add a specific header
LocalStorage:It is needed to provide the token in each request using an HTTP header.
For example
POST /authenticatedService
Host: example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
This is an example code to show how to execute an ajax POST request using jquery
$.ajax({
type: "POST", //GET, POST, PUT
url: '/authenticatedService' //the url to call
data: yourData, //Data sent to server
contentType: contentType,
beforeSend: function (xhr) { //Include the bearer token in header
xhr.setRequestHeader("Authorization", 'Bearer '+ jwt);
}
}).done(function (response) {
//Response ok. process reuslt
}).fail(function (err) {
//Error during request
});
Form submit
With a form submission you can not control the headers set by browser, so it is not possible to set the Authorization header with a Bearer token. In this case you can
Cookie: store the JWT in a cookie that will be sent with the form data. You will need to add extra security to avoid CSRF attachs
Form param: The JWT is stored in a hidden field of the form.
Use always POST (not GET) to avoid cache of JWT
Link
A link executes a GET request. You could build the link adding the JWT as a query param url?jwt=...
But, consider in this case the security risks. Browser can cache the url and it will be present in logs. An attacker could potentially obtain them if he has access. Also the user could copy the link and use it outside your web application (e.g send it by email...)
If you use cookies, the token will be automatically sent to the server by clicking on the link, but this will only work if the user is authenticated. In this case be aware of CSRF vulnerabilities
Your only option is to store the token in a cookie if you don't want to do anything suggested above. You can't set http headers in links.
I am working on a simple website using jwt. (node.js, koa.js)
Most example codes including expressjs, I cannot find the client-side example
about how to deal with jwt sent from a server.
Only one example (https://github.com/auth0-blog/cookie-jwt-auth) showed me that
[index.html]
... script src="app.js...
[app.js]
$.ajax({
type: 'POST',
url: 'http://localhost:3001/secured/authorize-cookie',
data: {
token: token
},
headers: {
'Authorization' : 'Bearer ' + token
}
After I read this example, I felt that I should have some scripts for users to send an authorization header with jwt. Is it right?
Or are there some front-end frameworks that deal with authorization header?
Thank you for reading newbie'q question.
Yes, you will need to define a mechanism for sending the user's JWT back to the server. It's up to you to decide where the JWT will live in the request -- the most common places are in the Authorization header, or by setting a cookie on the browser (which will be sent along with every HTTP request). You should also consider whether you want the JWT to persist across sessions / page reloads (using for example document.cookie or localStorage).
If you choose not to use the cookie approach, you can configure all $.ajax requests to set your Authorization header "pre-flight" using $.ajaxSetup({...}) (but this is a bit of a sledge-hammer approach). Manually setting the Authorization header on each individual $.ajax request, as you've demonstrated above, is a good option too.
If you want to skip headers all together, you can send the JWT inside the body of your request (as JSON, for example).
In my application,while registering the users i am saving username,password and jwt generated token with these fields in MONGO DB.When user tries to login with correct credentials then i will send the response with stored token.Then at client side(In my controller) i am using the localstorage to store the token so that i can send the same token for each and every request sent by the client.But I found some issues regarding this procedure:
I am generating same token for one user every time.So if any third person is able to get the token then he can access the restricted page.
Am i wasting space in db by storing the generated token in MONGODB
Can Anyone access the token stored in localstorage other than the user.
for each and every request in my single page application,I am again querying mongodb to get the token for that user and validating.Here,I am checking both client side and server side.
I am using jwt to generate tokens,Node,Express,Mongoose in my application
Am i following the good procedure.If not,can you please provide the solution for my approach or any new approach.
I have searched many sites for token based authorization and session based authorization,But nothing worked for me.
Note:I am beginner for Nodejs,AngularjS
You should store token in advanced key-value cache tool like: Redis
That would improve performance remarkably.
You will get token from database for 1st time then it should be stored in Redis. I used to set token as key and username as value. Next request , the token will be given from cache. with Redis you can set expire for token.
When a user registers, you would need to generate a JWT like you're doing now. That's OK. You don't need to save it to the database however. You didn't ask but I assume that the password should not be stored in clear text. You can use something bcrypt to encrypt before saving it to the database.
When user tries to login with correct credentials then i will send the response with stored token
Yes, that's correct way to do.
Then at client side(In my controller) i am using the localstorage to store the token so that i can send the same token for each and every request sent by the client.
Yes, on the client side, you can save the JWT to local storage and send it in subsequent requests to the server.
Now your bullet points:
So that you won't have the same JWT each time, you can include an "exp" claim in the payload (I'm assuming you're using something like jwt-simple to generate a JWT). Something like:
var payload = {
sub: account.username,
exp: moment().add(10, 'days').unix()
};
var token = jwt.encode(payload, "secret");
You don't need to store the JWTs in the database. In some cases, the token issuers (the authorization servers) are not the same as the resource servers. The resource servers only receives the JWTs in a request but there's no way for the resource servers to touch the database used by the authorization servers. Side note: If you eventually need to support refresh tokens, i.e. the JWTs that you hand to the clients will need to eventually expire, then you can store the refresh token in a database. Refresh tokens are not the same as JWTs (access tokens). The complexity to support refresh tokens will increase.
Local storage is not where you store passwords, but it can be used to store JWTs. For that very reason, a JWT must and should expire after a certain time.
Not sure what you mean by saying you check both client side and server side. When the client needs to access a resource (again it's fair to assume that the resource server might not be the same as the authorization server), the only thing that the resource server is passed is the JWT. Anyone can decode a JWT. For example, try to paste your JWT on this site http://jwt.io/. That's why a JWT should not contain any sensitive data. But if the resource server knows the secret that the authorization server uses when it encode the JWT, the resource server can verify the signature. Back to your third bullet, that's why it's OK to store the JWT in local storage of the client.
Update I'm updating this to answer to some of your questions in the comment box.
User clicks on 'Login' button triggers the Angular controller to post a request to the server, something like:
$http.post(url, {
username: $scope.username,
password: $scope.password
}).success(function(res) { ... })
Server receives the POST request, it checks username/password, then it generates a JWT, and sends back to the browser. Note that it does not have to save the JWT to the database. The code would be something like
var payload = {
sub: account.username,
exp: moment().add(10, 'days').unix()
};
var token = jwt.encode(payload, "secret");
res.status(200).json({
token: token
});
Back on the client side, in the success() callback above, now you can save the JWT in local storage:
.success(function(res) { $window.localStorage.setItem('accessJWT', res.token) })
The user is now authenticated. Now when user wants to access a protected resource, user don't have to provide username/password. With the JWT which can be retrieved from local storage, the client can now put the JWT in the Authorization header of the request using the bearer scheme, and sends the request to the server. In code, it would like:
headers.Authorization = 'Bearer ' + token;
The server receives the request. Again, this server receiving this request does not have to be the same as the server which generates the JWT above. The 2 servers can be in 2 different continents. Even if you save the JWT above, that does not do any good to this server which can not access the database where the JWT is stored. But this server can pull out the bearer token from the header of the request, validates the token and carries on with the normal tasks.
Hope this helps.
You do not want to store the JWT in mongoose because it appears in headers when logging in. You first generate a token then hash it using a module like crypto.
There are different ways to do this and they all use Passport which handles the tokens. Here's an example project Satellizer
I would recommend you generate the angular-fullstack project. Then go through the server/auth folder and the client/account folder. You will see how to securely handle authentication in a MEAN based app.