Imagine my surprise when I later learned how cookie storage worked! There was a brief moment of "oh, I guess not having to manually append this to my requests would be nice," only to read about CSRFs and other horrors cookies enabled.
Since then, I've fixed multiple existing CSRF exploits in production (or near-production) applications, and continued to avoid cookies wherever possible.
Cookies do not require javascript, unlike whatever solution using web apis.
At least, that's typically part of how I parse it when people refer to a site as an SPA.
Not just for performance reasons. But because that puts the burden of maintenance of those elements on the website.
It's a lot like how you shouldn't use fixed width and margins for pages. There are a lot of assumptions developers make when building these elements that can easily change and obsolete their work.
So unless you absolutely have to don't take on that burden.
But where I have more issues is that OWASP clearly advises not to use web storage for identities:
+ A single Cross Site Scripting can be used to steal all the data in these objects, so again it's recommended not to store sensitive information in local storage. + A single Cross Site Scripting can be used to load malicious data into these objects too, so don't consider objects in these to be trusted. + Pay extra attention to “localStorage.getItem” and “setItem” calls implemented in HTML5 page. It helps in detecting when developers build solutions that put sensitive information in local storage, which is a bad practice. + Do not store session identifiers in local storage as the data is always accessible by JavaScript. Cookies can mitigate this risk using the httpOnly flag.
That said as it is a wiki anyone is free to create an account and improve it :)
Your feeling is correct. Let me put it this way:
You mitigate SQL injection effectively by making sure no data (user input) can affect the code (SQL query). i.e. Prepared statements.
There is no analogous equivalent for defeating XSS. You have to escape output.
https://paragonie.com/blog/2015/06/preventing-xss-vulnerabil...
Escaping input for SQL injection "works", but has failed pretty hard in the past:
http://stackoverflow.com/a/12118602/2224584
(Character encoding, for the lose.)
By comparison, CSRF is trivial. You use a token that only the client should know, and implement a trivial challenge/response authentication layer onto your HTTP POST APIs, make sure you're using TLS, and call it a day.
`httpOnly` doesn't protect you from anything if you are using those same cookies in AJAX requests.
He's not saying that XSS protections don't matter because attackers "prefer" CSRF over XSS. They do not.
What he's saying is that pages that are vulnerable to XSS attacks --- or, properly: pages that are vulnerable to Javascript injection and DOM corruption --- open up a wide vista of exploits, and "stealing session tokens" is just the most naive of them.
Many of the more sophisticated exploits for Javascript injection resemble CSRF. But I think it's a little confusing to overload the term that way: Javascript-injectors have more latitude than conventional "blind" CSRF attackers do.
Long story short: once you've lost control of the Javascript on a page in your same-origin, you're almost always toast. Attackers can hijack sessions. That they don't store and retain the token is irrelevant; the token is just an artifact.
I don't know anybody who works full-time in software security who thinks HttpOnly is a meaningful defense, but, if you're out there, chime in and make a case for it. :)
I'm going to mostly agree. It's only a meaningful defense against bottom-rung attackers who go after the lowest-hanging of fruit, and if you're susceptible to them, you've probably got bigger problems (in the server-side RCE realm).
And of course, XSS means token-based SSRF/CSRF mitigations are completely toast in the absence of any extra controls (i.e. password re-prompts, CAPTCHAs). Rough and dirty pseudocode PoC for anyone who thinks different:
// Assuming the token is in the HTML form, and tied to the session.
// If it's in a cookie, just skip to $.post():
$.get("/url", {"arg": "value"}, function(data) {
// ... Grep for token ...
$.post("/url", {"arg": tokenGoesHere});
// pwnd
});
What httpOnly does well is put the burden on the attacker to create a more explicit exploit than "grab the session ID and POST it to this external site so I can swoop in impersonating them at my leisure". They instead need to make the payload do all the commands they intended or connect back to await instructions.Is that a meaningful security gain? Not in the big scheme of things, no. "Not vulnerable to script kiddie tactics" isn't exactly a bragging right.
The proper fix is, of course, to update your code and remove XSS vulnerabilities.
I'd say a case can be made for it with certain very specific preconditions, with the most obvious being for applications where session tokens persist for convenience, perhaps inordinately so. I agree that at least within that browsing instance, you're dead in the water, but it would still be worse if an attacker gains persistent session access on platforms which handle re-authentication in a manner similar to this: https://github.com/reddit/reddit/blob/1619fe1cc138aad3d9c6bc...
(I got credited[1] for that gem two and a half years ago. It looks like the faulty logic is technically still there[2], but I haven't checked at all to see if they've mitigated it further. Their partial mitigation after my constant harassment was to actually implement site-wide HTTPS/HSTS and implement session token blacklisting since their session management logic needs much more work, but I can't find the latter feature in production anymore.)
---
[1] http://www.reddit.com/u/eganist
[2] https://github.com/reddit/reddit/blob/862a7e0fc5142058a88044...
Correct me if this is exploitable. (I haven't done this myself, but I've seen this in a wild).
A server can override this default behavior by including a domain attribute in the Set-cookie instruction but this is pretty uncommon. Cookie scoping (and therefore cross-domain protection) can be managed differently if the default behaviour is not intended and HTTPOnly is not relevant here. HTTPOnly is really only a simple mitigation against the most obvious and trivial Cross-Site Scripting (XSS) exploitation technique (i.e stealing a session token).
In general, it's annoying because local/session storage are so close to being ready for prime-time, and as outlined here there are various advantages. But the XSS mitigations are too important to abandon if you really want the most security available, IMO.
Generally, you can take anything Portswigger says about web security to the bank.
It is worse than useless because it makes you think you are secure.
XSS attackers are more likely to generate arbitrary requests to secure endpoints as you via JS than they are to send the cookie to themselves at 3AM so they can rush to craft requests.
And httponly does didly squat to prevent that.
Better is to focus entirely on santizing your output properly in the context it is outputted. And use whitelists, never blacklists.
- If you get attacked in a way where the XSS would allow token exfiltration but are protected by HttpOnly, then the attacker is more likely to just grab data with your session cookie auth and POST that data over to their server using JS (or some other super-CSRF-like attack), so the HttpOnly limited the data you can protect to the token, which hardly matters at that point.
Is that a more fair understanding of the issue? Thanks for the correction here!
Of course the value is still sent over the wire so is vulnerable to MiTM attacks that are not otherwise mitigated.
I can't think of a benefit off the top of my head for having a second token that is accessible client-side, presumably that is something application specific. Perhaps there is a short-cut to getting a new session token after server-side session expiry (or the user accidentally closing their browser) which can only be used in the presence of a valid client-side token (though shortcuts like that are security holes waiting to happen IMO).
---
Also you mentioned this, but disappearing on tab-close makes it less of a drop-in replacement for cookies since that breaks existing behavior.
Interesting idea though! Enjoyed the read.
$('body').on('click', 'a', function() {
this.href += getToken();
});Better use an AJAX lib allowing a global override. Most of them allow it including the one in jquery and angular.
If you wanted user tokens to persist past the current browser session, you'd use localStorage.
OTOH, there are some usability issues with web storage. Mobile safari in private mode (which can be a not-insignificant percentage of iOS traffic) doesn't support localstorage. Also, if you have any server-rendered pages, say, you have both a single-page web app and some admin pages with server-rendered html content, you need cookies anyway for the server-rendered pages (unless you want to make all your non-ajax forms to submit via ajax to use the local storage token, which is a pain because you'll end up fighting against most web frameworks and re-inventing the wheel).
Given this, my takeaway is that cookies basically cover a super-set of the scenarios that local storage tokens do, so it seems preferable to stick with cookies everywhere vs. using two separate auth flows, even though I agree that web storage is conceptually a nicer model with a smaller attack surface.
Also, is this workable on most mobile devices? it us avail in electron because its essentially a chromium wrapper i think but are there any other compatibility issues / synchronization issues where one or the other makes more sense?
I see the exact opposite. The attack surface provided by enabling JavaScript is far greater. Cookies work with JavaScript disabled while Web Storage does not. If one is really really paranoid, hiding behind 7 proxies, using Tor, then one has JavaScript off entirely.
I do want my users to be able to keep their session across tabs and not be forced to sign in again and as far as I know the only way to do this is to use cookie storage
Does anyone have a strategy use one of Web Storage APIs AND keep your session across tabs/windows?
One use case would be somebody logging into a sensitive site from a public computer and expecting the session to end when the browser is closed.
Is there a way to mitigate this?
For example, you could use a cookie to authenticate serving GET /user/account-settings to render an HTML form, but then require submitting the form (e.g. POST /user/account-settings) to pass a token from localStorage.
This wouldn't protect you from all kinds of cookie-based attacks, but at least you'd have a guarantee your endpoints aren't vulnerable to CSRFs.
What is the definitive answer for where I should store them? Based on this blog post, the answer seems to be "Web Storage"
My session tokens are just 48 characters of base64 randomness, because that way I don't have to worry about dealing with JWT - it's a neat idea, but there have been enough bugs in the implementation that I'm still leery. Plus: this way I can expire tokens on the server side.
I keep session tokens stored in redis, and do an expire on them by default. Each time the token is fetched, it gets expired again (so the token actually expires N days after the last time the user visits). So yeah, each API request is burdened with two redis calls - one to verify the token and another to reset exipiry. It doesn't seem to weigh on my overall server perf - the actual API call is where most of the cpu goes (relatively speaking, it's still straightforward indexed sql queries most of the time).
The token is attached as a bearer token in the header of all my XHR requests.
The big problem is images. I work with teachers, who may want to post images of students or student work (to share with colleagues in a research project, behind security is okay, but not if those images can leak to the broader world). You can't (AFAIK) set browser-wide headers on the request that gets generated when the browser encounters an img src="url" tag. That's unfortunate, and I've played with a few options. One is: don't protect images at all - if the images aren't sensitive, then leaking them to twitter or pinterest when a user right clicks isn't the end of the world. One is: use a cookie that's only good for authenticating on the image server, and set it at app init time. One is: fetch the image data in an XHR, set the image up as a blob in the page, and set the img src attribute to the blob's dataurl.
None are particularly satisfying. You can crash the chrome tab (at least in chrome, I didn't test in safari / ff once I knew I was crashing chrome) if you go with the blob direction if the blob image is too large (goes OOM, then goes "aww snap"). Unfortunately, the blob approach is probably the best one if you're really worried about leaking images due to user foolishness - any user could download and re-upload an image anywhere, but you want to prevent someone from right-click, copy-image-url, tweeting the image. The blob approach prevents the image url from leaking in that situation, whereas the special-cookie approach doesn't.
Ultimately: we decided that we would allow images to leak if they had to - we're training users that they need to not embed potentially sensitive images inline at all. It's annoying, but there we are. Here's where session storage doesn't work as well as cookies.