Yup. Changing your password or 2FA wouldn’t help here, because they’re not actually logging into your account. Rather, they’re simply telling the server that they’re already logged in, using your auth token as proof. You know that little “Keep me logged in” checkbox that everyone clicks when they log in? That stores an auth token on your browser, which is tied to your account.
The next time the browser starts a session on the site, it sends that auth token instead of going through the regular login process. And since the site knows that auth token belongs to your account, it logs you in automatically without needing to go through the regular login process.
So basically, they’re stealing a cookie from your browser, with your name on it. Then they’re able to tell the server that they’re you, by presenting that cookie as proof.
Proper procedure should be to deauthorize any auth tokens when you change your password. But even big sites get lazy about this sometimes, so it may not be the default. If this is the case for Lemmy, even changing your password won’t help because it doesn’t automatically deauth that token.
Really curious to see how they kill the existing tokens, and whether admins have tools to easily clear all sessions. On one of the Matrix chats someone suggested that the tokens have a one year expiry date!
Tokens are signed with a secret string, which basically tells the server that it is legitimate. They could change that secret, and the server would immediately distrust any tokens signed with the old secret. This would be a pretty nuclear option though, because it would require every single user to log back in.
You’re not the first person to say that the expiration time is a year, which is hilariously long if true. A shorter expiration time is more secure (because it specifically limits attacks like this to a specific timeframe) but it also increases server load by requiring token requests more often. For instance, if the expiry was set at 5 minutes, you’d have requests every 5 minutes but an attacker would only have control of an account for a maximum of five minutes. Maybe it was done to help save on server load, since this is all basically run by a few people as a hobby.
Unless you are entering your credentials every time you perform an action on a website (click a button, navigate a page, submit a form), your session (or JWT in this case) is stored on your client and is accessible by your client.
So XSS can still extract usable tokens, regardless of the “remember me”.
JWTs have an access token (short lived) and a refresh token (long lived).
The refresh token allows the client to request a new access token without requiring further credentials (this is usually a stateful action and allows the server to check for banned accounts etc before issuing a new access token. It essentially “debounces” a lot of database activity, and still allows for a lot of authorisation of users).
However the access token is valid until it expires (unless servers run a stateful blocklist of tokens, which is unlikely).
The TTL of an access token defines the attack window.
5 minute tokens are more secure, however result in a lot more database hits due to more token refreshes.
30 minutes is still decent. A lot use 4 hours.
But once an access token is issued, it’s valid for its entire lifetime.
Maintaining a blocklist of invalidated access tokens (allowing instant user account blocking, instead of having to wait for access tokens to expire) is doable - because the tokens it has to store are short lived the dataset should be small and light.
This can even be done in-memory, with a 5m database poll to update the block list.
However, this becomes difficult to manage over distributed systems.
And it still leaves a 5 minute window.
The other way to invalidate JWTs is by rotating the secrets used to create them.
This will mean the server will not trust JWTs created with the old secrets. It’s a pretty nuclear option.
However this affects all tokens, so everyone has to reauthenticate.
Once a token is issued it is valid until it experies. There is no way to disable a token short of changing the secret used to sign them which would invalidate all existing tokens for all users.
I actually suggested exactly that elsewhere. It would be a nuclear option, for sure. Since it would require every single user to log back in. But it would 100% without a doubt stop the attacker in their tracks.
Yup. Changing your password or 2FA wouldn’t help here, because they’re not actually logging into your account. Rather, they’re simply telling the server that they’re already logged in, using your auth token as proof. You know that little “Keep me logged in” checkbox that everyone clicks when they log in? That stores an auth token on your browser, which is tied to your account.
The next time the browser starts a session on the site, it sends that auth token instead of going through the regular login process. And since the site knows that auth token belongs to your account, it logs you in automatically without needing to go through the regular login process.
So basically, they’re stealing a cookie from your browser, with your name on it. Then they’re able to tell the server that they’re you, by presenting that cookie as proof.
Proper procedure should be to deauthorize any auth tokens when you change your password. But even big sites get lazy about this sometimes, so it may not be the default. If this is the case for Lemmy, even changing your password won’t help because it doesn’t automatically deauth that token.
Really curious to see how they kill the existing tokens, and whether admins have tools to easily clear all sessions. On one of the Matrix chats someone suggested that the tokens have a one year expiry date!
The servers should theoretically have a way to murder the tokens, but I’m not sure how Lemmy has implemented authentication so I don’t know for sure.
Looks like you’re right, admins will just need to update the JWT secret.
That makes sense. Of course, updating the secret will log everyone out, but that’s a small price to pay to fix an admin breach.
Tokens are signed with a secret string, which basically tells the server that it is legitimate. They could change that secret, and the server would immediately distrust any tokens signed with the old secret. This would be a pretty nuclear option though, because it would require every single user to log back in.
You’re not the first person to say that the expiration time is a year, which is hilariously long if true. A shorter expiration time is more secure (because it specifically limits attacks like this to a specific timeframe) but it also increases server load by requiring token requests more often. For instance, if the expiry was set at 5 minutes, you’d have requests every 5 minutes but an attacker would only have control of an account for a maximum of five minutes. Maybe it was done to help save on server load, since this is all basically run by a few people as a hobby.
Thank you both for explaining that so clearly :)
Also yikes.
It’s not the “remember me” checkbox.
Unless you are entering your credentials every time you perform an action on a website (click a button, navigate a page, submit a form), your session (or JWT in this case) is stored on your client and is accessible by your client.
So XSS can still extract usable tokens, regardless of the “remember me”.
JWTs have an access token (short lived) and a refresh token (long lived).
The refresh token allows the client to request a new access token without requiring further credentials (this is usually a stateful action and allows the server to check for banned accounts etc before issuing a new access token. It essentially “debounces” a lot of database activity, and still allows for a lot of authorisation of users).
However the access token is valid until it expires (unless servers run a stateful blocklist of tokens, which is unlikely).
The TTL of an access token defines the attack window.
5 minute tokens are more secure, however result in a lot more database hits due to more token refreshes.
30 minutes is still decent. A lot use 4 hours.
But once an access token is issued, it’s valid for its entire lifetime.
Maintaining a blocklist of invalidated access tokens (allowing instant user account blocking, instead of having to wait for access tokens to expire) is doable - because the tokens it has to store are short lived the dataset should be small and light.
This can even be done in-memory, with a 5m database poll to update the block list.
However, this becomes difficult to manage over distributed systems.
And it still leaves a 5 minute window.
The other way to invalidate JWTs is by rotating the secrets used to create them.
This will mean the server will not trust JWTs created with the old secrets. It’s a pretty nuclear option.
However this affects all tokens, so everyone has to reauthenticate.
Once a token is issued it is valid until it experies. There is no way to disable a token short of changing the secret used to sign them which would invalidate all existing tokens for all users.
I actually suggested exactly that elsewhere. It would be a nuclear option, for sure. Since it would require every single user to log back in. But it would 100% without a doubt stop the attacker in their tracks.
That’s bad design because you can bind a user token to a per-account value which can be rotated to deprecate tokens