SameSite
cookies and 3-D Secure checkout flows
Fixing issues with 3-D Secure is a verification service for card payments taken online. From the point of view of a user or site, the flow generally looks like this:
- User enters their payment details on the site’s checkout;
- Site embeds or redirects to the appropriate page to verify that payment method (dependent on the user’s card, e.g. Visa, Amex, etc.);
- Third-party does its verification (via SMS or similar);
- Third-party returns the status to the main site, generally via a
POST
request.
The 3-D Secure interface may be displayed to the user in a number of different
ways: an iframe in the page, a pop-up, or a full page redirect. The common
pattern with all of these is a cross-site POST
request initiated at the end of
the 3-D Secure verification back to the original site. This is what causes a
challenge with the recent updates to apply a SameSite=Lax
by default behaviour
to cookies without a SameSite
value. In other words, cookies without a
SameSite
attribute will not be included on the 3-D Secure POST
callback.
This means that final POST
request may appear to your server as a new session,
meaning your site may attempt to set new cookies for the user or treat them as
if they were not logged in.
There are a number of options to fix this based on your site's archicture.
POST
request
Do not require cookies on the returning Generally, the cookies on your site for maintaining the user's session are only
intended for first-party use. You do not want them sent on cross-site requests
as this loses the security benefits of implementing SameSite=Lax
by default,
e.g. it makes Cross-Site Request Forgery attacks easier.
Check if you are able to process the returning POST
request without the need
for any of your session cookies. For example, if the 3-D Secure challenge was
included in an iframe
then you may only need to display a status message
within the frame while sending a postmessage()
call to your top-level window
to complete the transaction.
If the returning POST
request is a top-level navigation / redirect and you
would like to display session-specific content to the user, then you may want to
consider the
POST
/Redirect/GET
pattern.
That is:
- Process the incoming
POST
request without cookies recording any results you need in the back-end - Respond with a
303
redirect to an internal page for the result of the transaction, e.g./transaction/123/success
or similar. - The subsequent
GET
request is considered a "safe" method and will include anySameSite=Lax
cookies.
This means that the redirected request will include your sites first-party cookies and can be used to display session-specific content.
POST
request
Create short-lived, cross-site cookies for the returning If you do need cookies on the returning POST
request, they need to be marked
with the SameSite=None; Secure
attributes. You should not make your default
session cookies available to third-parties, so instead consider creating a
short-lived cookie that stores a token that allows you to link the incoming
POST
request to the associated user or transaction on your back-end.
This flow might look like:
- Create a short-lived (e.g. 15 minutes) cookie that will enable you to identify
the transaction:
Set-Cookie: transaction-token=abc123abc; SameSite=None; Secure; Max-Age=900
- Check the
Origin
orReferer
header on the returningPOST
request to ensure it matches the expected source. - Use the token to initialise the existing session.
- Mark the token as used in your back-end to ensure it is not used again.