Tell HN: Stytch Login SaaS Unicorn has common auth vulnerabilities
First of all: Why am I posting this on Hacker News instead of disclosing directly?
From experience, a lack of security policies and of communication on existing vulnerabilities implies retaliatory practices when submitting vulnerabilities responsibly. Since MITRE CVE does not accept vulnerabilities for SaaS services, I figured that Hacker News would be the way to get Stytch aware of their issues without getting me in the crosshairs.
The setting:
A few weeks ago (in June 2022 to be exact) I was looking at different authentication services and Stytch was one of the services that got my attention. Stytch.com was founded in 2020 with over $125M [1] raised at a $1bn+ valuation [2].
Checking out what the fuzz was about I decided to look into their security practices. The lack of a bug bounty program (e.g. HackerOne), ethical disclosure policy, and security policy already left a bad feeling. But I was keeping an open mind and wanted to see what their API has to offer.
The scoop:
The most critical issue I came across was a complete lack of Cross-Site Request Forgery defenses. All of the provided authentication APIs (e.g. [4]) except for Social Sign-In are vulnerable to the most basic of login attack vectors: Login CSRF [3]. This can be used to steal credit card information, for example. CSRF is completely missing in all of the Stytchs API concepts, a major oversight in the API design.
Further I found that the OTP tokens sent via their "passwordless" email authentication were valid for up to 7 days and did not invalidate on use. Attackers may find old magic link tokens in the browser, chat, or email histories and use them to get a valid session. While this issue has been fixed since June 2022, the vulnerability was not disclosed to customers.
Recently, Stytch has introduced a concept from OAuth2 called PKCE - a spec that is difficult to master for everyday developers - into their non-standardized Magic Link API which does not use a three-legged delegation authorization mechanism. This is not a direct vulnerability per se, but at least a questionable choice in terms of security.
A full report can be found here for anyone interested: https://www.klgrth.io/paste/kmxof
[1] https://www.crunchbase.com/organization/stytch-auth [2] https://stytch.com/blog/announcing-series-b/ [3] https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#login-csrf [4] https://stytch.com/docs/passcodes#sms_auth
51 comments
[ 12.4 ms ] story [ 190 ms ] threadFirst, I'm sorry you felt you had to post this on HN to both alert users and not be retaliated against. I wish you'd had an avenue to disclose the issues less publicly. This would allow the company to fix them and not put their customers at risk.
I think every company, and certainly every auth company, should welcome security issue reporting, as well as take other steps like regularly paying for pen testing. We do ( https://fusionauth.io/security ).
However, I haven't looked at how Stytch implemented PKCE, but it can provide CSRF protection in some scenarios.
The bigger picture is that this is just another reason to not invent your own standards. Use options like OIDC and benefit from the hard won wisdom of others.
Other reading:
* https://datatracker.ietf.org/doc/html/draft-ietf-oauth-secur... the OAuth Security BCP
* https://owasp.org/www-community/attacks/csrf CSRF attack description
Basically someone fires up a tool that checks a few things mentioned in a best practices document somewhere, scans as much domains as possible, and then sends out emails with the (subset of) the results.
Invariably, the results are false positives but hey, if you don't know that, they might get a new client. They'll file the (automatically generated) report and presto, easy money and a chance to upsell a retainer.
They never should have come close to passing. Details on the audit seem to be sparse so I can't call out the auditor and/or penetration tester but yeah this is a real bad look for the whole industry.
This really muddies the water for every security-minded startup that has taken the trouble to go through SOC-2 or ISO 27001. Very disappointing.
I guess billion dollar valuation really can buy you a rubber stamp.
There's a lot of stuff that comes up in a properly driven pentest that nessus just doesn't do. Never had a nessus operator pass the hash into domain admin or pretend to be a Spectrum technician and hide in the closet until after hours.
2) Reed (CEO) already responded to these claims: https://news.ycombinator.com/item?id=33164969
With (2) in mind: no, I don't think it's damning at all. Sure looks that way if you fail to analyze half the claims and take the remaining half out of context :)
Stytch... is a weird business. It's unclear to me why they were able to raise so much money with such low revenue. It's almost as if the VCs really really wanted them to succeed, hoping and dreaming and throwing cash into the fire.
there was a lot of fanfare around their founding team but not sure if they did that much at their previous employer.
[1]: https://scotthelme.co.uk/csrf-is-really-dead/
I wrote a bit about that here: https://simonwillison.net/2021/Aug/3/samesite/#login-csrf-sa...
Plus, SameSite cookies are only supported by 95% of worldwide browser traffic, according to https://caniuse.com/same-site-cookie-attribute
If you're a vendor selling SSO services I'm going to expect you to have 100% coverage on CSRF mitigations like this!
And the fix is...expire properly and make the user re-auth or?
Maybe it's like this to handle when the link is opened in a different browsing session?
Unless I'm misunderstanding, the underlying concern is that Stytch is:
- A new authentication company, that focuses 100% on authentication, and
- Has raised an absolutely insane amount of money for that, and
- Has extremely novice and easy-to-fix issues, and
- Those mistakes aren't being immediately addressed
I'm not saying that people aren't allowed to make mistakes, and auth mistakes can be very easy to make, but it's way more concerning if you're claiming to be the "Future of User Authentication" while making them.
Edit: formatting
It's proving surprisingly difficult to research. Here are my notes so far: https://github.com/simonw/public-notes/issues/2
SameSite=Lax is almost but not quite the answer I'm looking for, for a couple of reasons:
- It doesn't protect against attacks from subdomains, since they can trigger requests that use existing Lax cookies. This is a problem because I can never be sure that a future decision won't be made to CNAME helpdesk.mysite.com over to some third party vendor who might themselves have security holes that enable XSS attacks to run against my domain
- SameSite cookie support is at 95.75% global usage. That's not 100% yet. Where security is concerned, I care about that 4.25% of users. https://caniuse.com/same-site-cookie-attribute
I don’t immediately know of any sane configurations that strip a same-origin referrer, but that would cause every request to be blocked as cross-site.
Spoofing a third-party referrer to same-origin is rare, but occasionally done (I think uMatrix defaults to doing that). And unfortunately, it looks like Firefox at least (the only one I’ve checked) includes a trailing slash on third-party referrers rather than only including the origin (that is, it sends `Referer: https://example.com/` for different-origin requests rather than `Referer: https://example.com` like I hoped), so you can’t completely reliably distinguish between an actual first-party request of /, and a spoofed third-party request. But if you’re OK blacklisting the root path, it’d work.
(Actually, what about the Origin header? I’m a little rusty on current statuses and usages of the Origin and Referer headers; haven’t been doing loginny HTTP for a few years. Can’t remember how old the Origin header is, either. But is there some reason you’re not just going for it?)
Block requests where origin=helpdesk.mysite.com.
Also, since you're concerned about subdomain attacks, make sure you set the cookie on a subdomain rather than the naked domain to prevent it from leaking.
Edit: you can put it on the naked domain if your app is on the naked domain. If you do that, do not set the Domain= attribute in your Set-Cookie because that will cause it to leak to subdomains.
- https://stytch.com/docs/#security_overview
* Note: Search for “security@“ on link above.
- https://web.archive.org/web/20210801185042/https://stytch.co...
- https://www.google.com/search?q=stytch+security+disclosure
To me, little strange to be so critical and have obviously spent time to research and document their findings, then fail to do even basic due diligence.
Also appears author is posting this anonymously, which given the prior information, makes me wonder what their motives are. To me, this feels more like an attack than responsible disclosure. Flagging the post.
* The google search you linked it took me 2 minutes staring at the results page before I notifed the part where the relevant information comes up. It's on a sub-link with the heading "Docs", 80% of the way down the page in an h3. That's pretty damn buried.
I think it's safe to say they essentially phoned in and checked the box on the whole security disclosure thing. Do they meet the requirement technically? Yes. But the info is actually pretty buried so much so that archive.org has never seen it before. Should be on their site footer.
https://news.ycombinator.com/item?id=33164969
Regardless this is a HUGE stretch to say "has no CRSF-protection in their authentication API". You've shown one instance where they don't have CSRF protection on an endpoint that could be argued it's not necessary. Do they have CSRF protection on endpoints that let you adjust your account, perform actions in an app, or other legitimately concerning endpoints?
Can't really blame anyone for being concerned about corporate retaliation, but there are most certainly institutions that would send an email on your behalf to disclose vulnerabilities to a security@ email if you wanted to remain anonymous out of a sense of caution.
On top of that you've made some inflated comments on the general security practices about this company with no real evidence and the vulnerabilities listed best-practices at best and inconsequential at worst.
I question the motives behind a post like this:
Does the author have some financial incentive to attempt to discredit Stytch? Does the author want to cause some panic/concern by making scary claims about Stytch? Does the author not truly understand the impact of their vulns and thing that this is a serious issue when it's not?
I can't say but the whole situation really sits wrong with me.
All you have to do is sneak onto your target's gmail while they aren't looking (or phone), forward one of those emails within a week of them arriving, and you have 7-x days to compromise their account. This is very different from almost any other magic link which by convention are always consume-on-use with a short expiration and often a context cookie reqiurement.
Imagine the CTO is screen sharing and a notification showing the code part of the link shows up without them noticing and now anyone on the call who noticed has a week to login as the CTO. This stuff can happen way easier than you think.
https://news.ycombinator.com/reply?id=33164969&goto=item%3Fi...
I can't speak for Stytch, but I know at $CURJOB, we have run into issues with this where corporate phishing protection software invalidates one time use codes. I believe it does that because it retrieves links in emails before the user clicks on them. This was an issue several of our customers have raised. So it isn't as straightforward as you might think.
There's more details, including the workaround we ended up using, on the GH issue: https://github.com/FusionAuth/fusionauth-issues/issues/629
PS I agree they should absolutely have guidance around link lifetime as well as safe defaults. Converting the links to one-time use seems like a good start.
Something I totally agree with before you've passed multiple audits. At this stage it's more a question of how the hell did this slip by 2 pen tests and 2 external audits (since they will have had to renew their SOC-2 by now.
The "how this slipped through" is much more concerning than "what slipped through", though the "what slipped through" here is also rather serious imo.
- OP claims we do not have a bug bounty program. We do have a private bug bounty program. We also triage all reports that are sent to security@stytch.com. In addition to this, we have an in-house security engineering team and work with an external firm.
- OP claims we do not have a responsible disclosure program. We do [1]
- On the point about CSRF, OP is correct that login CSRF scenarios are common with magic link products. For the uninitiated, Login CSRF is a vulnerability where an attacker gets a victim to log into an account controlled by the attacker, instead of their own. For example, an attacker could forward a magic link email to a victim, who clicks it unknowingly and ends up logged into the attacker’s account. Stytch adapted PKCE [2] to magic links so that applications have cryptographic proof that the device clicking on the magic link is the same device that originally requested the magic link. Our PKCE design was audited by our external security consultant. PKCE - as well as other more contemporary cookie-based CSRF measures - are not effective on mobile applications due to heavy use of webviews. Often, there is no guarantee that the browser on the device (perhaps an embedded webview within an application) that requests the magic link is the same browser that is opened by the email client when the magic link is received. For mobile-centric customers with workloads that are sensitive to login CSRF, we recommend pairing magic links with one of our second factor products such as SMS OTP, TOTP, or WebAuthn.
- On their point about OTP tokens, it’s unclear if they’re talking about our magic link product or our actual one-time passcode products. In either case, the author is incorrect. Each product behaves slightly differently. For email/SMS/whatsapp one-time passcodes, these tokens expire both a) after first-time use and b) if the developer initiates a new one. For magic links, the token is invalidated once it is consumed or when it expires (whichever comes first). Their description of default behavior is also misleading. For example, while a sign-up magic link defaults to 1 week expiration (and can be configured lower) due to common email confirmation flows at sign-up, a login magic link has a lower default expiration at 1 hour. For OTP codes, the default code expires in 2 minutes and the longest it can be valid for is 10 minutes (this distinction is by design as there’s less entropy with a 6-digit code than a magic link token)
[1] https://stytch.com/docs/#security_overview [2] https://stytch.com/docs/magic-links#email-magic-links_adding...
It appears to refresh against https://stytch.com/web/sdk/sessions/authenticate with a Basic auth token which is also set on the client-side
Is there any protection to prevent this from leaking during an XSS attack? How does that work?
The approach our SDK takes (using client-side JS to write to storage) does not offer HTTPOnly protection. This pattern mirrors the approach taken by Firebase, which stores access tokens in local storage. In order to mitigate XSS impact, we have a few mechanisms available. The session JWT itself is only valid for 5 minutes (there is also a longer-lived opaque token, which is valid for longer and is rotated on its own cadence). We’re also introducing support for risk-based controls such as device fingerprinting.
That being said, there are designs that allow 3rd party APIs like ours to set HTTPOnly cookies, by proxying the 3rd party APIs as subdomains. In the future, we'll likely also offer a HTTPOnly session management offering in the SDK to interested customers.
A final note - for integrations with backend-as-a-service that require passing the JWT in a header (hasura [1], mongo atlas [2]), it's impossible to keep the JWT httponly. You can have multiple audiences but the JWT must be exposed to application code in order for the application code to send it somewhere else.
[1] https://hasura.io/docs/latest/auth/authentication/jwt/#heade... [2] https://www.mongodb.com/docs/realm/web/authenticate/#custom-...
However, I am concerned that the refresh mechanism is also not HttpOnly.
Firebase storing access tokens in client-side storage is an example of the former, not the latter. Are they also storing refresh tokens client-side?
FWIW - I am surprised that you would conflate access tokens and refresh tokens like this.
- You can provide a significantly better developer experience and set-up with this architecture. While there are designs that allow 3rd party APIs like ours to set HTTPOnly cookies by proxying the 3rd party APIs as subdomains, this creates new burdens on the developer for minimal gain considering that a XSS attack vector indicates a severe compromise of the application.
- Today, customers that feel strongly about using HTTPOnly session management will opt for a direct integration with our API using one of our back-end client libraries rather than our JS SDK. While we have interest in providing a HTTPOnly solution in the future to interested customers, we’ve decided the default behavior of the existing SDK is better suited for most developers.
[1] https://github.com/firebase/firebase-js-sdk/blob/0b3ca78eb97...
I remain shocked that an auth company CEO would push a solution without HttpOnly protection. This would not get by our security audits, and Auth0 and many open source tools I've used do not have the same limitation (Auth0 sets it in the SDK rather than a proxy).
OWASP and NIST are aligned that HttpOnly cookie should be used:
[1] https://cheatsheetseries.owasp.org/cheatsheets/Session_Manag...
[2] https://pages.nist.gov/800-63-3/sp800-63b.html#711-browser-c...
One callout I’d like to make is that there are two kinds of SDKs. Client-side ones (like Javascript SDKs) and Server-side ones (NodeJS, Go, Python, etc.). The server-side ones are capable of setting HttpOnly cookies, because the server-side ones run on the server and not in a browser context. This is true for both Auth0 and Stytch, and someone using any of the Stytch server-side SDKs will have HttpOnly protection.
Client-side only SDKs are never capable of setting first-party HttpOnly cookies without the aid of a proxy. In fact, there is no truly secure storage mechanism addressable by clientside javascript. All writable storage is accessible to all javascript loaded in the domain - that is to say that if at any point the Auth0 SDK has access to a token, any XSS attack running in the same document will also have access.
Auth0 has numerous clientside SDKs, but we’ll look at their most popular one - @auth0/auth0-spa-js. This SDK stores refresh tokens in a cache [1] in a few ways:
- By default, in memory, which makes exfiltration harder but still very possible via client-side JS. Wrapping a token in a closure doesn’t mean it isn’t addressable - a hacker can monkeypatch and listen to window.fetch for example. This also means that login state is not preserved across tabs or page refreshes, which is quite frankly extremely frustrating to both developers and users
- Auth0 also supports an iframe based flow, which breaks on browsers that use ITP2 such as Safari [2] - so 20% of all users on desktop and 25% of all users on mobile.
- Finally, for customers who do not want the above restrictions, Auth0 allows localstorage [3] to be configured as a cache storage. Local storage is just as open to XSS exfiltration as non-HTTPOnly cookies.
So while yes, Auth0 does not set cookies, their refresh tokens are still accessible client-side in many common deployment scenarios, and are still vulnerable to the same XSS exfiltration vulnerability that HTTPOnly cookies protect against.
Overall, the main reason that Google’s security team, Auth0’s security team, and our security team are comfortable with offering a non-HTTPOnly session management solution in a JS SDK comes down to:
1. HTTPOnly as a security layer can help prevent exfiltration, but if an app already has an XSS vector, it’s already severely compromised, making such a layer moot.
2. As an auth company (whether you’re Stytch vs. Auth0 vs. Google Firebase), you need to make a decision on how much flexibility you want to offer developers. Our stance is that when additional flexibility and an improved developer experience do not create any practical security risk, we should provide that better developer experience to our customers.
[1] https://github.com/auth0/auth0-spa-js/blob/0de9c6bf61d37fc21...
[2] https://community.auth0.com/t/silent-authorization-not-worki...
[3] https://github.com/auth0/auth0-spa-js/blob/0de9c6bf61d37fc21...