How and where to store access token securely - javascript

I know this question has been asked many times but frankly I have not seen an answer that satisfies the criteria I have.
So I have a ASP.NET WEB API that issues an access token (JWT) when user/pass are provided. At the moment requests are coming from an SPA application. The problem I have is storing the access token so it can be resent to the API from JavaScript.
So far it looks like there are 2 commonly suggested options
HTML5 Web Store
Cookies
But none of these is actually secure since they are not protected from XSS and CSRF. And on top of that it makes token easily accessible.
Any options you would suggest ?

Web store is vulnerable to XSS. Cookie is vulnerable to XSS if not HTTPONLY. Cookie is vulnerable to CSRF when not STRICT, even if it is HTTPONLY.
So I think we can store the access token in memory. Issue with that is there is no persistence between page refreshes. So for persistence we can store a refresh token in web storage or a cookie and whenever we do not have an access token we can get a new one silently with a refresh token.
When using an identity server a refresh token will not be of any use to any unauthorised client.
So if they XSS and steal the refresh token or CSRF with the refresh token cookie they cannot impersonate the user or get the access token.

Related

Where do I store the refresh token and access token and how do I use it?

i can't easily decide how to receive the refresh token and access token from the back-end and where to store it.
the authentication process I understand is as follows.
XSS can be defended with cookies.
using cookies is vulnerable to CSRF.
however, in the case of cookie, it can be stolen as 'document.cookie'. So, use the 'httponly' option to prevent access from javascript.
cookie is always included in header when making http request, so it is vulnerable to CSRF. So, when logging in, 'refresh token' and 'access token' are created in the back-end, stored in the DB, and returned to the client.
request an api using an access token, and if it expires, update it using a refresh token.
as I refer to many articles, it is said that XSS is to be blocked with cookies and CSRF is to be protected with refresh tokens and access tokens.
and in the case of refresh token, it is stored in webStorage.
however, in order to prevent XSS, it seems that in the case of access tokens, cookies should be used to protect them (+ httponly applied), and in the case of refresh tokens, it seems that they should be stored in the client.
if the refresh token is sent in a cookie with the httponly option, isn't it accessible from the client?
in conclusion...
in the back-end server, should the access token be sent as a cookie and the refresh token included in the body?
Any ideas would be appreciated.
The tokens are usually sent back in the body of the response. This way your frontend app can easily read them and store wherever needed. Usually storing them in memory should be enough (in a variable or state of your app, etc.). When the user refreshes the page they will have to log in again, but that should not be a problem if the Authorization Server supports things like "remember me". If you're using an OpenID Connect-compliant Authorization Server, then you can perform a silent login - so obtain tokens without the need of redirecting the user anywhere.
If you store your tokens in a http-only cookie then your app will not have access to them, so you won't be able to call any APIs from your app. I think this is not what you're trying to achieve.
Keeping the tokens in memory could help you be a bit more safe from XSS attacks, but you will never be 100% secure. Have a look at this talk: https://pragmaticwebsecurity.com/talks/xssoauth.html where it is explained. In fact the only way to be sure that an XSS attack can't steal your tokens is to keep the tokens in a backend app, not in the browser.

CSRF - Is it safe to ask it with api call?

I'm using session based CSRF on a site using Angular. Is it safe to make an HTTP call to ask for the CSRF token?
For example, if I sent a request with valid user session to a page called /csrf/get and it prints a raw token, is this secure enough for CSRF functionality? If not, is there anything I can do to make it more secure while keeping the JSON retrieval functionality?
It will be the first api call before everything else and I will keep it on localstorage to use it on every http call
In short, no. The way that you are trying to do CSRF protection exposes you to CSRF since your csrf/get endpoint is not protected from CSRF.
Essentially, you need to protect yourself from two main attack vectors: XSS, and CSRF.
CSRF
CSRF involves your site and a malicious site that will attempt to make authenticated requests to your site. If there is a way to request a CSRF token from the malicious site, you are not protected.
Usual methods for protecting from CSRF are by returning a token from your authentication API call, and storing that token in the browser session. The problem with this method is that it opens you up to XSS.
XSS
Cross-Site scripting, or XSS vulnerabilities are related to external scripts running on your page. This includes potentially malicious scripts inserted by an attacker.
Local storage and session storage are not safe, so you shouldn't store a token in regular cookies, for example.
To be safe from XSS attacks Your authentication response can store cookies that javascript can't read by using HttpOnly cookies.
So, using a token that you store with javascript protects you from CSRF, but opens you up to XSS, where using a session cookie protects you from XSS, but opens you up to CSRF.
Protect your API from XSS and CSRF
The solution is to use both approaches: your authentication API should set an HttpOnly cookie to protect from XSS, and should return a token to protect from CSRF.
Note that there's no need for a csrf/get api since the token should be returned by the authentication method: you want to only send that token in exchange of valid credentials. Remember to also send and validate that same token on all authenticated API calls.
Here is a great article explaining API security, why and how to do it in much more detail:
http://www.redotheweb.com/2015/11/09/api-security.html
Note on login CSRF:
Logic forms should also be protected from CSRF by creating pre-sessions with CSRF tokens.
(from https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#login-csrf)
Most developers tend to ignore CSRF vulnerability on login forms as they assume that CSRF would not be applicable on login forms because user is not authenticated at that stage, however this assumption is not always true. CSRF vulnerabilities can still occur on login forms where the user is not authenticated, but the impact and risk is different.
For example, if an attacker uses CSRF to assume an authenticated identity of a target victim on a shopping website using the attacker's account, and the victim then enters their credit card information, an attacker may be able to purchase items using the victim's stored card details. For more information about login CSRF and other risks, see section 3 of this paper.
Login CSRF can be mitigated by creating pre-sessions (sessions before a user is authenticated) and including tokens in login form. You can use any of the techniques mentioned above to generate tokens. Remember that pre-sessions cannot be transitioned to real sessions once the user is authenticated - the session should be destroyed and a new one should be made to avoid session fixation attacks. This technique is described in Robust Defenses for Cross-Site Request Forgery section 4.1.
First of all, use https, http is unsafe.
Then, better not to use GET.
Safe way is to send token in successfull auth request's (POST) response.
For more info, check:
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet

CSRF protection in singlepage web application

My Application structure as follows
1)API server running in api.mydomain.com
2)Frontend VUejs application running in www.mydomain.com
So i implemented authentication via httponly cookie.
But little confused with CSRF token implementation
Mysolution
1).CSRF token from the url like /getCSRF.
2) Store it in localstorage.
3) Send with every request.
But i don't think its the good way does anyone have suggestion?
First things first: Feel free to use session-based authentication in combination with the httpOnly (prevents your session cookie to be hijacked via XSS attack) cookie. Nothing wrong with that.
After a certain action (e.g. login), generate a CSRF token and store it in a cookie (make it accessible to JavaScript). You can HMAC the token with server secret so that attacker cannot recreate a CSRF.
Sure that CSRF will be sent as a cookie in each request, but the trick is to send it and expect it in a custom header (e.g. X-CSRF-HEADER). The final step is to check its validity.
The key idea is that attacker cannot attach custom headers while performing a CSRF attack.

Why should I put a CSRF token in a JWT token?

I want to bring a doubt about JWT tokens and CSRF from the Stormpath post that explain the advantages and disadvantages of storing the JWT either in localStorage or cookies.
[...] if you are reading values out of a cookie using JS, that means you
can't set the Httponly flag on the cookie, so now any JS on your site
can read it, thus making it the exact same security-level as storing
something in localStorage.
I'm trying to understand why they recommend adding the xsrfToken to
the JWT. Doesn't storing your JWT in the cookie and then extracting it
out and placing the JWT in the HTTP header and authenticating the
request based on the HTTP header accomplish the same thing as
Angular's X-XSRF-TOKEN? No other domain could make requests on a
user's behalf if you authenticate based on the JWT in the header,
since other domains cannot extract the JWT from the cookie. I don't
understand the purpose of the xsrfToken in the JWT - perhaps its just
an additional layer of defense - meaning that attackers would have to
have a compromised script on your site and CSRF a user at the time. So
they'd have to hit you in both ways to be able to pull of an attack.
The post is linked in this answer where says:
The last thing is to ensure that you have CSRF protection on every
HTTP request to ensure that external domains initiating requests to
your site cannot function.
[...] Then, on every request into your server, ensure that your own
JavaScript code reads the cookie value and sets this in a custom
header, e.g. X-CSRF-Token and verify that value on every request in
the server. External domain clients cannot set custom headers for
requests to your domain unless the external client gets authorization
via an HTTP Options request, so any attempt at a CSRF attack (e.g. in
an IFrame, whatever) will fail for them.
Even if they could set custom headers, they couldn't access the cookie where the JWT token is stored because only JavaScript that runs on the same domain can read the cookie.
The only way they could is via XSS, but having an xsrfToken in the JWT is compromised too if exists XSS vulnerabilities because a malicious script running in the trusted client domain could access the JWT in the cookie and include a header in the request with the xsrfToken.
So the equation should be:
TLS + JWT stored in secure cookie + JWT in request header + No XSS vulnerabilities.
If the client and server are running in different domains, the server should send the JWT and the client should create the cookie with the JWT.
I think that the equation is still valid for this situation.
UPDATE: MvdD agree with me:
As the browser does not automatically add the header to your request,
it is not vulnerable to a CSRF attack
I am the author of the Stormpath Blog Post. Storing XSRF token in the JWT isn't about that it is in the JWT, it is about that it is in a cookie. The cookie should be httpOnly, so you can not read it from Javascript.
Now, I think the point that caused a little confusion is where I talk about angular. Angular sets it's only XSRF cookie as well (which is not httpOnly) to put it into the header at request time (which can only be done by javascript on same domain). These are not the same cookie.
If you think about implementing XSRF support in your application, this has been done with storing server side state and the point of storing the XSRF. Storing it in the httpOnly cookie is about being stateless with XSRF. Here, you would validate the JWT signature, get the XSRF out of the claims, and compare it to the header.
The answer to your question is so that you do not need to store state on your server.
My understanding was this:
Store JWT is an HTTPonly cookie.
In that JWT, store a hashed version of an XSRF token.
Send the client the XSRF token when they sign in so they can store it in local storage
Later when the client sends requests, the JWT is automatically sent with each request via cookies and then you also send the XSRF token via a header or query variable and on the server side, re-hash to compare to what's in the JWT on the server
Your JWT is protected from being stolen in a XSS and you're protected from XSRF. XSS could still execute on your browser but could only do damage for that session in the browser. Ultimately, You couldn't stop someone from writing a really detailed script that just ran on your browser, so conventional safeties to protect from XSS are still needed by the web developer.

Is it possible to securely use OAuth2 on client applications without having to re-enter user credentials frequently?

I'm trying to authenticate multiple client applications with a backend REST API using OAuth2. I have three completely separate entities: backend API server, front-end Javascript app, and distributed mobile app. The clients are first-party applications, so they will take direct username/password input to generate access tokens.
So my question is: is it possible to authenticate to the backend REST API from these clients using OAuth2, without having re-enter user credentials every hour when the access token expires? I can't think of a solution that gives the access token a one-hour expiration, but yet seamlessly refresh the access token in the background without additional user input.
Yes, I know about setting an encrypted HTTP-only cookie containing the refresh token. That works fine for the Javascript app, but does nothing for the mobile app. Or any other non-web client that may pop up.
I thought that I had come up with a great idea, which was to use an additional authentication server to negotiate the authentication on behalf of the client. So, the client would send the authentication server the username/password, and the authentication server would then call the API, save the access/refresh token, and finally give the access token to the client application. The idea being that when the access token expires, the client could then send the access token to the authentication server, which looks up the refresh token, calls the API to get a new access token, and returns the new access token. The client has now refreshed its access token without ever knowing the refresh token.
But then I realized that I am essentially nullifying the existence of a refresh token, and instead trading an expired access token for a new access token. Unless I'm wrong, that is not really any better than just straight up giving the client the refresh token - if an attacker obtained an access token, he could forever have more access tokens.
Based on my research, I am coming to the conclusion that what I want to achieve is simply not possible. In that case, is the only possible method of having a persistent login to increase the access token's expiration time? That is a very poor outcome, in my opinion. Surely some other solution exists, right?!

Categories