Major sites running unauthenticated JavaScript on their payment pages


A few months ago, British Airways' customers had their credit card details stolen. How was this possible? The best guess goes something like this:

  1. BA had 3rd party JS on its payment page
    <script src="https://example.com/whatever.js"></script>
  2. The 3rd party's site was hacked, and the JS was changed.
  3. BA's customers ran the script, which then harvested their credit card details as they were typed in.

This should have been a wake-up call to the industry. Don't load unauthenticated code on your website - and especially not on your payments page.

If you absolutely have to load someone else's code, check to see if it has been altered. This is done using SubResource Integrity (SRI).

SRI tells the user's browser to check that the code hasn't been changed since the website was published. It looks like this:

<script src="https://example.com/whatever.js"
        integrity="sha384-eP2mZH+CLyffr1fGYsgMUWJFzVwB9mkUplpx9Y2Y3egTeRlmzD9suNR+56UHKr7v" 
        crossorigin="anonymous"></script>

If even a single bit of the code has changed since it was added to the page, the browser refuses to run it.

Who isn't using this

Deliveroo

Gig-economy food flingers add in code from CDNJS.
HTML source for Deliveroo's payment page.

What's especially annoying about this, is that the CDNJS website has a "one-click copy" for SRI.
A drop-down menu with a highlight on "Click to copy SRI".

Spotify

Their payment page loads code from live.adyen.com
HTML code from Spotify.
Adyen are their payment provider - so if they get hacked, credit card details are going to get compromised. But how much easier is it for an attacker to subtly change their JavaScript than to hack their entire mainframe?

The Guardian

Despite being a tofu-knitting member of the bourgeoisie, I am yet to subscribe to teh Gruan. If I did, I'd risk their affiliate tracker going rogue and stealing my organic credit card details.
HTML source of the Guardian's website.
Bonus points for leaving a handy pointer to their internal Google docs...

Fanduel

Sports betting site running unverified scripts from external sources.
HTML source for FanDuel.
They've also got external style-sheets

<link rel="stylesheet" href="//d2avoc1xjbdrch.cloudfront.net/6.26.0/styles/desktop.css">

If an attacker can change the JS or CSS, they could compromise users of the site.

EasyJet

I feel a bit conflicted about this one. You can probably trust Google not to get hacked. Right?
HTML source of EasyJet's website.

Google supports SRI - but doesn't mention it anywhere on their Hosted Libraries site.

British Airways!

Yup! They've not learned their lesson. Three pieces of unverified code running on the payment page.
HTML code.

  • Maxymiser is an A/B testing and analytics tool. Run by Oracle now. Most ad-blockers prevent it loading.
  • Google's reCAPTCHA. If that gets hacked, half the planet is compromised.
  • SiteSeal "proves" your site is secure by displaying a image. No, I don't understand that either.
An SSL badge which proves nothing.

This does not make the site magically secure.

All three of them are highly trustworthy. But if you're BA and you've already been bitten by bad security practices, doesn't it make sense to go full "belt-and-braces"?

...and more?

These are just a small sample of the sites I've found. SRI has been available for two years and it still isn't being used enough.

Responsible Disclosure

I've reported this issue to a few sites by using responsible-disclosure aggregator HackerOne.

Typically, my warning goes unheeded with a response like:

Based on your initial description, there do not appear to be any security implications as a direct result of this behavior, this is an Informational issue at best, unless you can prove those third-party domains can be compromised in any way.

or

This appears to be more of a risk acceptance rather than a vulnerability. Although there is no PoC for this report, I will forward the information to the customer and see where to go from there.

That's fair enough. I'm not expecting a huge payout and it is only an informative report; I can't prove that the external sites are vulnerable. But there really ought to be a concerted effort to make payment sites as secure as possible.

This needs to be taken seriously. If you're handling users' details, you need to take every possible step to keep them secure.


Share this post on…

12 thoughts on “Major sites running unauthenticated JavaScript on their payment pages”

    1. @edent says:

      Not annoying at all. I believe there is an open bug on Jetpack about that. Luckily this site doesn't ask for your credit card details.

      Reply
  1. Bob says:

    Interesting find.

    I suppose some issues with using SRI is:
    - how so the server admins get notified there is a newly updated JS? And how do they coordinate the switch-over so there is no downtime.

    Thoughts?

    Reply
    1. @edent says:

      The same way they do now. You'll notice most of those scripts are versioned. Any changes should be properly tested.

      Reply
  2. Amy says:

    Super interesting! Thanks for writing!
    So, if you use an outside service for payment processing (like spotify for example) are you suggesting they use SRI link for that script? Or to avoid using outside services at all?

    I guess this line isn't clear to me:

    But how much easier is it for an attacker to subtly change their JavaScript than to hack their entire mainframe?

    Reply
  3. @G2glvr says:

    I read the response comments from those you reported to, I kinda get their responses especially in the case of the Spodify example since their payment JS provider has been certified by a QSA (I know their assessor, PSC, they are good, thorough guys) but your point is well taken, you don't sometimes know the security of the script provider.

    I liked the article and the one linked too:
    https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity

    SRI is good and it would work for included script code that is very static. If once it changes and the hash does not match then something goes bad on the payment page, especially if you are getting the whole payment script from a provider (like Spotify does in the example). Now payment page type companies should be really good at not changing their scripts lets hope. I guess if the SRI failed you could write code to fire off an email to some developer or write an entry in a well monitored log so that it could be fixed quickly, but the payment page may fail in some way. Having it fail if a hacker changes the script is awesome, having it fail because the provider company is just updating or improving code is more difficult. Hard to potentially notify all your potential link users and get them to change before the script changes…but possible.

    An alternate would be to have two script places to go to in order to keep functionality going either until the hacked script is fixed or the updated script can be verified by the real provider. Maybe the payment page company provides an “always works but not so cool looking” backup script that they promise to never change the code you can switch to in an error or you could have an alternate company to go get a script (but now you are paying twice for services…depends on how much money you process I guess, may be worth it). Or, just throw up an error message saying “we can’t take your money today, try tomorrow”.

    Another problem I see is that SRI is not supported by Edge (partial support) or Internet Explorer (as of Nov 6, 2018 at least) which I expect is still a pretty big portion of the browsing community. So a browser side solution as of now is not a very comprehensive control.

    Reply
    1. Best practice for the script provider is to follow the ancient wisdom "Cool URIs don't change". Version their API, never change an existing version, and retain older versions for some time. (It's also a performance best practice.)

      The developer could still check if the script loaded and fall back to an alternative provider, just as it would if the script server was offline.

      Reply
  4. Hector Eryx says:

    Another option would be hosting locally on their own backend the payment scripts and redirect all the call through a proxy to them. If they already hacked your backend, you have way larger problems than a rogue JS.
    I know it might not be possible for most cases, but for things that you now must be controlled it is better to have them under your control. If for whatever reason their JS code needs to change, it is the engineering team (development & infrastructure) who should be notified first.

    Reply
  5. Alex says:

    We're going through this process at the moment of trying to get as many of our external scripts configured with SRI checks, which is easy enough for the devs to stick in but the most common push-back we're having is that a tonne of script sources do not have CORS switched on, which means you can't implement SRI on those scripts. Sure you could host the script locally but then that defeats the point of CDNs. In other instances, some scripts are not versions (plenty of Google ones) so the dev's don't want to risk sticking SRI on for the site to break when the host updates the script. Any thoughts on how to handle these situations?

    Reply
    1. says:

      You don't need a CDN for JS. There are so many CDNs that the chances of your user having that exact one cached are pretty minimal, I would have thought.

      Reply

What are your reckons?

All comments are moderated and may not be published immediately. Your email address will not be published.Allowed HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <p> <pre> <br> <img src="" alt="" title="" srcset="">