Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/hmac token signing #61

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Feat/hmac token signing #61

wants to merge 2 commits into from

Conversation

psibean
Copy link
Contributor

@psibean psibean commented Apr 6, 2024

The eventual intention before the next major release is to completely revamp the documentation and the testing suite as well, as well as provide more and better detailed examples. For testing, instead of repeating the same suite for all of the different configurations, I'd like to start splitting out the tests and only run the minimal amount needed, as well as generate a test coverage report to actually capture what has and hasn't been tested.

These changes are implicitly dependent on #60

The documentation and test changes aren't the best and there are some new edge cases not covered in the tests, such as one session trying to make a request with a CSRF token generated for another session. However, I plan on overhauling both the documentation and the tests before the next release, hopefully generating some sort of documentation from TSDoc, and also including a test coverage report.

@psibean psibean force-pushed the feat/hmac-token-signing branch from a172260 to 76efeff Compare April 6, 2024 05:31
@psibean psibean force-pushed the feat/hmac-token-signing branch from 76efeff to ff35b47 Compare April 7, 2024 01:38
@psibean psibean force-pushed the feat/hmac-token-signing branch from ff35b47 to 525eb68 Compare April 30, 2024 12:27
psibean added 2 commits May 1, 2024 23:17
BREAKING CHANGE: This change replaces the createHash method with
createHmac for hashing tokens. It introduces the getSessionIdentifier
configuration option which by default will return req.session.id. The
purpose of this function is to return the id of the session associated
with the incoming request. The session id will be included in the hmac
signature, forcefully tying the generated csrf token with that session.
This means by default generated tokens can only be used by the session
which they were originally generated for.

If you have any kind of session rotation (to mitigate session hijacking),
which you should be doing during privilege escaltions and de-escalations
(e.g. sign in, sign out) then you will need to generate a new csrf token
at the same time.

Additionally this change exposes the delimiter and hmacAlgorithm options.
@psibean psibean force-pushed the feat/hmac-token-signing branch from 525eb68 to bb9bc42 Compare May 1, 2024 13:48
@psibean psibean requested a review from davidgonmar May 1, 2024 13:50
Copy link
Collaborator

@davidgonmar davidgonmar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm.
I am assuming if one does not want to use session identifiers they can just do getSessionIdentifier = () => "", right?
Is it the best option to use express session as the default though?

@davidgonmar davidgonmar self-requested a review May 2, 2024 15:41
@psibean
Copy link
Contributor Author

psibean commented May 3, 2024

lgtm. I am assuming if one does not want to use session identifiers they can just do getSessionIdentifier = () => "", right?

I think the direction I'd like to go is to not explicitly support cases where the session id is not tied to the token. The recommended practice is that generated tokens should only be valid for the session that requested it, and this acheives that in a stateless way. In the case where getSessionIdentifier returns "", yeah that would work, but it will also be recommended that you otherwise tie the token to the session by some other means.

Is it the best option to use express session as the default though?

Not sure, are you suggesting we don't provide a default, thus removing the dev dependency on express-session?
And users have to provide this method? I wouldn't want to put a return of "" as a default, so no default would be the next best thing and would alleviate the implicit express-session introduced here. Could update doc to making it a required config option.

@davidgonmar
Copy link
Collaborator

davidgonmar commented May 4, 2024

lgtm. I am assuming if one does not want to use session identifiers they can just do getSessionIdentifier = () => "", right?

I think the direction I'd like to go is to not explicitly support cases where the session id is not tied to the token. The recommended practice is that generated tokens should only be valid for the session that requested it, and this acheives that in a stateless way. In the case where getSessionIdentifier returns "", yeah that would work, but it will also be recommended that you otherwise tie the token to the session by some other means.

Is it the best option to use express session as the default though?

Not sure, are you suggesting we don't provide a default, thus removing the dev dependency on express-session? And users have to provide this method? I wouldn't want to put a return of "" as a default, so no default would be the next best thing and would alleviate the implicit express-session introduced here. Could update doc to making it a required config option.

Hmmm isn't express-session stateful actually (afaik it stores them in memory by default)? I get the appeal, but the main point of double submit pattern is for it to be completely stateless, right?

Hmmm isn't express-session stateful actually (afaik it stores them in memory by default)? I get the appeal, but the main point of double submit pattern is for it to be completely stateless, right?

Also, I can't really think of a stateless way of maintaining a session in that sense.

@Psifi-Solutions Psifi-Solutions deleted a comment from davidgonmar May 5, 2024
@psibean
Copy link
Contributor Author

psibean commented May 5, 2024

Also, I can't really think of a stateless way of maintaining a session in that sense.

Right, but in a case where, let's say, a user is using JWT is a httpOnly cookie, then getSessionIdentifier here would just return the JWT from the cookie value. Or whatever httpOnly cookie value they're using for their authenticationauthorization identification.

If they aren't using a httpOnly cookie, then they aren't susceptible to CSRF, regardless of stateless or not.

I understand that the double submit pattern can be used from only the client side, without the backend being involved in the token generation at all - but all of the best practices recommend a backend using a secret so that you can guarantee where the token was generated from, and additionally, it's also recommended to include some piece of information when hashing that ties the token directly to the person who requested it.

Maybe I should rename it to getUniqueIdentifier as it isn't necessarily a session, it could also be the users id. The problem with using the user id, it doesn't exist before authenticating. But for people using JWT as a cookie, they don't typically need the authentication routes protected because there's no httpOnly cookie involved at that point for them.

Edit: I think I accidentally deleted a message, no idea how.

For express-session using memory by default, yes, it does, but express-session also says NOT to use the default in production and has a list of recommended DB or cache based stores. But yeah, it's stateful. Though providing it as a default doesn't require express-session if you're overriding it. Nor does the default even require express-session, it just requires a session object with an id to exist on the request, regardless of where it came from.

@davidgonmar
Copy link
Collaborator

Also, I can't really think of a stateless way of maintaining a session in that sense.

Right, but in a case where, let's say, a user is using JWT is a httpOnly cookie, then getSessionIdentifier here would just return the JWT from the cookie value. Or whatever httpOnly cookie value they're using for their authenticationauthorization identification.

If they aren't using a httpOnly cookie, then they aren't susceptible to CSRF, regardless of stateless or not.

I understand that the double submit pattern can be used from only the client side, without the backend being involved in the token generation at all - but all of the best practices recommend a backend using a secret so that you can guarantee where the token was generated from, and additionally, it's also recommended to include some piece of information when hashing that ties the token directly to the person who requested it.

Maybe I should rename it to getUniqueIdentifier as it isn't necessarily a session, it could also be the users id. The problem with using the user id, it doesn't exist before authenticating. But for people using JWT as a cookie, they don't typically need the authentication routes protected because there's no httpOnly cookie involved at that point for them.

Edit: I think I accidentally deleted a message, no idea how.

For express-session using memory by default, yes, it does, but express-session also says NOT to use the default in production and has a list of recommended DB or cache based stores. But yeah, it's stateful. Though providing it as a default doesn't require express-session if you're overriding it. Nor does the default even require express-session, it just requires a session object with an id to exist on the request, regardless of where it came from.

I think that's fine yeah. I saw the 'session' in the name and did not think of that haha

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants