Apr 20, 2022
Clerk
Though each service performs the same function, there are important differences between how Clerk and Stytch manage sessions.
Last week, Stytch launched a similar session management API to Clerk’s. Both services allow developers to create and manage sessions directly from the frontend, while the necessary backend is provided by the API.
The hardest part about building our API was persisting session tokens with best-practice security, so we were intrigued to see how Stytch handled the same challenge.
When we reviewed their API, though, we found a more lax approach to security than we consider acceptable. This post highlights three ways we found Clerk is more secure than Stytch for session management:
With each concern, we share why it’s important and how Clerk addresses the problem.
It is considered best-practice to set long-lived session cookies as HttpOnly because it helps mitigate XSS attacks. The Open Web Application Security Project (OWASP) publishes a cheatsheet on session management, and states that the HttpOnly attribute is “mandatory”:
The HttpOnly cookie attribute instructs web browsers not to allow scripts (e.g. JavaScript or VBscript) an ability to access the cookies via the DOM document.cookie object. This session ID protection is mandatory to prevent session ID stealing through XSS attacks.
Despite this, Stytch’s session management API does not set its long-lived session cookies as HttpOnly.
Since stytch.com uses their own session management solution, this can be verified simply by signing up and inspecting your cookies. You’ll see that stytch_session
is not set as HttpOnly:
Further, Stytch’s session management documentation indicates that it sets this cookie client-side via document.cookie
, instead of server-side via a Set-Cookie
header, which would make it impossible to use the HttpOnly option:
Not using the HttpOnly flag means that during an XSS attack, sessions can be hijacked and the attacker can act on behalf of users, even after the vulnerability is patched. Users that visited during the XSS vulnerability must have their sessions revoked.
In contrast, Clerk’s long-lived session cookie is set as HttpOnly. You can verify this by signing up on clerk.dev and inspecting the cookie named __client
:
Note: Clerk also uses a short-lived session cookie named __session
that is not set as HttpOnly. The JWT in this cookie expires every 60 seconds to mitigate XSS attacks.
Take another look at how the stytch_session
cookie is configured and notice that Domain
is set to .stytch.com:
This is also a configuration that OWASP highlights as dangerous:
Setting the Domain attribute to a too permissive value, such as example.com allows an attacker to launch attacks on the session IDs between different hosts and web applications belonging to the same domain, known as cross-subdomain cookies. For example, vulnerabilities in www.example.com might allow an attacker to get access to the session IDs from secure.example.com.
To see a practical example of this concern, we can visit status.stytch.com which is hosted by a third-party vendor, Instatus.
A simple dig
command verifies that status.stytch.com is hosted by Instatus:
> dig +nocmd status.stytch.com +noall +answerstatus.stytch.com. 115 IN CNAME cname.instatus.com.cname.instatus.com. 60 IN CNAME cname-china.vercel-dns.com.cname-china.vercel-dns.com. 60 IN A 76.76.21.241cname-china.vercel-dns.com. 60 IN A 76.223.121.106
And, inspecting a request to status.stytch.com confirms the cookie is being delivered to Instatus:
This means that if an attacker gains access to Instatus, they can inspect its HTTP traffic to hijack stytch.com sessions. Phrased more generally, the permissive Domain
configuration for session cookies introduces an attack vector through otherwise-trusted third parties.
Unlike Stytch, Clerk assigns our long-lived session cookies to a subdomain to prevent it from leaking to third parties. In clerk.dev’s case, it’s set to .clerk.clerk.dev:
As a result, even though our reference documentation is hosted by Gitbook, Gitbook does not receive this cookie when a user visits reference.clerk.dev.
Update April 27, 2022: Stytch has partially resolved this issue by allowing developers to configure the "Maximum session duration" in the dashboard. We say "partially" because we still consider the practice of coupling JWT duration to session duration to be dangerous, since it means JWTs used for stateless authentication are not revocable for the full lifetime of the session.
Stytch’s session management API returns JWTs so developers can run stateless authentication. When generating a JWT, developers are asked to set session_duration_minutes
, which is used to determine the JWT’s lifetime:
With stateless authentication, JWT lifetime is the primary variable that impacts how long it takes to revoke a session. Developers cannot revoke existing JWTs, but they can prevent new ones from being issued. As a result, a session is not truly revoked until all existing JWTs have expired. (JWTs can be revoked faster by maintaining a denylist, but then authentication would not be stateless.)
Our concern with Stytch’s implementation is that session_duration_minutes
is configured from the frontend, which cannot be trusted. An attacker can modify the value and generate a JWT that lasts for years – potentially allowing an attack to continue long after the developer believes all JWTs have expired.
This can be demonstrated by signing in on stytch.com, but intercepting the request to authenticate and modifying session_duration_minutes
. We were able to generate a session JWT that expires in a year, even though Stytch configures their sessions to last just 7 days.
Clerk also supports JWTs for stateless authentication, but we fixed the lifetime to 60 seconds and use our frontend SDKs to regularly retrieve new ones.
This ensures sessions can be revoked within 60 seconds, which we believe is an essential security feature. To that end, we also provide session revocation UIs as a standard feature in our User Profile component.
Clerk is a security company first-and-foremost, but we believe in a future where developers do not need to concern themselves with authentication security. We’ve worked hard to provide robust, best-practice security by default, and we hope this post demonstrates our team’s care and attention to detail on security issues.
While security will always be a challenge, we’re incredibly fortunate to benefit from the researchers and organizations who catalog attack vectors and publish potential resolutions. We will continue to reference their learnings as part of every product and feature we build.
Start completely free for up to 5,000 monthly active users and up to 10 monthly active orgs. No credit card required.
Learn more about our transparent per-user costs to estimate how much your company could save by implementing Clerk.
The latest news and updates from Clerk, sent to your inbox.