stealing authentication cookie with fetch request contains credentials: include - ajax

Let's assume I implement my authentication by http-only & secure cookie. At first glance it should protect from stealing this cookie if an attacker got access to my site through javascript injection. It's true, he can't access my token through javascript, but on the other side he can use ajax request, fetch with credentials: include to his malicious site and get my token there. So do I have any way to prevent this type of vulnerability?

Related

JWT in Cookies - do I need a refresh token?

I'm implementing security for my React SPA using Spring Security on the backend. After a lot of reading, I opted for the following approach :
HTTPS everywhere
POST /login takes credentials returns JWT_TOKEN & XSRF_TOKEN in cookie form. I build the JWT_TOKEN myself whereas Spring Security handles the XSRF_TOKEN. Both cookies are Secured and SameSite=Strict. The JWT token is HttpOnly.
Subsequent API calls require the X-XSRF-TOKEN header. This is read from the aforementionned cookie. Both are sent and Spring Security compares them. JWT is automatically sent and checked in a Filter.
Every time a XSRF token is used, Spring Security generates a new one to prevent session-fixation attacks
XSS protections are applied by Spring Security
So now I'm wondering about refresh tokens. I'm reading a lot of contradictory info out there. Do I need them with this setup? If so how best to handle this ?
Many Thanks
In general, as its name says, the refresh token changes from one token to another. Typically they are used in OAuth protocol-based authentication. They are useful when an access token has expired, but the user's session is still valid.
First, JWTs are a great choice for access tokens. They have claims that match the access tokens requirements, such as: exp, iat, jti, sub, etc. But, when using a cookie-based authentication there is no need for access tokens and possibly no need for JWT.
As you said, your JWT_TOKEN is being set as an HttpOnly cookie, which means that only the server has access to it. JWT is useful for sharing the initial state between the client and server, and vice-versa. If your server is just taking it to look up the database, you don't need a JWT, you are just using a session concept, and keeping session data on a JWT may not be a good practice.
Second, if your authenticated cookie data will live at /login and die at /logout, there is no need for refresh tokens. Refresh tokens are an exchange key for short-life access tokens. Instead, your cookies keep the session live and don't need to be exchanged by something else.
For example, if the user uses the /login route to exchange your username and password for one short life access_token. He may need the refresh_token to get a new access_token without needing to send his username and password again.
If you are using the OAuth protocol or similar, refresh tokens are essential to provide a more seamless experience for your users and avoid the inconvenience of repeatedly having to re-enter their credentials. But even on OAuth, they are not mandatory.

Does Laravel CSRF protection provide 100% safety?

Laravel documentation says:
Laravel automatically generates a CSRF "token" for each active user session managed by the application. This token is used to verify that the authenticated user is the person actually making the requests to the application. Since this token is stored in the user's session and changes each time the session is regenerated, a malicious application is unable to access it.
Laravel config session.php file guarantees session cookie lifetime is 120 minutes by default:
'lifetime' => env('SESSION_LIFETIME', 120)
So let's imagine, for example, I authenticate into the Laravel app and receive session cookies. What will happen if within 120 minutes after authentication I will go to a malicious website and get exposed to CSRF attack? Of course, considering the fact cors.php config is set to allow accept any (*) origin ('allowed_origins' => ['*']).
In my current understanding within these 120 minutes after authentication browser has the session cookie, so if I go to a malicious website and get exposed to CSRF attack, the attack will be successful.
Please correct me if my current understanding is wrong?
So the problem with my understanding was that I was not aware of the fact you can't access a cookie of the origin that differs from website you are trying to access it. So in case of csrf, origin of malicious website differs from the origin of CSRF-TOKEN cookie which is provided by Laravel server, therefore attacks fails. So yeah laravel csrf protection works.
Full explanation of CSRF protection to the beginners as myself:
What is csrf attack?
Imagine, you authenticated into website with domain A, and received the session cookie from server that serves the site A. Annother malicious website with domain B contains a script which produce a request to the server which serves domain A as you enter the site B. As long your browser contains the session cookie of website A, the script attempting to attack from website B will be successful.
So how does csrf token help to cover this vulnerability?
Now laravel server sends you response with XSRF-TOKEN cookie, when you try to send axios request with script from domain A, axios automatically places value of XSRF-TOKEN to X-XSRF-TOKEN header in case of same-origin request (when website A has domain of the same origin as the server). In case of malicious website with non same-origin request, script can't access the XSRF-TOKEN cookie because it is not possible to accesss a cookie of another origin. So axios can't place a value of XSRF-TOKEN to a requst header or a request parameter. Server examines incoming request for X-XSRF-TOKEN or csrf token parameter, server is not able to find it, and so therefore server doesn't validate the request.

JWT with Bearer Token protect from CSRF and XSS whilst using AJAX

I have a webapp that has a javascript page that makes ajax calls up to the webservices. The authorization scheme used for the site is JWT with bearer tokens being used in the authorization header.
However, I now realize that I am vulnerable to XSS, because the token is being stored in a cookie, and the javascript needs access to this in order to set the bearer token in it's requests. Because of this, httponly is set to false, thus meaning my token can be stolen.
So, what I would like to know is whether there is a way to protect against both CSRF and XSS whilst using ajax requests and JWTs?

Are Anti-CSRF tokens stored on client-side, server-side, or both?

I'm creating a React Js website with a Node.js backend. I've been attempting to figure out a user authentication implementation to prevent CSRF attacks however I'm really confused about where to store Anti-CSRF tokens.
My thought process of a secure implementation is as follows...
User submits request to login with credentials.
Credentials are authenticated.
Server creates session.
Server creates JWT to use as Anti-CSRF token.
Server stores JWT (Anti-CSRF token) in session storage.
Server sends response to client.
Response has header to store session ID in HttpOnly cookie on client-side.
Response payload includes JWT (Anti-CSRF token).
Client receives response.
HttpOnly cookie holding session ID is stored on client-side.
Client stores JWT (Anti-CSRF token) in localStorage.
I figure when a user needs to request information, the client can send the JWT (Anti-CSRF token) via a header or payload, and the session ID will be sent automatically due to it being a cookie. Then, the server can check if the JWT (Anti-CSRF token) exists in the session storage.
I know that the JWT (Anti-CSRF token) will need to be refreshed at some point.
My confusion is due to storing the JWT (Anti-CSRF token) on the client side. I keep reading that it should only be stored on the server. But if it's only stored on the server it doesn't seem to be doing anything at all.
I thought of using both cookies and localStorage because it seems that if a request to the server needs both a HttpOnly cookie and something from localStorage to send back an "authorized" response, an attacker would need to both successfully ride a session and successfully implement an XSS attack to get the Anti-CSRF token.
I just started learning about CSRF and XSS recently so I could be completely wrong and there could be a huge flaw in my implementation that I'm missing. But my main question is... don't Anti-CSRF tokens need to be stored on the client AND the server?
They are also called "CSRF Tokens". When a client requests for a form (e.g. bank login page), server generates the tokens and passes them to client and when the client fills the form, client passes CSRF token along with the completed form. Server verifies the token value and if it matches, request is fulfilled. CSRF tokens are stored on server-side in synchronizer token pattern.

REST authentication and exposing the API key

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.

Resources