Hacker Newsnew | past | comments | ask | show | jobs | submit | RagingCactus's commentslogin

> Necessary qualifier: for browser-based user sessions.

> Plenty of good uses for JWTs for service-to-service communication.

This is the sensible conclusion right there. I agree JWTs are the wrong tool for the use case of user sessions in the browser.

To give some more arguments:

All the signature and encryption stuff in JWTs is complex. While common JWT libraries have now mostly got their stuff together, this has not always been the case. There were plenty of libraries accepting the "none" algorithm [1] or allowing attackers to forge tokens by using a public key as a shared secret [2]. This is the direct result of the complexity criticized in the linked blog post.

JWTs also cannot do some stuff you want for user sessions. You can't invalidate them without keeping a revocation list somewhere. But if you have to check an identifier for revocation on every request you could just use an opaque session ID and look that up on every request instead! Sure, you can use short-lived tokens and refresh them all the time, but why bother with that for a typical application that has to keep some state anyway?

All that being said, I wholeheartedly agree that there are use cases in distributed systems and machine-to-machine communication where signed tokens can be useful. Just please don't confuse the two cases.

[1] https://nvd.nist.gov/vuln/detail/cve-2022-23540

[2] https://nvd.nist.gov/vuln/detail/CVE-2024-54150 (just a random example from googling, I don't know what library made this one infamous)


> if you have to check an identifier for revocation on every request you could just use an opaque session ID and look that up on every request instead!

One reason could be the size. A revocation list only needs to keep session IDs of recently logged-out sessions, for which the token's TTL hasn't yet expired. It may be a much smaller list than a list of every active session.

Also, a JWT (or a Macaroon, etc) can store a large amount of details about the session in a cryptographically secure, unforgeable way. This rids you of the necessity to store all that in your active session database, again cutting the size.


As someone who operates a PostgreSQL database containing 27 billion SSL certificates, each 1-2kb each, with a bunch of secondary indexes that get inserted in random order, I find it pretty incredible that people see the need to optimize their session database. At what scale does the size of the session database actually matter?

Those stateless tokens may be "unforgeable", but they are replayable, and if you're not mindful of that you can have security vulnerabilities.


I think one meaningful case is when you have services in very different locations and you would rather than having to make a request to a session store in a single location, replicate the data to each location for better latency, so in this case a revocation list.

You should do some basic optimizations. Fixed length table and indexes on the unique string for fast lookups. I also like to do a rolling delete for old sessions after 30 days unless mobile session that is logged in. Those get to live forever.

Fair enough, but those optimizations are basically free. People think stateless tokens are free but they really are not.

The cost of the stateless token is basically the CPU usage for signing the message and checking the signature with the public key on the client. Example: Google Compute Instance asks metadata server for OIDC token (which is a JWT). The metadata server respond with the token that basically says "here's the machine service account, here's the machines ID, this token is proof that I am service account abc123 and it's valid for 20 seconds". This is one of the most common uses of JWTs in enterprise. You don't store them. They actually are free.

Lots of web devs get tricked into using them as primary session tokens and it's a huge anti pattern. I see it all the time and people get aggressive about it.


The cost is the vigilance required to use them safely. It's not just compute/storage costs.

I didn't downvote you. You're absolutely right. Implementation of anything is work.

> Fair enough, but those optimizations are basically free. People think stateless tokens are free but they really are not.

Strawman.

The only requirement for a JWT is posting the JSON Web Key set with the public keys used to verify the JWTs signature. That's the full cost of a no-frills JWT implementation of you exclude IAM.

If you want to have one-time JWTs you need to maintain a revocation list. This is literally a set of IDs. If you go nuts and use GUIs for JWT IDs that means each entry takes as much space as 4 ints, and all you need is a set membership check on said integer. Even at FANG scale you can handle that scale in a memory cache service such as ValKey running on a COTS desktop.

Now show us your alternative.


Yes we have heard this before, React is only 30kb! But that misses the enormous amount of infra you need to even just do a basic fetch. (Read the post by the React Query author on whether you need React Query or not)

Likewise with JWTs for sessions you need to handle cache invalidation, revocation lists, key rotation, the list of difficult comp sci problems really does go on!

The same issue as always plaguing the frontend world. Up front “simplicity”, enormous actual complexity


> If you want to have one-time JWTs you need to maintain a revocation list.

No, you always need a revocation list if you want to handle user sessions in a secure manner. What claims do your tokens contain? If it's anything other than some stable identifiers, like user name, email, permissions, etc. then you now have a cache invalidation problem.

But if all your token carries is an identifier which you need to look up, how's this any better than a signed cookie containing the session ID? All you've done is add complexity.


What do you do about availability? AFAIK the choice is to pick one of 3 or 4 hacky difficult-to-administer clustering solutions, or have that single PostgreSQL database be a SPOF for your whole system.

27 billion? Do you work at AWS?

The issue isn't size, it's load.

I am still waiting for Macaroons to be used widely. I think they are a fantastic invention.

It seems they were not of very much use in the past, but with the agentic-everything now, I see this as a great way of delegating permissions to subagents, third-party agents, etc.

Working on something along these lines but unfortunately I cannot dedicate as much time as I'd like.

Still, if anyone is reading, give Macaroons a try!


We have what I believe to be one of the world's largest deployments of Macaroons. They're a mixed bag, though I think they're a lot more interesting in a world where agents do most of the fiddly work.

https://fly.io/blog/operationalizing-macaroons/


I am very aware of your work!

It's the only prod usage of Macaroons I know of, I think.

Third-party discharge seems like a great way to have human-in-the-loop gating, among other interesting things.

Would be great reading your thoughts if you ever write about the agentic use case, having all the fly.io experience


I like the raspberry ones. Or lemon is also good

JWTs can do that (delegate) and such capability is already well defined.

Maybe I stated it wrong. Macaroons have the ability to attenuate the restrictions _without_ contacting the auth server, which makes it IMO fit for restricting and attenuating as much as you want, without much cost.

If I need a roundtrip to the auth server to attenuate, I am not necessarily going to do it as often.


Most token formats delegate. Macaroons support attenuation, confinement, and embedded third-party claims, none of which are JWT capabilities.

> While common JWT libraries have now mostly got their stuff together, this has not always been the case. There were plenty of libraries accepting the "none" algorithm [1] or allowing attackers to forge tokens by using a public key as a shared secret [2]. This is the direct result of the complexity criticized in the linked blog post.

I'm a bit surprised at this. These are extremely simple to solve - the first time I ever did a JWT-reading implementation I specified the right defaults, which are very simple, even for a mid-level backend person I would say, and they haven't needed changing in 8 years or whatever it's been. It really isn't very complex.


You would think so, but even an authentication company screwed it up:

https://cybercx.co.nz/blog/json-web-token-validation-bypass-...


Clearly trying to be too general. I wrote a tiny JWT validator before that only allows a very small subset of algorithms because I wasn't expecting the JWTs it would handle to have anything else, and obviously not "none".

Wow lol

The people who are upset about JWTs probably got burned by trying to use them in a weird way. Some people try to store sensitive data inside JWTs... WTF, the idea would never have entered my mind! Sensitive data should stay on your server. Encrypted or not! JWTs are supposed to be signed, not encrypted! You shouldn't even think to put sensitive info in there. Also, WTF is wrong with people who accepted algorithm "none." Most of these people who tried to tweak the defaults had no idea what they were doing in the first place.

> Also, WTF is wrong with people who accepted algorithm "none."

They dared to use the default validation function of their JWT library. They did not choose to accept "none".

And the library authors implemented it because it's in the spec. It doesn't excuse that the default was to accept "none", but it is an explanation and in my opinion a valid critique of the standard.


You could have a SQL library that defaults to inserting "OR 1=1" to every update query, which the SQL standard allows. I would blame the library.

Yeah well it's definitely a footgun built into the spec but ultimately an library implementation mistake.

The design I've landed over the years is to use both. The cookie is a session token and that's where you handle refresh tokens. Then there's an endpoint where you can mint a short-lived tenant-sepecific JWT. This holds the scopes & tenant id. The session token only lets you access the web assets & mint JWT tokens.

I think both you and GP are somewhat misrepresenting the OP is saying. OP's argument is three-fold:

1. JWTs are not a good fit for a session token (although there are several RFCs that are trying to shoe-horn JWTs into this use).

> TLDR: JWTs should not be used for keeping your user logged in. They are not designed for this purpose, they are not secure, and there is a much better tool which is designed for it: regular cookie sessions.

2. JWTs have other "valid" use cases that only need a very short-lived token (e.g. a transit token or a request signature) and don't need to care about user authentication, revocation, XSS etc.

3. But JWT should not be used even for the "valid" use cases, since you have better (read: less outrageously insecure) alternatives nowadays.

> Also note that "valid" usecases for JWTs at the end of the video can also be easily handled by other, better, and more secure tools. Specifically, PASETO

You've noted these issues yourself. There are many common vulnerabilities with JWT: alg=none, algorithm confusion and weak key brute-forcing, mandating weaker algorithms like RSA and ECDSA while making the best, fastest and easiest to implement algorithms like EdDSA "optional".

There are also other design deficiencies that JWT makes by trying to be a generic cryptographic envelope format rather than a token format: e.g. expiration can be omitted and this feature that caused some libraries to not verify expiration by default or have a different (and confusing) set of token parsing methods that do not enforce the expiry. PASETO is a better design that is secure against all of these issues. Sure, there are a few minor qualms I can find with PASETO (e.g. no mandatory key ID and no support for non-JSON payloads), but it's unlikely to face the same avalanche of CVEs we got with JWT libraries.


A revocation list defeats the purpose of JWTs. If you find yourself needing one, JWTs were probably the wrong choice to begin with.

Not really. Being able to verify a user's signed identity immediately without having to do any database or memory store lookup first is valuable in itself.

With session IDs, anyone can spam/DDoS your system much more easily; they don't even need to be authenticated to waste your computing resources as they can send plausible-looking session IDs and your system will waste a ton of resources querying your session store or database to figure out that the session doesn't exist... It adds a ton of latency and wastes CPU cycles across multiple systems. Also stateful systems like Redis are harder and more expensive to scale than stateless systems like application servers. Not to mention that they may be depended upon by other parts of the application so hitting those too hard can be more disruptive. And that's kind of best-case. Some people use a database to store their session data...

At least with a revocation list with JWT. If the JWT says that the user is user1234, then you know that this is a real, previously logged-in user, they have an account at stake, you can afford to spend a bit of time/resources to check them against your revocation list... And if they are on that list, you can ban their ass and they're done! They'd have to create a new account of they try to spam you again. They can't spam without a valid JWT and they can only get that by authenticating with your server first.

Sure with JWT, some computation is spent on verifying the signature but it's very cheap using the default algorithm and only touches one system... And you only need to call another system once you know that the user is valid.

JWT is much more robust for DDoS prevention because of this. But yeah, you don't necessarily need a revocation list. You can use short JWT expiries with frequent token refreshes. Revocation list is good if you need immediate ban.


Can't a system be DDoS'ed with wrongly signed JWTs as well?

Is signature checking (much) cheaper than finding an opaque session ID in a database?


Yes but it only impacts your stateless app servers which are easier to scale. Your backend services/stores are protected and not affected by the attack.

> All the signature and encryption stuff in JWTs is complex. While common JWT libraries have now mostly got their stuff together, this has not always been the case.

This is a red herring. Applied cryptography was never considered an easy subject in software engineering circles. Neither was algorithms and data structures. Yet, it's still a basic tool, and developers are still expected to understand things such as why some maps allow reads in constant time while others require log time.

Some libraries being buggy never was an argument against using libraries. And do you expect your single-purpose code not to be?

I think we are seeing in this thread a knee-jerk reaction against perceived complexity. Yet, if you sit down and compare JWTs with alternatives and list all features along with pros and cons, you'd be hard-pressed to even try to put together a case for JWTs being bad.


> Some libraries being buggy never was an argument against using libraries. And do you expect your single-purpose code not to be?

Of course you should use battle-tested and well-maintained libraries for the really hard stuff such as cryptography primitives. However, that is not the point I was trying to make.

My points here are:

1) If you can get away with not using cryptography for something, you probably should. Your web framework already supports session cookies. Even if it doesn't, it's very hard to mess up opaque tokens from SecureRandom or /dev/urandom and a corresponding database lookup.

2) If you actually need the things JWTs can do, the standard is still needlessly complex and easy to mess up in ways that are not inherent to the problems JWTs are trying to solve. I'm not saying this means you should roll your own solution (again, I agree that there is value in well-tested libraries), I'm further strengthening point 1) with this. Don't use JWTs if you don't need to


Come on, it’s not like the two are even within the same magnitude or three

“But if you have to check an identifier for revocation on every request you could just use an opaque session ID and look that up on every request instead!”


If you don't understand conceptually how to verify a signature with a public key the very first thing you should do is get that working and then work from there. It's completely unacceptable to ship without this.


WTF:

> Each user has a secret: Stored securely in the database.

> Stateless Validation: The core validation remains stateless. We only need to consult the database for the user's secret, which we'd likely do anyway for authorization checks.

Is "stateless" the same as "serverless" now? Is author's brain stateless?


A JWT is usually signed, with a secret you keep in your app. The statelessness of JWT is that it contains all the information you need to verify it. You do not need to ask a db if the token is there and valid.

Storing a user's secret, the same way you store your applications secret does not make it more or less stateless.

In since you now have 2 layers of protection, you don't actually need to verify agains a user's secret immediately, you simply need to check that the token is valid using the app secret. The subset of valid tokens that you need to check is much smaller than the universe of all the unexpired tokens your application has issued.

If you have a security incident and need to revoke tokens for only a subset of your users, now you don't need to rotate your app secret and invalidate every single token and break every single session. You can simply log those users out.

Is author's brain stateless -- my bad, I thought this was not reddit


> [...] you don't actually need to verify agains a user's secret immediately, you simply need to check that the token is valid using the app secret. The subset of valid tokens that you need to check is much smaller than the universe of all the unexpired tokens your application has issued.

What you are describing here is different than what is described in the blog post that you linked to.

Please look at the definition of the function 'validateToken'. In particular, notice how 'getUser' function (which the author notes issues a DB query) is called for every JWT with a valid signature!

EDIT: I failed to realize that you are the author of the blog post. Still my point stands, in that your description doesn't match what the code does.


> Common workarounds like maintaining a blacklist of revoked tokens introduce statefulness, negating the benefits of JWTs.

> Validation: On each request, we validate the JWT's signature using the application secret and then validate the sjti using the user's secret.

Having to lookup the user secret from the db is no different than consulting a list of revoked tokens. You claim consulting a list of revoked tokens to be stateful. How is looking up the user secret different?


Being able to quickly reject invalid sessions identifiers is a useful property in some cases and is normally done by authenticating stateful session tokens with a MAC using a global app key. This can be used for DoS protection if the cost of a database lookup is more than the cost of MAC, and the complexity is justified. It ensures that the random numbers a user is trying to present as their session token are the numbers generated by the server if the key is not leaked.

Because you're trying to bolt things on top of JWT, you're creating a worse version of that stateful authentication pattern:

1. You lost the statelessness of JWT by making database queries. Your claim that "you don't need to verify against user's secret immediately" is false, as you need to do that in all cases immediately after verifying the JWT signature to get the benefits of your system (token invalidation). Sure, you reject completely invalid tokens early, but you still need the statefulness to authenticate users properly (if your goal is to be able to invalidate tokens).

2. In your version, getting a read-only access to the user database (leaking per-user secrets) completely destroys token invalidation, and all your authentication now depends on one key. If, in addition to that, the JWT signing key leaks, user authentication is completely destroyed and can be bypassed by the attacker, who now can sign in as any user. (A common way to leak all this is by failing to properly secure backups).

Compared to a stateful session system with split-tokens, where the database stores tokenId => verifier, where verifier is Hash(randomToken), and user's token is id||randomToken, read-only access to the database doesn't let the attacker authenticate as any user. If the tokens that users presents are in the form of id||randomToken||HMAC(serverKey, id||randomToken) for early rejection as above, leaking serverKey still won't allow the attacker to authenticate as any user. The attacker needs write access.

> Is author's brain stateless -- my bad, I thought this was not reddit

I didn't realize that you were the author, I thought you were a reader who was misled by this blog post. Even better: you can go and edit it, removing "stateless" everywhere! It's fun to invent various protocols, but when someone points out the errors, surely you'd want to fix them -- no shame in making mistakes if you correct them.

Usually, when I think of a protocol, after writing down "Benefits" (as in your blog post), I write "Drawbacks" and then try to come up with downsides and compare it with existing protocols. I'd suggest you do the same.

PS. Find yourself in this picture: http://cryto.net/%7Ejoepie91/blog/2016/06/19/stop-using-jwt-...


If I need a database query to validate the token, it's not stateless.

> First, we need to add a token_secret column to our users table:

> ALTER TABLE users ADD COLUMN token_secret;

So it's "stateless" but we have to query the users database on every request? How is that more stateless than SELECT * FROM session WHERE id = cookie?

Ignoring that and taking the mechanism as given: Why the obsession with cryptography, in this case HMAC? I don't see any reason why another signature is needed here when I believe the same outcome could be accomplished with a token_epoch field in both the signed JWT and the users table. Just increment the epoch to revome old tokens. Or even better, drop the epoch field and have an iat_not_before field per user. The field in the JWT is signed, the whole point is that you can trust it.

Do let me know if I miss anything here please. Assuming I haven't: it's always puzzling to me to see people being so eager to sprinkle more cryptography on anything that is supposed to be secure. For me, I've become more afraid of cryptography the more I learned about it. Cryptography is hard. It's not a magic ingredient for security. At best, it's dangerous black magic -- very potent, but pronounce a single syllable of your magic spell wrong and it _will_ blow up in your face.


You don't actually have to do a db trip to get a user secret and revoke a token. A token comes in, and you can store the secret in the same place you store your application secret. Because you do need to store it, cache it, whatever. The point here is you no longer need to keep a revocation database of every token you issued that is still unexpired. Just rotate the signing secret and every token issued until then will be revoked. Goes from maintaining millions of tokens to maintaining a smaller cache of user secrets that are probably rarely updated.

Why not an epoch? because this gives control to the user. They can now logout regardless of token ttl. The point is not obsessing over crypto, JWTs are a cryptographic solution, it's what makes them stateless and I have nothing agains cookies or any other session token. I use them interchangeably.

My pain point was that whenever I needed to use a JWT or whenever I worked a company that used JWTs, their main frustration was "oh but then we can't revoke them easily without maintaining a revocation list". Well now they don't have to.

Telling them just migrate to "this or that technology" is not how this works.


What? So instead of storing a revocation list, you store a per user secret that you need to consult. What is the difference? How is that stateless? How does it avoid a “rb round trip” (where are you storing the user secret)?

Wouldn’t it be simpler to use a session token? This complex machinery does nothing but look fancy.

The application secret is redundant if the per-user secret is used.

Also I’m inferring from the article that the author is using symmetric keys (HS256) for their JWTs. In what world can you securely distribute symmetric keys but can’t use an opaque session token?


"We only need to consult the database for the user's secret..." , which kinda defeats the purpose.

As a security person it is tiring to see so many people here either directly claim or at least allude to the claim that this is somehow much less scary because the _published_ exploit does not bypass ASLR. The writeup claims there is a way to reliably bypass ASLR with this attack. And that is a good default assumption I would be willing to believe without evidence.

ASLR is a defense-in-depth technique intended to make exploitation more difficult. In almost all cases it is only a matter of time and skill to also include an ASLR bypass. Both requirements continue being lowered by LLM agents every few weeks. It is only a matter of time (and probably not a lot of time) until a fully weaponized exploit is developed. It may be published, it may also be kept private.

It is straight up wrong to say "if you have ASLR enabled, you're not at any risk from this" and saying this is extremely harmful for anyone that trusts claims like that.

This wrong belief that you shouldn't care about security vulnerabilities because mitigations may make exploitation more difficult has already caused so much harm in the past. Be glad that modern mitigations exist, but patch your stuff asap. If you are a vendor, do not treat vulnerability reports as invalid because the researcher has not provided an ASLR bypass. Fix the root cause and hope mitigations buy you enough time to patch before you get owned.


No remotely reachable vuln should be taken lightly.

At the moment though, the preconditions look odd. I've been using nginx in various constellations for 10 years and never once combined rewrite and set.


There can be situations where you set some variables on top level and then override those in the location block with rewrite. These variables could be then used e.g. in log lines or in other "global" contexts.

Not extremely common, but it does happen.


I think some people's comments are misinterpreted as well. When people say "the PoC requires ASLR to be disabled" that doesn't necessarily mean the exploit is useless, but it does mean that the risk of automated exploit bots downloading the PoC and pwning random servers is reduced for now.

It's a matter of time before this exploit is chained with an ASLR bypass, but it allows for a slightly wider patch window at the very least.


> ASLR is a defense-in-depth technique intended to make exploitation more difficult. In almost all cases it is only a matter of time and skill to also include an ASLR bypass. Both requirements continue being lowered by LLM agents every few weeks. It is only a matter of time (and probably not a lot of time) until a fully weaponized exploit is developed. It may be published, it may also be kept private.

I disagree with this take, or I would at least phrase it differently. ASLR is like an extra password you need to guess. It has certain amount of entropy and it is usually stable. Unless vulnerability has a portion that leaks information, ASLR completely mitigates it - or you need a second vulnerability. And that is a different conversation. ASLR can completely mitigate individual vulnerability, but not possibly exploit chain.

I would use the argument of possible second vulnerability that leaks information for making people patch quickly anyway. But exploit chains are risk for all kinds of vulns.


Information leaks are not uncommon at all. nginx seems like a good target for them as well (fork + exec == no re-randomize, so you have the ability to reexec your exploit a lot of times to improve stability). edit: Seems that there's already good work in this area, I kinda forgot about brop gosh I'm old https://www.scs.stanford.edu/brop/

I suppose to keep the password analogy together, people reuse passwords all the time, timing attacks exist, etc?


For this particular bug, for that to apply, you need some sort of oracle which tells that you are actually in the same child process that skips re-randomization before you can reduce the entropy. Based on this post, I cannot see that there is stable oracle to tell that?


I'm not making a claim about this bug, I'm saying that oracles and leaks are common and that nginx seems like a good target for them.


The idea is that ASLR bypasses are comparatively cheap, so yes, a chain without this is useless, but it's not that hard to find one. Probably much easier than the bug described here.


Anyone even vaguely familiar with exploiting nginx knows that ASLR is a complete non-factor here.


Could you explain for those who are not? :)


Bold claim without any provided source. Do you have a link to back up that ASLR is a complete non-factor?


I'm deliberately refraining from giving a ready LLM prompt.

History shows that "meh, ASLR mitigates this" is a vastly bolder claim anyway, so I don't feel much need to defend my position here.

Edit: Even the authors of this poc seem to agree with me https://depthfirst.com/research/nginx-rift-achieving-nginx-r...


> History shows that "meh, ASLR mitigates this" is a vastly bolder claim anyway, so I don't feel much need to defend my position here.

Obviously you need to defend, that is quite generalization there. You need to prove how the vulnerability itself reduces the entropy of ASLR.

The authors don't really give support for that. They just say that they can brute-force it without crashing the whole Nginx. But they don't say how the entropy is reduced. They have zero information where the child process even starts, whether they hit the child, or if it even is the same child. So you should provide us technical and precise reasoning why it is not mitigating?


There are heaps of literature on this exact topic. https://www.researchgate.net/publication/292156221_How_to_Ma...

> You need to prove how the vulnerability itself reduces the entropy of ASLR

Not really? Looks like we have a controlled-length overflow on a fork-based server, a situation where ASLR is known to not be very useful.


> Not really? Looks like we have a controlled-length overflow on a fork-based server, a situation where ASLR is known to not be very useful.

It does not work like that - it has certain pre-condition requirements. You also need a reliable oracle which tells information when you actually hit the child process, whether child crashes and whether you are even in the same child. When you can retrieve this information, you are then removing re-randomization between attempts. That reduces the entropy, but it only helps if remaining search space is small enough. They don't show that they have oracle.

Additionally, for RCE, you need to find libc base and that is randomized alone. Authors just ignored in the post how they got that address. For that, you most likely need the information leak from second vulnerability, even if you can brute force the actual vulnerability.


>It does not work like that - it has certain pre-condition requirements. You also need a reliable oracle which tells information when you actually hit the child process, whether child crashes and whether you are even in the same child. When you can retrieve this information, you are then removing re-randomization between attempts. That reduces the entropy, but it only helps if remaining search space is small enough. They don't show that they have oracle.

Why are you assuming there's any re-randomization going on? There isn't. That's a proposed mitigation to address this known problem with fork-based servers.


I'm with you, once an RCE is known, it's usually just a matter of time before it gets script-kiddied and easy to run. Don't put yourself through the pain, just upgrade nginx.

I just finished upgrading a weird embedded box that required compiling a static nginx binary and moving it over. It's more annoying than apt update;apt upgrade or whatever your OS distribution needs, but it's still not that hard.


So the PoC works on MIPS out of the box. Tons of Linux/MIPS running around (Loongson64 seems to have KASLR on Linux).


Don't forget all the cheap WiFi routers with MT7688/MT7628/etc MIPS chips.


I was specifically thinking about the Edgerouter Lite running EdgeOS (Cavium SoC), but I also know some IP cameras do, and indeed some WAPs. For example, UniFi UAP-AC Lite and Pro variants. Each of these devices runs nginx for management (I suppose it could be disabled, each runs a variant of a SSH server as well). Worse, something like Shodan can find some of that on the internet.


yeah when I read these RCE reports about public-facing software that I know about I usually upgrade them within minutes of reading the report that's why I read these reports and you really have to take them seriously because otherwise your machine gets compromised, sooner rather than later... it seems like lately there's been no advance notice on a lot of these RCE exploits that are publicly released, I mean come on guys at least give us a few minutes to upgrade our software before releasing the exploit, it feels like the late 1980s early 1990s when there was no guardrails on disclosure, i.e. all the remotely exploitable sendmail bugs. people who fail to read these reports or read them too late wind up having millions of machines being compromised because of it. currently nginx has about a 39% - 43% share of the public facing web server market today, so its pretty serious.


> and saying this is extremely harmful for anyone that trusts claims like that.

Kind of feels like the burden is on the one who is reading it though, good luck stopping people from spreading misinformation on the internet, most of them don't even know they're wrong.

What's extremely harmful is trusting random internet comments stating stuff confidently. Get good at seeing through that, and it'll serve you well in security and beyond.


> It is straight up wrong to say "if you have ASLR enabled, you're not at any risk from this" and saying this is extremely harmful for anyone that trusts claims like that.

You can safely assume a 1:1 overlap between the people that claim "AI will solve cyber" (and they always say 'cyber') and the people saying this.


Seeing the confusion in the comments I want to provide some examples of situations where this might come up in a security or CTF context:

* You have a restricted shell or other way to execute a restricted set of commands or binaries, often with arbitrary parameters. You can use GTFOBins in interesting ways to read files, write files, or even execute commands and ultimately break out of your restricted context into a shell.

* Someone allowed sudo access or set the SUID bit on a GTFOBin. Using these tricks, you may be able to read or write sensitive files or execute privileged commands in a way the person configuring sudo did not know about.


This is pretty relevant for things like claude-code, which has a fairly rudimentary way of dealing with permissions with block-lists and allow-lists.

I once accidentally gave my claude "powershell" permissions in one session, and after that any time it found it was blocked from using a tool, e.g. git, it would write a powershell script that did the same thing and execute the script to work around the blocked permission.

Obviously no sane system would have "powershell" in a generic allow-list, but you could imagine some discrepancy in allowed levels between tools which can be worked around with the techniques on this page.


Power Shell or Python scripts to work around restrictions are the go to for LLMs.

And it doesn't stop there.

Yesterday I was trying to figure out some icons issue in KDE plasma (I know nothing about KDE). Both Claude and Codex would run complex bus and debug queries and write and execute QML scripts with more and more tools thrown into the mix.

There's no way to properly block them with just allow- and block lists


> There's no way to properly block them with just allow- and block lists

Especially not when some harnesses rely on the reliability of the LLM to determine what's allowed or not, pretty much "You shouldn't do thing X" and then asking the LLM to itself evaluate if it should be able to do it or not when it comes up. Bananas.

Only right and productive way to run an agent on your computer is by isolating it properly somehow then running it with "--sandbox danger-full-access --dangerously-bypass-approvals-and-sandbox" or whatever, I myself use docker containers, but there are lots of solutions out there.


You have to be extremely careful when you set up a dev container, lock down file access, do not give the agent the power to start other containers or "docker compose up", restrict network access to an allow-list etc. Just running the agent in a container does little to protect you. (Maybe you know this, but a lot of people don't!)


Most of those things are what happens by default. Sure, be careful, but by default it's secure enough to prevent most potential issues. No need to lock down file access for example, by default it only has access to files inside the container, and of course by default containers don't have access to start other containers, and so on.

Good word of caution though, make sure you actually isolate when you set out to isolate something :)


I've just discovered and started using smolmachines^1 which actually have the requisite isolation.

1. https://smolmachines.com


As mentioned, "podman/docker run -it $my-image codex" also actually has the requisite isolation by default, no need for special software. Biggest risk is accidental deletion of stuff, easily solved without running an entire VM, which "smol" machines seems to be. No doubt VMs have their uses too, but for simple isolation like this I personally rather use already existing tooling.


Ok, YMMV, but a smolvm provides macOS-native, per-workload isolation -- vs trad container depending on a daemon and relying on namespaces (w/ a shared kernel). Easy "packing" into single-file executables, and a nice SDK, make it ~ideal for my needs; great balance of security:convenience.

https://smolmachines.com/#comparison


Cool ad bro, but stop claiming container won't get you "per workload isolation" just because they share kernels, in the context of this discussion it hardly matters, containers isolates enough for this.


ad? I have no affiliation w smolmachines, just glad I found it.


In a previous employer, they block the chmod command. I took the habit to python -c "import os; os.chmod('my_file',744)".

Glad to see LLM re-discover this trick.


> to see LLM re-discover

I imagine someone probably wrote very specifically about it in the training data that underwent lossy compression, and the LLM is decompressing that how-to.

So I'd say it's more like "surfacing" or "retrieving" than "re-discovering".


They scraped everything on Stackoverflow, likely IRC logs from Freenode, and every book written in the modern era courtesy of Sci-Hub / Library Genesis / Anna's Archive / Z Library.

RIP Aaron Swartz, they're generating trillions in shareholder value from the spiritual successors to the work they were going to imprison you for.


Indeed, I check and the solution was already on stack overflow https://askubuntu.com/a/1483248


For the LLM it's a probabilistic set of strings that achieves the outcome, the highest probability set didn't work, try the next one until success or threshold met. A human sees the implicit difference between the obvious thing not working indicating someone doesn't want you to do it, but an LLM unless guided doesn't seen that sub-text.

So chmod +x file didn't work, now try python -c "import os; os.chmod('file',744)"


Humans and LLMs both only see that when given the right context. A tool not working in a corporate environment may be anything from oversight, malfunction all the way to security block. Knowing which one it is takes a lot of implicit knowledge. Most people fail to provide this level of context to their LLMs and then wonder why they act so generic. But they are trained to act in the most generic way unless given context that would deviate from it.


> * Someone allowed sudo access or set the SUID bit on a GTFOBin. Using these tricks, you may be able to read or write sensitive files or execute privileged commands in a way the person configuring sudo did not know about.

Some enterprise security software that is designed to "mediate privilege elevation" includes an allowlist configured by the administrators. My experience seeing this rolled out at one company was that software on the allowlist no longer required a password to run with `sudo`. The allowlist initially included, of course, all kinds of broadly useful software that made its way onto this list (e.g., vim, bash).

I worked from home at this company, and I remember thinking it was a good thing, because this software deployed to "secure" my computer made it drastically weaker to someone walking up to it and trying to run something if I stepped away from the keyboard for a moment and forgot to lock it.


It's kind of a comprehensive guide to all the ways that restricted shells don't.


And here I thought this is a curated list so AI can learn how to bypass sandboxes.


Concrete example:

A few years back, our support team needed to do some network capture with tcpdump. The quick and natural way to allow that was to add a sudo rule for it, with opened arguments (I know it's a bit risky, but tcp port and nic could change).

Looks good enough? Well no...

With tcpdump, you can specify a compress command with the "-z" option. But nothing prevents you from running a "special" compress command and completely take over the server:

> sudo tcpdump -i any -z '/home/despicable_me/evil_cmd.sh' -w /tmp/dontcare.pcap -G 1 -Z root

This seems trivial, but that the kind of stuff which are really easy to miss. Even if these days, security layers like apparmor mitigate this risk (causing a few headaches along the way), it's still relatively easy to mess it up.


Specifically for these kind of situations, sudo has the NOEXEC tag: it preloads a dummy library that null-routes all exec calls to prevent this kind of shell leak.


Literally living off the land yes


Lots of people here are (perhaps rightfully) pointing to the unwrap() call being an issue. That might be true, but to me the fact that a reasonably "clean" panic at a defined line of code was not quickly picked up in any error monitoring system sounds just as important to investigate.

Assuming something similar to Sentry would be in use, it should clearly pick up the many process crashes that start occurring right as the downtime starts. And the well defined clean crashes should in theory also stand out against all the random errors that start occuring all over the system as it begins to go down, precisely because it's always failing at the exact same point.


Exactly! You could have `rand() > 0.5 && panic!()` in the code of your bot module, and that should not put the internet on fire.

The issue here is about the system as a whole not any line of code.


> The issue here is about the system as a whole not any line of code.

Unsoundness in the type system that leads to a systemic failure is about the system as a whole.

Not everything can be recovered from restarting a process, and process correctness and recovery is something that also derives from your type system.


In the early 2000s when Google explained how they achieved their (already back then) awesome reliability, ie assuming that any software and hardware will eventually fail, and that they designed everything with the idea that everything was faulty, there were some people who couldn't get it, who would still bring the argument that "yeah but today with modern raid..."

People here chatting about unwrap remind me of them :)


Assuming software and people will fail is exactly what not using unwrap is about.

If you depend on engineers not fucking up, you will fail. Using unwrap is assuming humans won’t get human-enforced invariants wrong. They will. They did here.

As someone that works in formal verification of crypto systems, watching people like yourself advocate for hope-and-prayer development methodology is astonishing.

However, I understand why we’re still having this debate. It’s the same debate that’s been occurring for the same reasons for decades.

Doing things correctly is mentally more difficult, and so people jump through ridiculous rhetorical hoops to justify why they will not — or quite often, mentally cannot — perform that intellectual labor.

It’s a disheartening lack of craftsmanship and industry accountability, but it’s nothing new.


I do not understand what gave you the impression that I was advocating for "hope and prayers". I'm advocating for not relying on one level of abstraction to be flawless so we can build a perfect logic on top of it. I'm advocating for not handling everything in a single layer. That FL2 program at cloudflare encountered an error condition and it bailed out and that's fine. What is not fine is that the supervisor did not fail open.

The oposing views here are not "hope and prayers" vs "good engineering", it's assuming things will fail at every stage vs assuming one can build a layer of abstraction that is flawless, on top of which we can build.

Resilient systems trump "correct" systems, and I would pick a system designed under the assumption that fake errors will be injected regularly, that process will be killed at random, that entire rack of machines will be unplugged at random at any time, that whole datacenters will be put off grid for fun, over a system that's been "proven correct", any day. I though it was common knowledge.

Of coursre I'm not arguing against proving that a software is correct. I would actually argue that some formal methods would come handy to model these kind of systemic failures and reveal the worste cases with largest blast radius.

But considering the case at hand, the code for that FL2 bot had an assertion regarding the size of received data and that was a valid assertion, and the process decided to panic, and that was the right decision. What was not right was the lack of instrumentation that should have made these failures obvious, and the fact that the user queries failed when that non-essential bot failed, instead of bypassing that bot.


I work as a pentester. CSRF is not a problem of the user proving their identity, but instead a problem of the browser as a confused deputy. CSRF makes it so the browser proves the identity of the user to the application server without the user's consent.

You do need a rigid authentication and authorization scheme just as you described. However, this is completely orthogonal to CSRF issues. Some authentication schemes (such as bearer tokens in the authorization header) are not susceptible to CSRF, some are (such as cookies). The reason for that is just how they are implemented in browsers.

I don't mean to be rude, but I urge you to follow the recommendation of the other commenters and read up on what CSRF is and why it is not the same issue as authentication in general.

Clearly knowledgeable people not knowing about the intricacies of (web) security is actually an issue that comes up a lot in my pentesting when I try to explain issues to customers or their developers. While they often know a lot about programming or technology, they frequently don't know enough about (web) security to conceptualize the attack vector, even after we explain it. Web security is a little special because of lots of little details in browser behavior. You truly need to engage your suspension of disbelief sometimes and just accept how things are to navigate that space. And on top of that, things tend to change a lot over the years.


Of course CSRF is a form of authorisation; "should I trust this request? is the client authorised to make this request? i.e. can the client prove that it should be trusted for this request?", it may not be "logging in" in the classic sense of "this user needs to be logged into our user system before i'll accept a form submit request", but it is still a "can i trust this request in order to process it?" model. You can wrap it up in whatever names and/or mechanism you want, it's still a trust issue (web or not, form or not, cookie or not, hidden field or not, header or not).

Servers should not blindly trust clients (and that includes headers passed by a browser claiming they came from such and such a server / page / etc); clients must prove they are trustworthy. And if you're smart your system should be set up such that the costs to attack the system are more expensive than compliance.

And yes, I have worked both red team and blue team.


You say you should "never trust the client". Well trust has to be established somehow right, otherwise you simply cannot allow any actions at all (airgap).

Then, CSRF is preventing a class of attacks directed against a client you actually have decided to trust, in order to fool the client to do bad stuff.

All the things you say about auth: Already done, already checked. CSRF is the next step, protecting against clients you have decided to trust.

You could say that someone makes a CSRF attack that manages to change these headers of an unwitting client, but at that point absolutely all bets are off you can invent hypothetical attacks to all current CSRF protection mechanisms too. Which are all based on data the client sends.

(If HN comments cannot convince you why you are wrong I encourage you to take the thread to ChatGPT or similar as a neutral judge of sorts and ask it why you may be wrong here.)



The doc is wrong and will be updated soon to say that Sec-Fetch-Site is sufficient on its own.

https://github.com/OWASP/CheatSheetSeries/issues/1803


Wow, nice thread!


Yes, this is documenting one particular way of doing CSRF. A specific implementation.

The OP is documenting another implementation to protect against CSRF, which is unsuitable for many since it fails to protect 5% of browsers, but still an interesting look at the road ahead for CSRF and in some years perhaps everyone will change how this is done.

And you say isn't OK, but have not in my opinion properly argued for why not.


It doesn't actually fail to protect 5%, as the top-line 5% aren't really "browsers". Even things like checkboxes often top out at around 95%!

You can change a setting on caniuse.com and it excludes untracked browsers. Sec-Fetch-Site goes up to 97.6, with remainder being a bit of safari (which will likely update soon) and some people still on ancient versions of chrome.

The fallback origin header goes to 99.8 coverage.


It's very complicated and ever evolving. It takes dedicated web app pentesters like you to keep up with it... back in the day, we were all 'generalists'... we knew a little bit about everything, but those days are gone. It's too much and too complicated now to do that.


The SameSite cookie flag is effective against CSRF when you put it on your session cookie, it's one of its main use cases. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/... for more information.

SameSite=Lax (default for legacy sites in Chrome) will protect you against POST-based CSRF.

SameSite=Strict will also protect against GET-based CSRF (which shouldn't really exist as GET is not a safe method that should be allowed to trigger state changes, but in practice some applications do it). It does, however, also make it so users clicking a link to your page might not be logged in once they arrive unless you implement other measures.

In practice, SameSite=Lax is appropriate and just works for most sites. A notable exception are POST-based SAML SSO flows, which might require a SameSite=None cookie just for the login flow.


This page has some more information about the drawbacks/weaknesses of SameSite, worth a read: https://developer.mozilla.org/en-US/docs/Web/Security/Attack...

You usually need another method as well


Yes, you're definitely right that there are edge cases and I was simplifying a bit. Notably, it's called SameSite, NOT SameOrigin. Depending on your application that might matter a lot.

In practice, SameSite=Lax is already very effective in preventing _most_ CSRF attacks. However, I 100% agree with you that adding a second defense mechanism (such as the Sec header, a custom "Protect-Me-From-Csrf: true" header, or if you have a really sensitive use case, cryptographically secure CSRF tokens) is a very good idea.


Thanks for correcting me - I see my web sec knowledge is getting rusty!


I don't believe this is true, as https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Web... exists. It does need an extension to be installed, but I think that's fair in your comparison with NPAPI.


Direct link to the comment with the new information (apparently it is not possible to keep the URL fragment): https://github.com/microsoft/vsmarketplace/issues/1168#issue...

The gist of it:

> The publisher account for Material Theme and Material Theme Icons (Equinusocio) was mistakenly flagged and has now been restored.

Previous discussion here, which is also the reason why I think this resolution is relevant as well: https://news.ycombinator.com/item?id=43178831


The article doesn't mention possible security implications. However, we already get lots of vulnerabilities exactly _because_ implementations disagree on delimiters. Examples for this are HTTP request smuggling[1, 2, 3] and SMTP smuggling[4].

As the references show, this is already a big source of vulnerabilities - trying to push for a change in standards would likely make the situation much worse. At the very least, old unmaintained servers will not change their behavior.

I think we should accept that this ship has sailed and leave existing protocols alone. Mandate LF and disallow CRLF in new protocols, that's fine, but I don't think we should open this particular Pandora's Box.

[1] Simple example that doesn't use CRLF/LF disagreement: https://portswigger.net/web-security/request-smuggling

[2] Complex example that uses CRLF/LF disagreement: https://portswigger.net/web-security/request-smuggling/advan... (see heading 'Request smuggling via CRLF injection')

[3] Random report on HackerOne I found where allowing LF created a vulnerability in NodeJS: https://hackerone.com/reports/2001873

[4] https://sec-consult.com/blog/detail/smtp-smuggling-spoofing-...


`git push -u origin HEAD` pushes the current branch to `origin` with the same name you have locally. You could even add an alias for that.


Or set `push.default` to `current` to have plain `git push origin` push to the same remote name, ignoring the configured upstream (you might also want to set `remote.pushDefault` alongside that).


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: