lemmy
lemmy copied to clipboard
[Security Issue]: Auth JWT does not have expiration time and is sent in the URL to all requests.
Requirements
- [X] Is this a bug report? For questions or discussions use https://lemmy.ml/c/lemmy_support
- [X] Did you check to see if this issue already exists?
- [X] Is this only a single bug? Do not put multiple bugs in one issue.
- [X] Is this a backend issue? Use the lemmy-ui repo for UI / frontend issues.
Summary
Lemmy authenticates users with a JWT access token. This JWT does not expire and is only revocable if the user does an event like changing their password. This is insecure and does not follow current best practices for access tokens or for JWTs. To fix this:
- Add some sort of session revocation mechanism.
- Move the JWT from a URL param to a header.
- Give JWTs a short expiration time and have frontends perform a refresh with a refresh token. Have the refresh token rotate on every refresh call.
Steps to Reproduce
Lemmy's API authenticates users using the login
operation, which returns a JWT. This JWT is then passed on to all subsequent API calls in the auth
param, which is sent in the URL for GET
requests. While the JWT uses a good encryption algorithm, it does not contain an expiration time. There are no explicit ways for a user to revoke a particular session or JWT, but all JWTs can be revoked by doing an event like resetting your password. The JWT functions like an access token.
This breaks multiple security best practices around bearer tokens and JWTs, namely:
- JWTs should expire.
- JWTs should have a short expiration time (ex. 1 hour).
- Bearer tokens should be sent in headers or the body of the request, not the URL.
- After access tokens expire, they should be refreshed with a refresh token stored offline. This token should rotate with each access key rotation.
This is a security vulnerability. If any user JWT is stolen, that JWT will remain active until the user changes their password. Attackers will have access to a user's account for an unlimited amount of time.
Technical Details
No logs.
Version
All Lemmy versions
Lemmy Instance URL
No response
Also I may be wrong on this, I read the Claims
code but there could be something I'm missing here. But on lemmy.blahaj.zone, I'm seeing a JWT sent both in the cookie and as a URL parameter.
I went a bit deeper on one of your point here: https://github.com/LemmyNet/lemmy/issues/3364
JWTs should have a short expiration time (ex. 1 hour)
Doesn't that mean you'd be logged out after one hour?
That's what the refresh token is for.
Please add security label to this issue
That's what the refresh token is for.
Refresh tokens are mainly to reduce server load by avoiding the need to verify the auth token on every request.
While there can be edge cases where they help - I'm of the opinion that's more than offset by increased complexity and risk of a bad/buggy implementation, etc etc. Refresh tokens increase your attack surface.
Having said that, verifying auth tokens once an hour (by checking the refresh token) is certainly better than never verifying them, which seems to be what lemmy is doing now?
And yeah, move the tokens into a HTTP header. Preferably the cookie header - where you can take advantage of the httpOnly
and secure
flags.
https://github.com/LemmyNet/lemmy/pull/3946 moves auth tokens into header/cookie.