Open fstanis opened 4 years ago
Would it be possible to include the recipient email address in the JWT payload? That way the server endpoint can identify which user the request was made on behalf of.
Example:
{
"iss": "https://email.example",
"aud": "sender@sender.example",
"rcpt": "user@email.example",
"iat": 1516239022
}
Thanks for raising that! We discussed something to that effect on a few occasions - the issues previously raised were:
user@email.example
actually arrive to user@emailclient.example
? This can happen in a few different ways:
(note that not all of these are supported by all email clients supporting AMP, e.g. forwarding AMP emails isn't possible, but we don't want to inadvertently define this feature in such a way it limits other email features)
Finally, even if these are addressed, we'd prefer to encourage senders to instead generate limited-use ID tokens (in both scope and time), as this is a good overall security practice.
That makes sense, glad to hear it was discussed! The canonical email problem is a concern. It could potentially be solved by adding a header (something like X-AMP-RCPT
) to the email?
I still think it is worthwhile to do for a few reasons:
Obviously, these are very rough thoughts and it's always good to lean towards more secure solutions 😅
Thanks, these are some thoughtful points. At this point, we're in territory that wasn't discussed before at a WG meeting, so what follows is just my personal opinion. :slightly_smiling_face:
Adding a new header is probably not a good idea. It wouldn't (by default) be included in the DKIM signature, opening it up to potential tampering. This would mean ESPs would need to adjust and always include it and also potentially email clients requiring this header to be included, so a lot of work on the ecosystem as a whole.
When it comes to engineering effort... this is a double-edged sword. My view is that we don't want to decrease the effort to do something potentially dangerous (using an email as opposed to a limited use token). Certainly, we don't want to limit it per se, but authentication is a serious component and I'd prefer that whomever uses it in an email has made the conscious decision on what they want to use as an identifier.
It makes it easier for email platforms like SparkPost and SendGrid to support AMP tracking for their customers.
I'm not sure I understand this point: these platforms already support modifying the URL for things like links and images, so I'd say they'd have no trouble also inserting a limited use access token to e.g. amp-list before sending.
Background
Authentication using ID tokens
On a high level, when a user is authenticated, their client attaches a unique identifier (referred to as ID token in further text) to HTTP requests it sends. This ID token is resolved on the server side and maps to the user's identity.
The preferred way to attach ID tokens to HTTP requests is using HTTP cookies. Email clients, however, block HTTP cookies for privacy and security reasons. This means that emails can neither read existing cookies from the sender's website (and determine if the user is already logged in) nor set new cookies.
Using ID tokens in the URL
Because cookies can't be used, emails attach ID tokens to the URL itself. As the ID token is part of the email content, it's only visible inside the recipient's inbox. In other words, the ID token is tied to the user authenticating with their email client, making this a viable method to assert the user's identity.
The most common existing use of this method are password reset emails. For example, a website may send an email that contains a hyperlink to
https://website.example/accounts/resetpassword?user=17436e4c45c62e88cfe78fea37ce002f
where17436e4c45c62e88cfe78fea37ce002f
is an ID token that uniquely identifies the user attempting to reset their password. In addition to being uniquely associated with the user, this token is limited in scope (it can only be used for one purpose, resetting the password) and is time-limited.Authentication in AMP emails
The same method of authentication (using ID tokens in URLs) can already be used in AMP for Email. For example, an
<amp-list>
may similarly use an ID token to identify the user:In this case, the server endpoint can use
b14a9d005403979154389c851e1a9fb2
to uniquely identify the user and return data personalized to them.Session hijacking attack
Assuming an attacker somehow successfully obtains the email source code outlined above, they are able to manually make an HTTP request to the endpoint used by
<amp-list>
and receive the list of recommendations for another user.This document proposes a method to mitigate this type of attack: email client assertion tokens. Separate measures can and should be taken to minimize the possibility of this attack happening in the first place, but this is beyond the scope of this document and proposal.
Email client assertion tokens
Overview
An email client assertion token allows an HTTP server to differentiate between HTTP requests made from within an AMP email rendered inside an email client from requests made through other means (web browser,
curl
or any other HTTP client).Specifically, an email client assertion token is a type of digital signature that can only be generated by the entity hosting the recipient's inbox, determined based on the recipient email's domain (e.g.
email.example
foruser@email.example
).High level token generation and verification flow
The types of HTTP requests eligible to contain an email client assertion token are:
GET
requests made usingamp-list
when theinclude-assertion
attribute is setGET
andPOST
requests made usingamp-form
when theinclude-assertion
attribute is setA prerequisite for implementing email client assertion tokens is for the email to already be using a proxy server for these requests, as the tokens can only be generated on the email client's server-side.
When a request is made through one of these two methods, the following flow happens:
src
oraction-xhr
attribute) and adds the assertion token to theAMP-Email-Assertion-Token
HTTP header.Detailed token specification
Email client assertion tokens are JWT-based tokens signed with the email client's private key and generated in the proxy server used for
amp-list
andamp-form
requests. This token contains:audience
fieldhttps://email.example
) in theissuer
fieldissued at
field.Example token payload:
The public key used to verify the token is hosted on the HTTP server the email client controls in the
.well-known
folder per RFC 5785, specificallyhttps://<key_domain>/.well-known/ampforemail-keys
The
key_domain
represents a domain controlled by the email client. The process to determinekey_domain
is as follows:user@email.example
indicates the domain isemail.example
. This domain is referred to asrecipient_domain
.recipient_domain
. If there's a record in the format ofampforemail-keys-domain=<domain>
, then<domain>
is thekey_domain
. For example, ifemail.example
has a TXT record that equalsampforemail-keys-domain=emailclient.example
, thenemailclient.example
is thekey_domain
. If thekey_domain
is found, skip the remaining steps.recipient_domain
in order of priority.ampforemail-keys-domain=<domain>
, then<domain>
is thekey_domain
.If a valid
key_domain
is found, then a GET request is made to fetch the file athttps://<key_domain>/.well-known/ampforemail-keys
. If the HTTP request fails, treat the JWT payload as invalid.The response from this HTTP request can be cached locally. It's expected that the HTTP server returns an
Expires
header to indicate how long the public key can be cached for, but other standard mechanisms (such asLast-Modified
andETag
) can also be used.ampforemail-keys
contains a PEM encoded public key in the ASN.1 as defined in the X.509 standard. This key is used to verify the signature of the JWT data.The presence of a valid, signed JWT token in the request guarantees to the sender that the HTTP request has been made by the entity that controls the domain of the recipient's email.
Fallback for non-supporting email clients
Initially, email clients are expected to opt-in by adding
data-amp-assertion-token-opt-in
to the<html>
element. This signals to the AMP runtime that the email client supports this feature.The JWT token is only attached if requested in the markup, via the
include-assertion
attribute, e.g.<amp-list src="..." include-assertion>
or<form method="post" action-xhr="..." include-assertion>
. If the email client doesn't support assertion tokens (data-amp-assertion-token-opt-in
not present in<html>
), then the AMP runtime never makes XHRs for elements that haveinclude-assertion
and immediately displays thefallback
foramp-list
.If it supports this feature, an email client may choose to always include the assertion token (even when
include-assertion
isn't present). The purpose ofinclude-assertion
is to indicate a server endpoint requires assertion tokens, rather than to limit sending them.Caveats and limitations
amp-list
andamp-form
. There is no safe way to generate a JWT payload without a server-side component.