If you have an API, you can program your web client like an API client, using bearer tokens for authentication (put them in local storage). It's probably better than cookies.
Why do you think so? I would guess it's a tradeoff about what you think is more likely to happen. XSS or CSRF.
Local storage (and session storage) is vulnerable to XSS. Use a strict content security policy and escape (htmlspecialchars in php and similar functions in other languages) output to combat that.
Cookies are vulnerable to CSRF but can't be read from JS if they are http only (no XSS). To combat CSRF most frameworks already have built-in csrf token support. In case of a API use a double submit cookie. Frameworks like AngularJs/Angular support that out of the box. Also use the secure flag SameSite and __Host prefix [0][1]
If you mean that HttpOnly for cookies protects against XSS, you are mistaken. The attacker will simply generate requests to the secure endpoints rather than steal the token and use it from somewhere else. HttpOnly does not really protect you against XSS at all.
With "no XSS" I meant a XSS exploit doesn't allow access to the data stored in the cookie. I didn't mean it would protect against XSS. Poor/lazy wording on my part, sorry.
It's true that a attacker simply can generate requests from the XSS'ed browser, my understanding was that the session/token is more valuable to an attacker then only an XSS exploit.
However it seems that someone in the past had the same understanding as me and tptacek disagreed [0]. Oh well. Also reading the linked article [1] (are you the author since you use the same wording?) and it's linked articles it seems both cookies and webstorage are not ideal solutions, but local storage might be preferable since CSRF is not a problem, so one thing less to worry about.
How is that better than a cookie though? Cookies already provide automatic storage and expiry mechanism. Bonus feature is that they are not accessible by JS code at all, if set httponly flag.
Browsers automatically attach cookies to HTTP requests, opening the door to attacks like CSRF.
The security impact of automatic client-side expiry is tiny, since token expiration must be done server-side anyway.
The HttpOnly flag as an XSS mitigation is almost useless; competent attackers will simply run their code from the victim's browser and session. To protect against XSS, HttpOnly doesn't really help you at all. You should be setting a CSP that prevents inline and 3rd party scripts by default, and whitelist what you must.
Overall, cookies may seem like they have a lot of security features, but in reality they are just patches over poor original design. IMHO, using local storage is probably better, because there's less room to get it wrong.
If you use cookies as a storage mechanism and ignore the cookie header on your backend, you close the door to CSRF attacks.
Here's one glaring problem with local storage: literally any script on your page can access it (for example, vendor scripts). Cookies can only be accessed by scripts from the same domain from which they're created.
That's true, but if you run untrusted scripts on your site it's pretty much game over, anyway.
Why should those scripts limit themselves to stealing tokens when they can send authenticated requests from the browser? To put it another way, why would you care about knowing the root password when you have a way to run a root shell at will?
It's interesting that every time this comes up people talk as though the only vector for a malicious script running on your site is you serving it yourself. A reminder that browsers have a ridiculously lax permissions/security model for extensions which extension developers have been shown again and again to abuse (see the Stylish incident for instance).
The scenario is XSS, where the attacker manages to run their JS code on your page, and get all the same privileges as your own code on the page. Whatever mechanism your own JS code uses to perform authenticated requests, the attacker can do the same.
That is not the scenario you described (running untrusted scripts on your site). Cookies are not protected from XSS, but they are protected against malicious or compromised vendor/CDN scripts and browser extensions. Local storage, however, is vulnerable to all of the above.
It seems that you are saying that cookies are more protected from third party code than your own code. That is incorrect.
Let's get specific: let's say you have a page on mysite.com. When a user signs in, the server sets a HttpOnly session cookie to authenticate later requests from the user.
Now let's assume your page loads evilsite.com/tracker.js. The code in tracker.js can now send requests to mysite.com, and your HttpOnly session cookie will be sent. There is no extra protection for cookies that would check if the JS code doing the sending came from mysite.com.
Obviously tracker.js cannot read the value of your session cookie (and, indeed, neither can your own code), but mysite.com is more or less totally compromised.
You're describing CSRF, but again: this vulnerability doesn't exist in the scenario I'm describing.
If you don't set HttpOnly on your cookies and ignore the cookie header on your backend (i.e. only use cookies for storage, not for transport), cookies are strictly better than local storage, since the only difference between the two is now local storage's lax access policy.
The scenario you're describing can also be solved by using a CSRF token retrieved from the backend. Meanwhile, there is literally no way to secure secrets kept in local storage from third party scripts.
No, I'm describing XSS. You know, where an attacker injects scripts on your pages, and the attacker's requests come from the same origin as your own requests. In CSRF, the attack is hosted outside the target site.
I don't believe a situation exists where using cookies for client-side storage is more secure than local storage. Could you please explain this in more detail?
Your page is mysite.com. When a user signs in, you save some sort of session token to a non-HttpOnly cookie. Your backend ignores the cookie header, and you send the session token as a different header with every request. (Basically, the same way you'd authenticate if you were to use local storage).
Now assume your page loads evilsitem.com/tracker.js. They can send requests to your backend, and the cookie will be included, but since your backend ignores the cookie header it doesn't matter. The malicious script, however, cannot access the cookie directly, since the script's origin is not the same.
That's why I say cookies used this way are strictly more secure than local storage: the fact that they're included with every request is irrelevant, and they're protected from direct access by third party scripts. Local storage is not. Even if you use JWT for auth, you should still store it in a cookie.
You are misunderstanding how the same-origin policy applies to scripts. If a page on mysite.com loads evilsite.com/tracker.js, then tracker.js runs with the same "origin" as the rest of the page that loaded it. The script has all the same access, including document.cookie, as a scripts loaded from mysite.com. Try it.
The same-origin policy only limits access between windows and frames. All scripts loaded on a page will have the same "origin".
> competent attackers will simply run their code from the victim's browser and session
What do you mean? JS even on the same page can't read HTTPOnly cookies. If you are assuming that the browser has been hacked then it is pretty much game over regardless of what you use.
We are talking about XSS, where an attacker can run their JS code on your page. If the attacker can run JS on your page, they can already do whatever your signed-in user can do. No need to read the cookie to make authenticated requests, just like your own code doesn’t need to read the cookie.
No, they've described a Bearer Token workflow. JWT is a specific method that also (most times) uses Bearer tokens, but it wasn't the first, nor does it have a monopoly on Bearer tokens.
I remember building a service when I was experimenting with web development that used randomly generated tokens in a custom HTTP header, and that is closer to Bearer Token (the standard) than Bearer Token is to JWT.
You're trying to be disingenuously pedantic. It's irrelevant if the workflow is specific to JWT or is shared by other bearer token schemes. The point is that JWT, which is a bearer token scheme, follows that workflow, thus it makes no sense to present that workflow as an alternative to the JWT workflow, as it's precisely the same.
If you believe JWT is "precisely" the same as mere presentation of a token, then you're woefully ignorant of JWT.
> ... it makes no sense to present that workflow as an alternative to the JWT workflow ...
But that's not what happened, is it? In fact, it's the opposite. As I read it, [1] suggests a bearer token workflow, to which [2] replies that the suggestion is "an awful lot like JWT", whereupon [3] clarifies that the original suggestion is just a normal bearer token scheme, which, I claim, shares nothing with "JWT" except the "T".
> ... JWT, which is a bearer token scheme ...
The "T" in "JWT" is the least interesting bit of JWT, and merely a necessity.
> It's irrelevant if the workflow is specific to JWT or is shared by other bearer token schemes
When not talking about any specific bearer token scheme, it is absolutely relevant. Only the generic point was under discussion, until JWT was introduced. JWT is not just another bearer token scheme. It comes with its own additional obligations, restrictions, and extra steps, not to mention the purpose-defeating pitfalls.
> JWT is not just another bearer token scheme. It comes with its own additional obligations, restrictions, and extra steps, not to mention the purpose-defeating pitfalls.
The big objection to JWT is that it's a bearer token with no revocation support. If you're going to implement a bearer token with no revocation support, or a custom revocation implementation, anyway, then the criticisms of JWT apply just as much to the system you're building and you might as well just use JWT.
> The big objection to JWT is that it's a bearer token with no revocation support.
That statement is not true. JWT do support revocation. In short, servers are free to reject any token, which triggers a token refresh on the client-side. Token revocation is even an intrinsic aspect of JWT as they suport issue and expiry timestamps, along with a nonce to avoid replay attacks.
It seems some users have an axe to grind regarding the idea of having to keep track of some tokens that were poorly designed (i.e., absurdly long expiry dates without a nonce) but the solution quite obviously is to not misuse the technology. In the very least, if a developer feels compelled to use a broken bearer token scheme that does not expire tokens based on issue date then quite obviously he needs to keep a scratchpad database of blacklisted tokens to compensate for that design mistake.
> In short, servers are free to reject any token, which triggers a token refresh on the client-side.
Servers can of course implement whatever custom behaviour they desire, but the protocol itself (and common implementing libraries) does not have any direct support for revocation.
Furthermore, any revocation implementation will inherently have to compromise the statelessness that is JWT's most prominent selling point.
> Token revocation is even an intrinsic aspect of JWT as they suport issue and expiry timestamps, along with a nonce to avoid replay attacks.
JWT does indeed support expiry and nonces. But these are not the same thing as revocation.
> It seems some users have an axe to grind regarding the idea of having to keep track of some tokens that were poorly designed (i.e., absurdly long expiry dates without a nonce) but the solution quite obviously is to not misuse the technology. In the very least, if a developer feels compelled to use a broken bearer token scheme that does not expire tokens based on issue date then quite obviously he needs to keep a scratchpad database of blacklisted tokens to compensate for that design mistake.
Insults and "obviously"s are not a good way to convince people of your point of view.
> Servers can of course implement whatever custom behaviour they desire, but the protocol itself (and common implementing libraries) does not have any direct support for revocation.
That's patently false. The protocol does support revocation. In fact, its basic usage specifically states that servers are free to force the client to refresh its tokens by simply rejecting it. If a JWT is expected to be ephemeral and servers are free to trigger token reissues, what lead you to believe that JWT didn't supported one of its basic use cases?
> Furthermore, any revocation implementation will inherently have to compromise the statelessness that is JWT's most prominent selling point.
That's false as well for a number of reasons, including the fact that JWT use nonces to avoid replay attacks. And additionally JWT's main selling point is that's a bearer token that's actually standardised, extendable, provided as a third party service, and is usable by both web and mobile apps.
> JWT does indeed support expiry and nonces. But these are not the same thing as revocation.
Expiration timestamps and nonces automatically invalidate tokens, which are supposed to be ephemeral, and nonces are a specific strategy to revoke single-use tokens. As it's easy to understand a bearer token implementation that supports revoking single-use tokens is also an implementation that supports revoking tokens, don't you agree?
> Insults and "obviously"s are not a good way to convince people of your point of view.
Perhaps educating yourself on the issues you're discussing is a more fruitful approach, particularly if you don't feel confortable with some basic aspects of the technology and some obvious properties.
Also, people make a big stink when authentication cookies aren’t marked as HTTPONLY. Storing tokens in localstorage (even sessionstorage) is just as bad but for some reason more accepted.
Stealing tokens from localstorage or cookies means the attacker can run code in the user's security context. Why would they limit themselves to stealing tokens? Using them outside of the browser would be stupid, anyway, as it would risk tripping reauthentication, IPS, or whatever.
HttpOnly is a joke, and people should stop claiming it helps with XSS. It does not help. It's security benefit is at most neutral. In fact, people often seem to think that it prevents XSS, and get lulled into a false sense of security. For that reason, HttpOnly seems to be worse than neutral.
Persistent access via an authentication token is a hell of a lot more reliable than relying on the user not navigating from/refreshing a specific page where XSS is present.