Hacker News new | comments | show | ask | jobs | submit login

There's some OK stuff here, but the list on the whole isn't very coherent. If this is a guide specifically for "APIs" that are driven almost entirely from browser Javascript SPA's, it makes sense. Otherwise, a lot of these recommendations are a little weak; for instance, most of the HTTP option headers this list recommends won't be honored by typical HTTP clients.

Further, the list succumbs to the cardinal sin of software security advice: "validate input so you don't have X, Y, and Z vulnerabilities". Simply describing X, Y, and Z vulnerabilities provides the same level of advice for developers (that is to say: not much). What developers really need is advice about how to structure their programs to foreclose on the possibility of having those bugs. For instance: rather than sprinkling authentication checks on every endpoint, have the handlers of all endpoints inherit from a base class that performs the check automatically. Stuff like that.

Finally: don't use JWT. JWT terrifies me, and it terrifies all the crypto engineers I know. As a security standard, it is a series of own-goals foreseeable even 10 years ago based on the history of crypto standard vulnerabilities. Almost every application I've seen that uses JWT would be better off with simple bearer tokens.

JWT might be the one case in all of practical computing where you might be better off rolling your own crypto token standard than adopting the existing standard.




Most web frameworks I'm familiarized with have a concept of middleware, where you can perform any authentication checks before yielding.

Having read a bit into the topic, I'd +1 avoiding JWT. Getting authentication "right" is difficult. I think most applications should default to using stateful authentication. By the time you actually need stateless authentication "to scale", you'll hopefully have enough experts on-board to help you understand the tradeoffs.


Yeah, in my experience a lack of centralized authorization checks is one of the most sinister issues in typical API construction. And I've seen pretty wonky reasons (relatively speaking) for not wanting it ("it would take a lot of refactoring", or "that presents a single point of failure").

If you don't set up centralized auth checks and instead prescribe !!!CONSTANT VIGILANCE!!!, you're just setting yourself up for an auth bug in a hastily submitted pull request at 4 pm on a Friday afternoon, when someone is lethargic and ready to head out for the weekend. The code is going to get committed, then pushed to production after three people write a quick "LGTM!" Three months later a bug bounty is going to come in with a snazzy report for you (hopefully).

The better thing to do is 1) abstract all authorization checks to a central source of authority and 2) require the presence of this inheritance for tests to pass before deployment.


have the handlers of all endpoints inherit from a base class that performs the check automatically

I disagree. Much better to have a single endpoint which does nothing except validate opaque requests and passes them upstream. No good ever comes from having crypto code mixed up with non-crypto code.


I'm not talking about crypto --- really, it was an offhand comment about what real advice about structuring code for security looks like, compared to "validate inputs so you don't have XSS" --- and whatever you're proposing is probably something I'd agree with.


Do you have any further info on why you so strongly recommend against JWT? My MO has been to know and understand the standard, what it provides (e.g. signed assertions a la SAML, albeit easier on the eyes) and what it does not (e.g. encrypted body without adding your own JWE), and to use it accordingly.

This is probably the first I've heard from someone I know is more than just some random HN commenter that JWT is not recommended.



In a nutshell with my comments:

Benefits

- Easier to (horizontally) scale: that's true. My previous company shut down a few servers thanks to JWT

- If it has a vulnerability, just update to patch it ... instead of fixing your customized algorithm. It's way better, but you need to use the most popular / battle-tested / maintained library for your language and keep track of its CVEs as any another dependency.

Drawbacks

