Can XSS replace an auth token/CryptoKey with a malicious one when stored in IndexedDB? - oauth-2.0

The bounty expires in 2 days. Answers to this question are eligible for a +100 reputation bounty.
raneshu is looking for an answer from a reputable source.
Can an XSS attack replace a CryptoKey stored in IndexedDB with a malicious CryptoKey?
TL;DR
The WebCrytpo API allows creating a CryptoKey (which will be used to encrypt/decrypt/sign/verify/etc) with a configuration of {extractable:false}. This allows the key itself to be stored “outside” the browser (somewhere in the user’s device). Only the CryptoKey Object (not the key itself, but the object that references the key) is stored in the browser (indexeddb).
This mechanism seems very safe, but if XSS can just replace the CryptoKey object with a malicious cryptoKey object, is it really that safe?

Related

CSRF tokens for Rails API application

In Rails API applications we don't have out-of-box CSRF protection. In particular, storing access tokens (JWT for example) in the localStorage is not recommended, it is recommended to store in cookies (with httpOnly flag, SameSite, and etc.). But this time we're vulnerable to a potential CSRF attack. In a full-stack generated Rails app, a CSRF token is generated and embedded every time we open a form. But, I don't know and couldn't find anything how we protect against CSRF using tokens in Rails API apps. Do we have best practices or could anyone suggest an approach? I use access and refresh JWTs.
This is a usual tradeoff of API design, and you can choose from several different approaches, with different risk profiles.
You can store the access token in localStorage or sessionStorage, accessible to javascript, and accept the risk. The risk obviously is mostly around cross-site scripting (XSS), because this way javascript will have access to the token, and in case of XSS, it can be accessed by the attacker leading to session compromise. If talking about an API, responses should have the content type set to application/json, which makes the API itself protected from XSS in modern browsers. However, that does not mean the client (presumably a single page javascript app) is also protected, that can easily be vulnerable and leak the token. Some frameworks are better protected by default against XSS, some are not so much, and you might have checks like static scans in your SDLC that give you a level of assurance that might allow you to accept this risk. Also if your SPA needs to send the token to multiple origins (different api endpoints), you don't really have another option. In this case the token can be sent as a request header, and CSRF is not an issue.
Or you can exchange XSS for CSRF, by storing the token in a httpOnly cookie. This is generally considered more secure, because CSRF in general is a lower risk vulnerability (but still significant ofc). In that case you will not be able to send the token to different origins, but XSS will also not have access. This does not eliminate XSS for the whole application, but at least the token will be secure. The cost is now you will have to deal with CSRF. One way to do so is the samesite attribute to cookies. Using that for the token cookie will prevent most cases of CSRF, but it is a UX tradeoff, users of some browsers will not be protected, and some cases might be missed when using the lax option for samesite (like when a GET request changes state). Only having samesite as the protection will likely also be flagged in a penetration test for the reasons above.
If based on the above you decide to have more protection, you can implement something like double submit, and still keep it stateless, which these APIs many times aim to be. In case of double submit, you generate a random value, set it as a cookie (either directly on the client, or by a response from the server), and copy the same value from the cookie in a request header. The server only has to compare the value from the cookie to the one from the request, if they match, the request is ok. The reason this works is because an attacker on their own domain (origin) cannot set or read cookies for the victim application domain, this is ensured by the same origin policy of browsers.
A somewhat different approach might be applying a message authentication code (like HMAC) computed from the whole request and a shared secret (like the API key), and checking that on the server, but this is a can of worms, it's easy to have unprotected fields not covered by the HMAC, the server needs to have access to plaintext api keys so it can compute the hmac and so on - it's not at all straightforward to get this right).
Note that if the client app is vulnerable to XSS, that negates any CSRF protection as the attacker will have a way to get any secret from the client and with that, perform any request, with any computed field (like a valid token).

Forging a Cross Site Request Forgery (CSRF) token

I had a look at Rails' ActionController::RequestForgeryProtection module and couldn't find anything related to using secrets. Basically, it uses secure PRNG as a one time pad, xors, computes Base64 and embeds into HTML (form, tags). I agree that it is impossible for an attacker to guess what a PRNG generates, but nevertheless I can generate (or forge if you like) a similar token, embed it into my "evil" form and submit. As far as understand Rails compares ( verifies) it on the backend. But I can't fully understand why it is secure. After all, I can generate my own token exactly like Rails does. Could someone clarify how the security is achieved?
You might misunderstand what this protects against, so let's first clarify what CSRF is, and what it is not. Sorry if this is not the point of confusion, might still be helpful for others, and we will get to the point afterwards.
Let's say you have an application that allows you to say transfer money with a POST request (do something that "changes state"), and uses cookie-based sessions. (Note that this is not the only case csrf might be possible, but by far the most common.) This application receives the request and performs the action. As an attacker, I can set up another application on a different domain, and get a user to visit my rogue application. It does not even have to look similar to the real one, it can be completely different, just having a user visit my rogue domain is enough. I as the attacker can then send a post to the victim application's domain, to the exact url with all the necessary parameters so that money gets transferred (the action will be performed). The victim user need not even know if this happens in xhr from javascript - or I can just properly post a form, the user gets redirected, but the harm is done.
This is affected by a few things, but the point is that cross-origin requests are not prevented by the same origin policy, only the response will not be available to the other domain - but in this case when server state changes in the victim application (like money gets transferred), the attacker might not care much about the response itself. All this needs is that the victim user that visits the attacker's page while still being logged in to the victim application. Cookies will be sent with the request regardless of the page the request is sent from, the only thing that counts is the destination domain (well, unless samesite is set for the cookie, but that's a different story).
Ok, so how does Rails (and similar synchronizer token solutions) prevent this? If you lok at lines 318 and 322 in the source, the token received from the user is compared to the one already stored in the session. When a user logs in, a random token is generated and stored for that particular user, for that particular session. Subsequent requests that change state (everything apart from GET) check if the token received from the client is the same that's stored in the session. If you (or an attcker) generate and send a new one, that will be different and the request will fail validation. An attacker on their own website cannot guess the correct token to send, so the attack above becomes impossible. An attacker on a different origin also cannot read the token of a user, because that is prevented by the same origin policy (the attacker can send a GET request to the victim app, but cannot read the response).
So to clarify, CSRF is not a protection against parameter tampering, which might have caused your confusion. In your own requests, if you know the token, you can change the request in any way, send any parameter, the CSRF token does not protect against this. It is against the attack outlined above.
Note that the description above is only scratching the surface, there is a lot of depth to CSRF protection, and Rails too does a little more, with some other frameworks doing a lot more to protect against less likely attacks.

How do sessions and cookies work in Rails?

I have been using Devise for a while to handle authentication on my Rails apps, but never really understood how it works. Because Devise also uses the session storage config set on Rails, I'm assuming this is a question on session handling with Rails.
Basically, I'm an auth newbie. I've read a few articles about authentication but most deal with abstracted libraries (they talk about engines, middle ware, etc) that don't make much sense to me. I'm really looking for lower level details.
Here's what I know so far..
I know about cookies and sessions. Cookies are strings stored on client-side which is used to maintain session across multiple HTTP requests.
Here's my basic understanding of authentication (please correct me if I'm wrong):
When user logs in, we send the SSL encrypted request to the server. If the credentials are valid, we save a random string called session id on the database (or any other data store) as the valid session id associated with a user id. This session id changes for each login/logout of the user.
After saving that session id on our data store, we return a response that asks the browser to set a cookie with the session id. This session id along with the user id would then be sent for successive request to the domain until it expires. For each request, our server would check the session id on the headers and verify if that session id is valid for that user id. If it is, then consider that user authenticated.
Here are my questions:
I've read that by default starting from Rails 2, it now uses CookieStore (instead of SessionStore) which generates session hashes with SHA512 (instead of session ids), and all this is stored on a cookie which means multiple user id's can literally have the same session hash and it would just work fine. It seems to me that this is a very dangerous thing, exposing a large number of hashes with a single secret key stored on the server and basing your entire authentication system based on this key. Is there a real world large scale application that uses hashing instead of storing server side session id's?
On the topic of storing active session id's on server side, I've also read that you can switch to use different kinds of session storage for Rails. Based on this, I've heard of systems moving authentication systems out as services and using auth tokens instead. What's an auth token and how does it differ from a session id?
Seems like I can just keep guessing a random string (for both hashing and server side sessions) to grab an existing session. Is there a way to protect against this? Is it normal to use more values stored on a cookie? (such as the username, real name or even another hash for authentication)
I know I'm asking a lot but I believe this will be useful for people like me who do not understand authentication and will be very useful to get a solid foundation about the topic.
I've read that by default starting from Rails 2, it now uses
CookieStore (instead of SessionStore) which generates session hashes
with SHA512 (instead of session ids), and all this is stored on a
cookie which means multiple user id's can literally have the same
session hash and it would just work fine. It seems to me that this is
a very dangerous thing, exposing a large number of hashes with a
single secret key stored on the server and basing your entire
authentication system based on this key.
Yeah, it seems scary at first blush, but I'm not sure what the danger really is. In Rails 4, session data is encrypted using PBKBF2, and then signed with your session secret. This signing helps detect if the contents of the encrypted session have been tampered and the server will reject the session if it detects tampering.
https://cowbell-labs.com/2013-04-10-decrypt-rails-4-session.html
If someone gains access to the session token (which is used to sign the session cookie), you likely have much bigger problems on your hands than end-users attempting to impersonate the wrong user.
Is there a real world large scale application that uses hashing
instead of storing server side session id's?
I honestly don't know the answer to this one offhand, but I suspect that the fact that this is the "default" for Rails means that there are more than a handful of sites out there using cookie session stores.
On the topic of storing active session id's on server side, I've also
read that you can switch to use different kinds of session storage for
Rails. Based on this, I've heard of systems moving authentication
systems out as services and using auth tokens instead. What's an auth
token and how does it differ from a session id?
I'm doing this on a server now - basically a random hash is generated when a user authenticates, and that hash is stored, encrypted and signed, in the cookie. The cookie hash is a key into a server-side datastore (in my case Redis, but it can be in a relational database or memcache or whatever you like), and the actual session data is the stored server-side mapped to that key. This leaves less of your session data in the hands of the client were people could potentially decrypt and analyze it, so it's generally a bit safer.
Seems like I can just keep guessing a random string (for both hashing
and server side sessions) to grab an existing session. Is there a way
to protect against this? Is it normal to use more values stored on a
cookie? (such as the username, real name or even another hash for
authentication)
Yes, you could do that, but it would take a very very long time. You would also need to guess how to sign the newly tampered cookie data so that it'd match what the server expects to see on its side, and it's signed with a pretty large key.
I really don't think there's much alternative for persisting authentication state to using cookies (I suppose HTML5 Local Storage would work if you're feeling exotic and don't care much about legacy browser support).

Rails way to store data on client for long periods

What is the preferred way to store some data on the users computer for long periods of time. I am unsure how long a session lives. As an alternative I could store the data in a cookie, but here I can't find any high level storing API which also takes care of signing my data and make sure it is not tampered with.
How do you typically solve this problem of having a persistent user id between sessions.
Check out these links, they might help you on understanding the cookie handling:
http://m.onkey.org/signed-and-permanent-cookies-in-rails-3
http://www.quarkruby.com/2007/10/21/sessions-and-cookies-in-ruby-on-rails
http://www.tutorialspoint.com/ruby-on-rails/rails-session-cookies.htm
You handle a persistent user with a cookie--but you are quite limited in how much data you can store in a cookie (4K?).
Large sessions should be stored on the database. Some browsers support local storage (HTML5) which may also be an option.
If you want to guarantee it can't be tampered with outside of your application, you should store it on the server-side in a DB. The signed cookies make tampering difficult (or at least detectable), but you're still size-contrained.

Should the SessionID in the QueryString or the Cookie of a GET request take precedence?

If I receive a request to the url host.com/site-directory/page-slug.html?session={someValidNonExpiredSessionGuid} and I detect a session cookie with the value: {someOtherValidNonExpiredSessionGuid}, then
Which session is the correct session to associate with the request?
Here's some background:
The pattern I am using for maintaining state across HTTP requests is to store a unique ID in the querystring, form collection and/or cookie. Then, on each request, I locate the unique Id and pull date from a database table. If the id is not valid, or the session is expired, a new session will be created (with a new id, a new cookie, etc).
The default behavior is that all forms will have a field:
<input type="hidden" name="session" value="{someGuid}" />
All links to other pages on the site will have an appended querystring parameter
a sample link
And, if the user's browsing device supports cookies, a cookie will be set having the session's value.
If I have validated that the user's browsing device supports cookies, then my form and querystring will no longer require the the session id field/parameter.
However, I am having a conceptual issue in deciding whether the session parameter of the querystring should take precedence over the cookie value or vice-versa. It seems like I could potentially get bad data in either circumstance. I could get data with a bad querystring parameter if the user bookmarked a page with the session parameter included in the URL. I could also get bad data from the cookie, if a user closes the browser without terminating the session, and the session expire-window has not yet closed. It also seems like both options could be vulnerable to a malicious user intercepting the request and sending a request with the same session information.
So, once again, my question is
If I receive a request to the url host.com/site-directory/page-slug.html?session={someValidNonExpiredSessionGuid} and I detect a session cookie with the value: {someOtherValidNonExpiredSessionGuid}, then Which session is the correct session to associate with the request?
I am leaning towards the cookie session, because it seems like the most common scenario will be a bookmark with the session included. I've already decided that the form post data should take the greatest precedence, because a page will always render the form with the correct ID, and the only possible situation with a wrong, non-expired ID is a very quickly implemented XSS attack, which is circumvented by including a request-scoped anti-forgery token field.
In addition to the primary question I appreciate any insight to any security-related or logical oversights I have expressed in this description. I apologize for the long post, but felt it was necessary to explain the situation. Thank you very much for your input.
Also, it is not necessarily relevant to the question, but I am using ASP.NET MVC in most situations, and setting my cookies manually with Response.Cookies.
From a security standpoint sessions should not be stored in query strings.
For example:
If sessions are stored in queries and you link to a remote host on the same page, the users valid session could be sent to the remote host via the referer header.
Sessions should always be stored in cookies.
You should try to store it in the cookie (looking the the browser caps to see if the browser supports cookies) and then have a fall back for query string.
I too would lean towards using the cookie session ID in case of ambiguity. This primarily because I'd trust the cookie implementation to be more well baked, more well tested, and more idiot-proof than my own home brewed session tracking implementation. For e.g. there are cases where ASP.NET automatically knows to clear the session cookies, renew them etc.
Also, I would design it so the cookies aren't persistent to minimize the edge cases. If the user closes the browser, then the session is closed.
The other simplification I would consider is to have either cookies or URL based tracking and not both. If the browser supports cookies, there is really no reason to also track the session through a URL.
Thoughts?
Curious ... What were your reason to rule out using the stock ASP.NET cookieless session implementation? - http://msdn.microsoft.com/en-us/library/aa479314.aspx#cookieless_topic2
If I receive a request to the url
host.com/site-directory/page-slug.html?session={someValidNonExpiredSessionGuid}
and I detect a session cookie with the
value:
{someOtherValidNonExpiredSessionGuid},
then Which session is the correct
session to associate with the request?
To answer your specific question, I'd recommend putting session management in the cookies as opposed to the querystring. As has been mentioned, cookies can be set to expire whereas the querystring cannot. This allows your thin-clients to assist in their own session maintenance by removing their own expired cookies. Moreover, since cookies are dropped to specific browsers, you reduce the chances of another browser spoofing the original browser session.
The only way I would use the querystring to pass session information would be as a backup method to re-establish a browser session onto a new browser instance. Here's a scenario: you have an active session using browser A on machine A which suffers some catastrophic error. You want a way to re-establish that same session on another browser instance on either the same machine or on another machine. If your code-behind can recognize that the session cookie doesn't exist, but that a valid session id exists in the querystring, you could initiate a challenge-response to verify the integrity of that session id and then drop a new session cookie on the new machine. Kinda extreme in my humble opinion, but the functionality might be useful in certain situations.
ADDED: I understand that you may want to accommodate users who have turned cookies off on their browsers, and while you can use the querystring to hold the session id I'd recommend against it. But if you must, encrypt that sucker using browser-machine specific information.

Resources