-
Notifications
You must be signed in to change notification settings - Fork 1k
Prevent accidental leak of PII when Copy & Pasting of Flow URLs which include Flow IDs #1282
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
Comments
Both solutions seem acceptable.
I would go with 1 |
One other approach I see is not having the session ID in the URL in the first place. Having it in a cookie instead will eliminate the risk of passing on the URL completely. |
I would go with the first option as well. Tracking the user's browser fingerprint (IP, location, a unique DeviceID cookie that is generated and stored on the browser, User-Agent, ...) can be useful to notify that a new device is requesting access. Having the option to do extra flows on new device detection could be nice. (Regenerating the FlowID if the DeviceID changed, notifying the user that a new device tries to access his/her account, enforcing MFA on the first login with a new device, ...) What do you think? |
I fully quote @zepatrik solution, having the flow id in the URL makes it a bit "weird" to the end-users as this is not common for website logins (at least the most popular ones). Additionally, as highlighted in this ticket, users tend to copy/paste the full URL disregarding any leak of potentially sensitive information. I did so myself by mistake more than once. |
I agree, but how would this work IRL? I see several problems:
Alternatively we could ask the UIs to do a 302 with What other alternatives do we have? |
One possibility could be to do the redirects using 307 which would allow Ory Kratos to forward the request (which includes the flow id in the post body) to the UI. The UI would then parse the body and extract the flow id. However, this does not work for the initial request because that is always a GET request Generally, this would also not work with OIDC flows as we end up with a GET callback meaning we can't upgrade that to a POST request. |
In my opinion doing post redirects won't work in the long run. The browser behaviour was changed more than once in that regard and there is also the fact that some browsers have some additional security rules that will just make that impossible. The main focus should be stability on different devices since people tend to do weird things when something doesn't work (like copying links to forums) Removing the flow param would be nice, but to limit the complexity we would need to rethink the browser flow a bit. |
I think POST redirects won't go away, actually 307 and 308 were introduced to deprecate 302 (which is deprecated but kept indefinitely due to popularity)! What I worry more about is cookies in redirects. This is already pretty difficult today and will only get harder in the future. Maybe we just need to require for Ory Kratos and the login UI to be on the same domain? |
I guess it has the same challenges as your proposed second solution. Cookies are in essence just metadata, and it might be easier to forward them as they are more standardized than the other metadata you proposed. |
As long as everything is on one TLD, cookies would be the best option. For non-TLD set ups we might need to add a proxy (thinking about you, |
Well, not really - IP and User Agent are much easier to collect than Cookies which are seeing an intense crackdown in the browser world, following SameSite security measuers and other Do Not Track technology! But if we operate under one TLD everything should be fine! |
We had the same discussion in our oidc solution (built on top of fosite) and we decided that we would use both the flow ID and a session cookie. So only when both are present, state is valid. We want flow ID in the URL so that user might not get confused by doing inside multiple tabs, e.g., if in one tab user is approving app A and in another tab user is approving app B. So we want to keep that separate but we also want flows to be valid only in this browser. So, a session cookie is set. |
The only truly secure option here is probably to initialize the flow not using the browser but instead using the backend. The Ory Kratos API then sends something like:
which then is used by the backend (so no initial redirection is required!) to render the form as usual:
The submission could either be against the backend (a bit more flexible but difficult for CSRF), or directly against ory kratos (a bit easier but not as flexible). In the latter case, and if a validation error occurred, ory kratos would redirect to the registration ui and include the flow ID as we know. However, for the backend to make a successful request, it would need to look like this:
This would be a pretty significant API change. As an alternative we could use heuristics (e.g. IP address) to keep the flow as-is. Another alternative would be to keep the flow as is and require the UI and Ory Kratos to be co-located on the same domain, so that Ory Kratos can set the CSRF cookie for all of the domain, and the UI then uses not the administrative endpoint but instead the public endpoint and is required to include the CSRF cookie in the request. That would certainly be easier to implement on Ory Kratos' side, have less breaking changes, and - if documented correctly and the SDK is playing along - could be pretty straight forward to implement. |
I think the last idea is probably the best one. Will pursuit that option! |
BREAKING CHANGE: This patch introduces CSRF countermeasures for fetching all self-service flows. This ensures that users can not accidentally leak sensitive information when copy/pasting e.g. login URLs (see #1282). If a self-service flow for browsers is requested, the CSRF cookie must be included in the call, regardless if it is a client-side browser app or a server-side browser app calling. This **does not apply** for API-based flows. As part of this change, the following endpoints have been removed: - `GET <ory-kratos-admin>/self-service/login/flows`; - `GET <ory-kratos-admin>/self-service/registration/flows`; - `GET <ory-kratos-admin>/self-service/verification/flows`; - `GET <ory-kratos-admin>/self-service/recovery/flows`; - `GET <ory-kratos-admin>/self-service/settings/flows`. Please ensure that your server-side applications use the public port (e.g. `GET <ory-kratos-public>/self-service/login/flows`) for fetching self-service flows going forward. If you use the SDKs, upgrading is easy by adding the `cookie` header when fetching the flows. This is only required when **using browser flows on the server side**. The following example illustrates a ExpressJS (NodeJS) server-side application fetching the self-service flows. ```patch app.get('some-route', (req: Request, res: Response) => { - kratos.getSelfServiceLoginFlow(flow).then((flow) => /* ... */ ) + kratos.getSelfServiceLoginFlow(flow, req.header('cookie')).then((flow) => /* ... */ ) - kratos.getSelfServiceRecoveryFlow(flow).then((flow) => /* ... */ ) + kratos.getSelfServiceRecoveryFlow(flow, req.header('cookie')).then((flow) => /* ... */ ) - kratos.getSelfServiceRegistrationFlow(flow).then((flow) => /* ... */ ) + kratos.getSelfServiceRegistrationFlow(flow, req.header('cookie')).then((flow) => /* ... */ ) - kratos.getSelfServiceVerificationFlow(flow).then((flow) => /* ... */ ) + kratos.getSelfServiceVerificationFlow(flow, req.header('cookie')).then((flow) => /* ... */ ) - kratos.getSelfServiceSettingsFlow(flow).then((flow) => /* ... */ ) + kratos.getSelfServiceSettingsFlow(flow, undefined, req.header('cookie')).then((flow) => /* ... */ ) }) ``` For concrete details, check out [the changes in the NodeJS app](ory/kratos-selfservice-ui-node@e7fa292). Closes #1282
About two months ago we released Ory Kratos v0.6. Today, we are excited to announce the next iteration of Ory Kratos v0.7! This release includes 215 commits from 24 contributors with over 770 files and more than 100.000 lines of code changed! Ory Kratos v0.7 brings massive developer experience improvements: - A reworked, tested, and standardized SDK based on OpenAPI 3.0.3 ([ory#1477](ory#1477), [ory#1424](ory#1424)); - Native support of Single-Page-Apps (ReactJS, AngularJS, ...) for all self-service flows ([ory#1367](ory#1367)); - Sign in with Yandex, VK, Auth0, Slack; - An all-new, secure logout flow ([ory#1433](ory#1433)); - Important security updates to the self-service GET APIs ([ory#1458](ory#1458), [ory#1282](ory#1282)); - Built-in support for TLS ([ory#1466](ory#1466)); - Improved documentation and Go Module structure; - Resolving a case-sensitivity bug in self-service recovery and verification flows; - Improved performance for listing identities; - Support for Instant tracing ([ory#1429](ory#1429)); - Improved control for SMTPS, supporting SSL and STARTTLS ([ory#1430](ory#1430)); - Ability to run Ory Kratos in networks without outbound requests ([ory#1445](ory#1445)); - Improved control over HTTP Cookie behavior ([ory#1531](ory#1531)); - Several smaller user experience improvements and bug fixes; - Improved e2e test pipeline. In the next iteration of Ory Kratos, we will focus on providing a NextJS example application for the SPA integration as well as the long-awaited MFA flows! Please be aware that upgrading to Ory Kratos 0.7 requires you to apply SQL migrations. Make sure to back up your database before migration! For more details on breaking changes and patch notes, see below.
About two months ago we released Ory Kratos v0.6. Today, we are excited to announce the next iteration of Ory Kratos v0.7! This release includes 215 commits from 24 contributors with over 770 files and more than 100.000 lines of code changed! Ory Kratos v0.7 brings massive developer experience improvements: - A reworked, tested, and standardized SDK based on OpenAPI 3.0.3 ([ory#1477](ory#1477), [ory#1424](ory#1424)); - Native support of Single-Page-Apps (ReactJS, AngularJS, ...) for all self-service flows ([ory#1367](ory#1367)); - Sign in with Yandex, VK, Auth0, Slack; - An all-new, secure logout flow ([ory#1433](ory#1433)); - Important security updates to the self-service GET APIs ([ory#1458](ory#1458), [ory#1282](ory#1282)); - Built-in support for TLS ([ory#1466](ory#1466)); - Improved documentation and Go Module structure; - Resolving a case-sensitivity bug in self-service recovery and verification flows; - Improved performance for listing identities; - Support for Instant tracing ([ory#1429](ory#1429)); - Improved control for SMTPS, supporting SSL and STARTTLS ([ory#1430](ory#1430)); - Ability to run Ory Kratos in networks without outbound requests ([ory#1445](ory#1445)); - Improved control over HTTP Cookie behavior ([ory#1531](ory#1531)); - Several smaller user experience improvements and bug fixes; - Improved e2e test pipeline. In the next iteration of Ory Kratos, we will focus on providing a NextJS example application for the SPA integration as well as the long-awaited MFA flows! Please be aware that upgrading to Ory Kratos 0.7 requires you to apply SQL migrations. Make sure to back up your database before migration! For more details on breaking changes and patch notes, see below.
Is your feature request related to a problem? Please describe.
While UUIDs are hard to guess, they are very prominent in the URL:
This might tempt users to copy and paste that link and send it to someone else (Alice says: "Hey Bob, here's the registration URL!"). There are two problems with that:
Describe the solution you'd like
Before Ory Kratos 0.5 we had a CSRF protection in place for public endpoint. Fetching the flow would require sending the CSRF cookie as well. This completely disarms the second example as Bob does not have Alice's CSRF Cookie. This can also allow Ory Kratos to instruct the UI to re-initialize the flow. However, this only works for requests made to the Public API - hence single-page apps / browser apps.
This becomes problematic for the Admin APIs used by server-side apps (e.g. NodeJS). Here, the server-side ui (e.g. our NodeJS example) makes a HTTP request to the Admin API to fetch the data. Requiring a CSRF cookie on Kratos' Admin API will not work here because the UI might be hosted on another domain and might not have access to the CSRF cookie of Ory Kratos.
Thus, we need another mechanism to prevent the NodeJS app of leaking the information. There are several possibilities to achieve this:
Both approaches leave a little bit of attack surface:
Describe alternatives you've considered
Requiring the CSRF cookie to be available on the domain that hosts the UI on the server side.
Additional context
Guessing the UUID is not really a problem because registration, login, ... flows have relatively short expiry times and use UUIDs. To find a collision here is not possible as the subset of issued UUIDs will never go above one billion.
Even with 100 trillion - making one billion network requests takes a lot of time!
So, what do you think is the best approach here?
The text was updated successfully, but these errors were encountered: