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