- Not revocable, but you can 1) make it short lived, 2) create a blacklist check in a key/store database or 3) tie another verification to it with the cost of a database call (https://dadario.com.br/revoking-json-web-tokens/). Regarding the article (part 2), when it says what would happen if your server is down .. seriously, it's way easier to anything but a key/store value of a few items to get down first than any other server

- Developers think that the data is encrypted, when it's only base64'd

- Libraries have to make up for the flawed specification that allows the JWT to carry both the algorithm used and the signature

- Libraries are not as battle-tested as cookies

- Libraries may support flawed algorithms (e.g., RSA with PKCS #1v1.5 padding - for JWE), thus you have to know what you're picking

Some points that I agree or partly agree with the author of such article:

- Easier to use: that's nonsense. I'd say that it's the same thing as implementing a cookie mechanism

- Works better on mobile: that's nonsense

- Works for users that block cookies: you can very well put your session token in the LocalStorage and achieve the same effect

- They take up more space: true

- Built-in expiration functionality: that's nonsense. Cookies have it as well.

- By storing it on LocalStorage you avoid CSRF, but you can do that with session tokens already. Regarding the links above that 'by using JavaScript you open up your application to a lot more risk' .. well, to build a page without JavaScript in 2017 is madness. Let's get back to reply with full HTML content again? Lol. Backing to the point, it's not really a benefit of JWT, but commonly related to tokens that you store on LocalStorage, even though they're subject to XSS, as opposed to cookies w/ HttpOnly flag -- and then we go again to discuss how this flag only passes a 'false sense of security' because if you have a XSS, you already have lots of trouble.

Topics that are complicated:

- Saying 'more secure' or 'less secure' depends on how it is implemented. You could have secure JWT implementations and flawed stateful session implementations.

- Data goes stale: depends on what data you put on it! Just putting the user's UUID is not something that is likely to change, except when the user is removed;


I generally agree with your conclusions, but I don't understand why you compare JWT to cookies. These technologies are completely orthogonal. JWT can be stored in cookies and whatever you put in traditional cookies can generally be stored in local storage.

The right comparison is JWT vs. session tokens stored in DB or KVS. Or in case you already decided against storing sessions in DB, you should compare JWT against rolling your own crypto.

With this approach, cookies should be thought more as a mechanism for storing and presenting session data, not as security mechanism. You can't rely on cookie expiry date for instance - if someone steals that cookie, they can completely disregard expiry, HttpOnly, Secure, Domain or whatever other property you stick to the cookie.

Cookie expiration is basically worthless. Whether you're storing your sessions in a database or cryptographically signing them you should always add your own expiration mechanism. I've seen too many systems that blindly relied on cookie expiration for security, only to realize the implications later.


> I generally agree with your conclusions, but I don't understand why you compare JWT to cookies.

You're right when it comes to terms. Allow me to clarify what I meant by Cookies and JWT in the explanation above:

I was referring to Cookies as the default storage for stateful session mechanism used by web frameworks that makes use of a random session ID with high entropy. It's the no-brainer approach to implement stateful sessions and (usually) doesn't require changes on the client-side but require you to store all sessions in a file/redis/db. And expiration that you pointed now makes sense, because I'm talking about the expiration of the session on the server-side, although Cookie has this mechanism which does little to prevent session hijacking.

JWT, on ther other hand, usually is stored on LocalStorage and requires some development changes on the JavaScript framework because it needs to read from LocalStorage, capture the JWT and send it in every request. With a web framework's default approach (that I used the term Cookies), it's seamless.


> Developers think that the data is encrypted, when it's only base64'd

Doesn't it depend on the specific implementation? For example I have been using github.com/dgrijalva/jwt-go package to build a token, add claims and sign it along with github.com/auth0/go-jwt-middleware to validate the requests. The JWT in that case is signed and encoded as a string using the secret.


jwt-go says right in the readme that it is not encrypted:

>> It's important to know that JWT does not provide encryption, which means anyone who has access to the token can read its contents. <<


Okay, let's say that 3rd party read the user id which my app keeps in a JWT token. What would they do with it?


> Works for users that block cookies: you can very well put your session token in the LocalStorage and achieve the same effect

AFAIK LocalStorage is disabled when cookies are disabled.


Here's something longer I wrote about JWT:

https://news.ycombinator.com/item?id=14292223

I really ought to just suck it up and write a blog post.


Is most of this specific to JWT and its format? Meaning if my token consists of a stateless "id" and an authenticated hash of it appended, and I have it passed as an HTTP auth bearer header, am I good? Assumptions being my authed hash algo is acceptable, my "id" value embeds a creation time that I expire in a few hours, and nothing can be gleaned from the "id" itself.


> I really ought to just suck it up and write a blog post.

Yes please!


>> Finally: don't use JWT. JWT terrifies me, and it terrifies all the crypto engineers I know. As a security standard, it is a series of own-goals foreseeable even 10 years ago based on the history of crypto standard vulnerabilities. Almost every application I've seen that uses JWT would be better off with simple bearer tokens.

This is really surprising to me. I use Play! framework and the whole play framework community suggests to use JWT for authentications as Play! doesn't support sessions out of the box. Is it just JWT itself is bad or how developers use it is bad? Just a noob question.


It is a standard for crypto created by non-crypto people. It is bad, don't use it. Using it correctly is harder than rolling your own stupid simple bearer token, which is very rare for standards. Using stateful authentication is even simpler. Using django or something like that is even simpler.


>JWT might be the one case in all of practical computing where you might be better off rolling your own crypto token standard than adopting the existing standard.

This isn't the first time I heard this claim, but I've also read that vulnerabilities were related to libraries and implementations, not the standard itself. Is it true?

To me, I don't see the benefit of passing meaningful information via JWT, and it might even pose a risk.

>simple bearer tokens

I guess you mean cryptographically secure random byte strings?


What are some more trusted alternatives to JWT for signed tokens with claims / expiry?


Depending on your situation, you've got only 3 reliable options, as far as I'm concerned.

1. You could just generate random session IDs (UUIDs or 128-bit base64 strings) and store them in your database or in a persistent cache like Redis. Most OAuth middleware offer this functionality already. Drawback: Scalability - but in most cases you don't need it.

2. Use an alternative format that doesn't provide all the features of JWT, but provides better security: Fernet or Macaroons. https://github.com/fernet/spec/blob/master/Spec.md https://github.com/rescrv/libmacaroons

You'll need to implement claim validation and expiry validation all by yourself.

Fernet is probably better for you if you don't need the killer feature of macaroon (stacking caveats). The payload can be anything, but if you really like JWT you can always stick a JSON-encoded JWT payload inside the token and use your favourite JWT library to verify it.

If you want to support use cases like delegation or claims verified by third parties, Macaroons are worth a look.

Drawbacks: - Limited programming language support. - No built in mechanism to support key rotation (like JWT header kid). You'll need to roll your own.

3. Roll your own crypto. This not as bad as it sounds, since you could (and should!) use the NaCl/libosodium primitives. The only difference between NaCl secretbox and Fernet is that the latter includes a timestamp - which you can easily add on your own.


> - No built in mechanism to support key rotation (like JWT header kid). You'll need to roll your own.

That's not true. Macaroons have identifier field. See the Readme doc in libmacaroons [0]. Direct quote:

> The public portion tells us which secret we used to create the macaroon, but doesn't give anyone else a clue as to the contents of the secret.

[0]: https://github.com/rescrv/libmacaroons/blob/master/README

I'd say that the biggest difference between JWT and Macaroons is that Macaroons are on one hand simpler than JWT (only one algorithm allowed) and on the other a lot more flexible. Caveats are just byte arrays and it's up to the user to decide how to verify them. The official docs present simple case of string predicates (user = Alice) but it'd be also possible to use something similar to Bitcoin Script. Of course this flexibility has a price: if using third party caveats (another unique aspect of Macaroons) all services must use the same caveat language.


Interesting, I didn't realize that. The JWT ecosystem (or JOSE ecosystem to be exact) offers a lot of other machinery beyond just having a key ID for key rotation like JWK and OpenID Connect discovery, but there's nothing preventing you from using the same discovery mechanisms with Macaroons. I'm using these mechanisms already with a variety of other non-JWT implementations.


Oh yes, exactly, JWT has a stronger ecosystem. Sometimes I feel like they are aimed at different problems, JWT to replace opaque tokens with stateless ones (but if you want instant revocation it becomes a problem) and Macaroons for delegated access. JWTs can be easily used to replace session tokens while Macaroons work best when you've got your entire architecture designed with them in mind.

Just recently I was thinking that it would be nice if my DNS provider used Macaroons for API access. If I want to limit Let's Encrypt's client's access to just _acme-challenge.example.com I could take the Macaroon from DNS provider that has complete access and limit it to "_acme-challenge" and TXT records only. What is nice with Macaroons is that you can derive sub-tokens offline, just from the master token. Wrapping JWTs in JWTs, while possible, leaves one with the base64-in-base64 matrioshka problem.


I think Kerberos [0] is the industry standard.

[0] https://en.wikipedia.org/wiki/Kerberos_%28protocol%29


no JWT but "simple bearer token" is not a good advice as I have no idea how to implement that. I'd rather use JWT with restricted crypto (make sure "noop" is not allowed).


In tptacek's other post from two months ago:

> For almost every use I've seen in the real world, JWT is drastic overkill; often it's just an gussied-up means of expressing a trivial bearer token, the kind that could be expressed securely with virtually no risk of implementation flaws simply by hexifying 20 bytes of urandom.

This is then to say "generate a random number, give it to the client, accept that same random number in the future as evidence of the client's authorization". A familiar form of that would be a session cookie whose content was generated by a cryptographic random number generator. The session cookie is an index into a database that indicates the properties and authorities that that particular session does or does not have.

Now I guess the reason people may like JWT is that they don't have to have a database or store of tokens that they're issued and what authority each one connotes, because they can verify the signatures on the JWT and then believe the payload. And one system can issue authorizations that another system can consume without direct communication between the two. I think these believing-the-payload properties are a part of what Thomas doesn't like.


On rare occasions there might be a good reason for stateless auth. I think stateless auth is overused (it's especially funny to see it used on sites that are otherwise dependent on HTTP cookies), but not intrinsically evil.

But there can be no reasonable argument for a standard conceived of in the last 10 years to allow users to deploy something for which the payload chooses the cryptographic interpretation of the payload. No application anyone on HN is deploying needs user-selectable cryptography. This is never a feature; it's only ever an invitation to horrible vulnerabilities.


> But there can be no reasonable argument for a standard conceived of in the last 10 years to allow users to deploy something for which the payload chooses the cryptographic interpretation of the payload. No application anyone on HN is deploying needs user-selectable cryptography. This is never a feature; it's only ever an invitation to horrible vulnerabilities.

When I read about JWT's I saw the alg fields to a simple indicator of the algorithm being used on the JWT, not that it is allowing the token to select whatever algorithm it wants for the server to run. Granted, this is a semantic difference, but if you treat the alg field as such it then becomes the servers choice of what algorithms to support. The practical solution is simple: Only support one algorithm, and if the token's alg does not match what the server is expecting, do not authorize. Sure this is a weakness in the JWT spec, but the real underlying issue is dev's not understanding the security mechanisms and libraries they are deploying. That is bad news no matter what tech they are using.


If you leave the `alg` field out, the exact same scenario occurs: someone sends you a token, and you fail to authorize using the server-side configured algorithm. Having this field adds nothing of real value.

The only thing having an `alg` field does is make the standard trivially misusable by well-intentioned developers.


Well, practically every JWT library developer thought otherwise, because they'll all verify the JWT based on the alg field, which means every careful implementation of JWT must validate "alg", but I'm afraid there are too many developers out there who don't.

Realistically speaking, it looks like JWT won the popularity race and IETF unfortunately won't deprecate the algorithm header anytime soon, so we should at least try to campaign library maintainers to have the algorithm field ignored by default and use the algorithm specified by client code instead.


I feel about this the way I imagine an internal medicine doctor feels when a patient starts earnestly discussing colloidal silver.


> make sure "noop" is not allowed

Security by blacklisting is a bad idea. You don't need to look far - it's JWT libraries that could be fooled into accepting public key as a symmetric key [0] so even if you fix the noop bug you are still vulnerable. That's what's wrong with JWT - you always have one more issue than you think.

[0]: https://auth0.com/blog/critical-vulnerabilities-in-json-web-...




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: