Skip to content
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

fix: avoid email delegation via GET request #430

Merged
merged 2 commits into from
Feb 10, 2023
Merged

Conversation

travis
Copy link
Member

@travis travis commented Feb 9, 2023

Originally proposed in #398, moved here to make rebasing simpler and to fix tests.

Original PR message from @natevw:

The email validation approval process is now split into two stages: a GET request with no side effects except to load a page, that then auto-submits a POST request to actually continue the flow.

Summary of problem

This fixes the API so as to follow proper HTTP semantics:

The purpose of distinguishing between safe [i.e. like GET] and unsafe [like PUT/POST] methods is to allow automated retrieval processes (spiders) and cache performance optimization (pre-fetching) to work without fear of causing harm. In addition, it allows a user agent to apply appropriate constraints on the automated use of unsafe methods when processing potentially untrusted content.

That is, a PUT or POST (rather than a GET) must be the method used in order to do things like

  • cause a message to be sent (forwarding a UCAN delegation via a separate connection's websocket)
  • cause an untrusted keypair to be associated with a billable email address (which is the outcome of that forwarding, in practice!)

Fixing the HTTP semantics should address all of #348, and is the first step to addressing the security concerns in #333.

Summary of solution

Clicking (or scanning/pre-fetching/previewing/etc.) the link in the email no longer finishes the validation process. Instead, it loads a (harmless to scan/pre-fetch/preview) landing page which simply says "Validating Email" while using JavaScript to auto-complete the process.

My preference would have been to move more of the approval process out of the email and into this landing page. (So rather than auto-approving, this landing page would contain details/context and force an informed clear "Yes, approve this new space" vs. "No, I didn't want this" decision.) However, the team preferred to keep* all that in the initial email and requested that this fix be based on an auto-approval.

Given that preference, this patch is able to fix the core HTTP semantics very self-contained:

  • no changes needed to the email templates (* though still would be good to do)
  • will not break any existing unexpired links at the moment it is deployed
  • is essentially the exact same UX from a user's perspective (they might notice just a little extra blink)
  • does degrade gracefully if user has JS disabled, and any non-browser clients could still trigger the POST ± just as easy as before
  • no changes needed on the w3ui side for this part of the email validation improvements

natevw and others added 2 commits February 9, 2023 12:05
the email validation approval process is now split into two stages: a GET request with no side effects except to load a page that then auto-submits a POST request to actually continue the flow. this fixes the API to follow proper API semantics and thus starts addressing some of #333 and presumably fixes all of #348
@travis travis temporarily deployed to dev February 9, 2023 06:01 — with GitHub Actions Inactive
@travis travis self-assigned this Feb 9, 2023
Copy link
Contributor

@vasco-santos vasco-santos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good to me. Thanks @travis ! It would be quite nice to test it and see if before shipping.

Sadly, we don't have per PR deployments in CF, so we should make sure we test this in staging once this gets merged and are happy with it

@@ -88,7 +88,7 @@ describe('access/authorize', function () {
/** @type {import('@web3-storage/access/types').EncodedDelegation<[import('@web3-storage/capabilities/types').AccessSession]>} */ (
url.searchParams.get('ucan')
)
const rsp = await mf.dispatchFetch(url)
const rsp = await mf.dispatchFetch(url, { method: 'POST' })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, thanks for catching these up! I thought I had successfully re-run the tests but apparently not.

(and this is the exact change intended/expected — for anything out there in the wild that was relying on the old approval URLs they simply need to POST instead of GET after these changes)

@@ -119,7 +119,7 @@ describe('access/authorize', function () {

const url = new URL(inv)
// click email url
await mf.dispatchFetch(url)
await mf.dispatchFetch(url, { method: 'POST' })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the comment say 'click email url', which I assume would usually involve an HTTP GET, but this changes it to 'POST'?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right so this change splits the approval process into 3 pieces (from the 2 that it is now):

  1. an email with a link
  2. a page that gets served on GET that uses JavaScript to redirect users to
  3. a page that gets served on POST that does the actual verification and triggers the websocket data flow

here's the page that gets served by the GET request:

https://github.com/web3-storage/w3protocol/pull/430/files#diff-1a9cb8eda7a1020a96a7a7c0db5406ee04e5f8953fb6c00ab35f3bb8e6fb2519R103

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and for clarity, in this framing our current process is:

  1. an email with a link
  2. a page that gets served on GET that does the verification and triggers the websocket data flow

@travis
Copy link
Member Author

travis commented Feb 10, 2023

Sadly, we don't have per PR deployments in CF, so we should make sure we test this in staging once this gets merged and are happy with it

sweet - agreed! fwiw I did test this locally and it looked basically indistinguishable from the current process because the redirect process happens so quickly.

I haven't tested in our staging environment before, is there a persistent env for that or do I need to do something to get that deployed once this is merged? I'll merge now and we can get this tested in staging today!

@travis travis merged commit d282d6a into main Feb 10, 2023
@travis travis deleted the fix/validate-get-vs-post branch February 10, 2023 02:11
travis pushed a commit that referenced this pull request Feb 16, 2023
🤖 I have created a release *beep* *boop*
---


##
[4.10.0](access-api-v4.9.0...access-api-v4.10.0)
(2023-02-16)


### Features

* add support for access/authorize and update
([#392](#392))
([9c8ca0b](9c8ca0b)),
closes [#386](#386)
* optional override for Postmark email From: field
([#354](#354))
([f6b2350](f6b2350))
* rm /reproduce-cloudflare-error route
([#426](#426))
([99cbd2f](99cbd2f))
* rm upload-api-proxy ability to route to separate environment audiences
([#407](#407))
([5cfe274](5cfe274))


### Bug Fixes

* align postmark/welcome.txt with .html version
([#431](#431))
([a53d6e6](a53d6e6))
* avoid email delegation via GET request
([#430](#430))
([d282d6a](d282d6a))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
gobengo pushed a commit that referenced this pull request Apr 11, 2023
The email validation approval process is now split into two stages: a
GET request with no side effects except to load a page, that then
auto-submits a POST request to actually continue the flow.

## Summary of problem

This fixes the API so as to follow [proper HTTP
semantics](#333 (comment)):

> The purpose of distinguishing between safe [i.e. like GET] and unsafe
[like PUT/POST] methods is to allow automated retrieval processes
(spiders) and cache performance optimization (pre-fetching) to work
without fear of causing harm. In addition, it allows a user agent to
apply appropriate constraints on the automated use of unsafe methods
when processing potentially untrusted content.

That is, a `PUT` or `POST` (rather than a `GET`) **must** be the method
used in order to do things like

* cause a message to be sent (forwarding a UCAN delegation via a
separate connection's websocket)
* cause an untrusted keypair to be associated with a billable email
address (which is the outcome of that forwarding, in practice!)

Fixing the HTTP semantics should address all of #348, and is the first
step to addressing the security concerns in #333.

## Summary of solution

Clicking (or scanning/pre-fetching/previewing/etc.) the link in the
email no longer finishes the validation process. Instead, it loads a
(harmless to scan/pre-fetch/preview) landing page which simply says
"Validating Email" while using JavaScript to auto-complete the process.

This patch is able to fix the core HTTP semantics in a very self-contained way:

* no changes needed to the email templates
* will not break any existing unexpired links at the moment it is
deployed
* is essentially the exact same UX from a user's perspective (they might
notice just a little extra blink)
* does degrade gracefully if user has JS disabled, and any non-browser
clients could still trigger the `POST` ± just as easy as before
* no changes needed on the `w3ui` side for this part of the email
validation improvements

---------

Co-authored-by: Nathan Vander Wilt <nate@calftrail.com>
gobengo pushed a commit that referenced this pull request Apr 11, 2023
🤖 I have created a release *beep* *boop*
---


##
[4.10.0](access-api-v4.9.0...access-api-v4.10.0)
(2023-02-16)


### Features

* add support for access/authorize and update
([#392](#392))
([bf41071](bf41071)),
closes [#386](#386)
* optional override for Postmark email From: field
([#354](#354))
([00db0ec](00db0ec))
* rm /reproduce-cloudflare-error route
([#426](#426))
([158f309](158f309))
* rm upload-api-proxy ability to route to separate environment audiences
([#407](#407))
([eefb6c6](eefb6c6))


### Bug Fixes

* align postmark/welcome.txt with .html version
([#431](#431))
([0d72795](0d72795))
* avoid email delegation via GET request
([#430](#430))
([e0f67e8](e0f67e8))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants