Join us for Clerk Office Hours on September 29th at 2pm Eastern
Engineering

Three ways Clerk is more secure than Stytch for session management

Clerk

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:

  1. Stytch’s session cookies are not HttpOnly and are vulnerable to XSS attacks
  2. Stytch’s session cookies can leak to third parties in normal operation
  3. Stytch’s JWTs can be manipulated to expire much later than expected

With each concern, we share why it’s important and how Clerk addresses the problem.

Stytch’s session cookies are not HttpOnly and are vulnerable to XSS attacks

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:

Stytch documentation
Stytch documentation

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.

Stytch’s session cookies can leak to third parties in normal operation

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 +answer
status.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.241
cname-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.

Clerk’s session cookies do not leak to 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.

Stytch’s JWTs can be manipulated to expire much later than expected

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:

Stytch documentation
Stytch documentation

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.

Standard request
Standard request
Modified request
Modified request

Clerk’s JWT lifetime is fixed to 60 seconds

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.

Conclusion

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.

Ready to see what Clerk can do?

Start completely free for up to 500 monthly active users.
No credit card required.

Start building

Pricing

Learn more about our transparent per-user costs to estimate how much your company could save by implementing Clerk.