Protect Yourself from Magecart Using Subresource Integrity

Magecart has become a big issue in web application security the past few days. They have skimmed credit card information from British Airways and more recently have been injecting into JavaScript assets served by Feedify. Modern websites use many resources to provide the rich experiences customers have come to expect. However, if you don’t directly host or control those resources you are vulnerable to a provider getting attacked and having malicious code injected into the assets you were previously consuming.

We’ve previously written about Subresource Integrity but I’d like to reiterate the benefits and show how to get started securing your assets. Subresource integrity is an official browser feature that allows websites to ensure the integrity of resources loaded from external sources, such as Content Delivery Networks (CDNs). This is a common technique used by websites to speed up the loading of assets, including common JavaScript libraries like jQuery, Google Analytics or’s analytics.js.

Since these JavaScript libraries are uncontrolled external code that is being run in the context of your web application, their content must be audited and trusted. Subresource integrity serves to mitigate this issue by ensuring that all loaded resources contain the exact content expected by the website. This is done through the use of a cryptographic digest or hash, computed on all fetched resources, that is then compared against a digest that is served with your page. This provides the browser the capability of detecting resources that have been tampered with, allowing it the opportunity to abort the loading of the resources before any malicious code is executed.

Protecting a resource is as easy as adding the "integrity" attribute to an asset’s HTML tag:

<script src=""

Since we previously wrote about Subresource Integrity support has grown from browser vendors and all modern desktop browsers support it. We highly recommend this solution but it comes with a caveat - if the external entity changes the JavaScript for a bug fix and doesn’t notify you then your integrity hash won’t match. This is by design but you may want to look for a mechanism to link to a specific version of a library. You may also need to evaluate your risks on a per-page basis. Many of the popular web frameworks provide libraries that make it easy to enable subresource integrity on your assets, and further instructions on making use of the technology are available on the Mozilla Developer Network. The SRI Hash tool provides an easy way to calculate integrity hashes for your assets.

I hope you are inspired to integrate Subresource Integrity into your website. All Tinfoil Security scans flag external resources that are not protected by subresource integrity. Give it a try by signing up for our 30-day free trial.

Ben Sedat

Ben Sedat is the Engineering Wizard of Tinfoil Security. He's a bit of a blend between a traditional software engineer (builder) and security engineer (breaker). He spends a lot of time thinking about security: both detection as well as creating solutions for the security issues that exist in software and the internet. He also plays lots of video games. Lots.

A Quick Guide to the Complex: Ecto.Multi

Ecto.Multi, a data structure added in Ecto 2.0, is an extremely useful tool for creating and executing complex, atomic transactions. This very brief guide will cover a few of the most useful methods associated with Ecto.Multi and when to use them.

Common Uses

insert(multi, name, changeset_or_struct, opts \\ [])
The most straightforward way to use Ecto.Multi is to chain individual changesets together. insert, update, and delete functions are available and all behave as you might expect them to, with all operations are executed in the order in which they are added. You can imagine a transaction dealing with a user signing up via an invitation email might look something like this:
|> Ecto.Multi.insert(:user, user_changeset)
|> Ecto.Multi.delete(:invitation, invitation)
|> Repo.transaction()

What might have been be two separate database transactions has been condensed into a single, atomic transaction, with Ecto.Multi handling rollbacks when necessary. But what about when one operation relies on the results of a previous one?


run(multi, name, fun)

Run is an extremely versatile method that adds a function to the Ecto.Multi transaction.  

The function must return a tuple in the for of {:ok, value} or {:error, value}, and, 

importantly, is passed a map of the operations processed so far in the Multi struct. This means we can key into the changes created by previous operations, and use the those values while executing any code we like. The transaction creating and sending the invitation mentioned above could look something like this:

|> Ecto.Multi.insert(:invitation, invitation_changeset)

  |>, fn multi_map ->

  send _invite_email(multi_map)


    |> Repo.transaction()


append(lhs, rhs)
append is a handy way to combine two Ecto.Multi structs into a single atomic transaction. One potential pattern is to compose multiple functions that return Ecto.Multi structs, and combine them as needed. As noted above, operations are executed in order, so if you want the appended struct to be executed first, you’ll want to use prepend instead.

def create_and_send_invite(invitation_changeset)

|> Ecto.Multi.insert(:invitation, invitation_changeset)

  |>, fn multi_map ->

  send _invite_email(multi_map)



def clear_expired_invites()

        |>, fn () -> 




def invite_user(inviation_changeset)

invite_multi = create_and_send_invite(invitation_changeset)

clear_expired = clear_expired_invites()

|> Ecto.Multi.append(invite_multi)

|> Ecto.Multi.append(clear_expired)

|> Repo.transaction()



merge(multi, fun)

Similar to run, merge will execute a given function and any arbitrary code associated with that function. Unlike run, this function is expected to return a multi struct whose operations will be merged into the multi struct passed to it as the first parameter.

def add_user_to_organization(multi_map, organization)

    user = multi_map[:user]

        |>, organization.add_user(organization, user)


def create_collaborator(user_changeset, organization)

|> Ecto.Multi.insert(:user, user_changeset)

|> Ecto.Multi.merge(:add_to_org, fn (multi_map, organization) -> 

          add_user_to_organization(multi_map, organization)


|> Repo.transaction()


Peter Ludlum

Peter is a Software Engineering Intern at Tinfoil Security. A recent graduate of App Academy, he enjoys nothing more than bringing beautiful (and functional) web pages to life. When he isn't coding, Peter is usually lost in a book or strumming out a new tune on the ukulele.

Useful Flags for Chromedriver

Chromedriver is a powerful and flexible tool for remotely controlling a browser while testing your site for use interactions or as part of a crawler or other automated application. It has a lot of configuration options in the form of flags passed in on load which allow you a great deal of control over chrome or chromium’s behavior. Unfortunately, there are a lot of flags and navigating them can be a bit of a slog, so I wanted to outline a few flags which I’ve used in my own chromedriver adventures and found generally useful.



As you might expect, these modify the size of the browser window when chromedriver starts up. Trying to see what the browser is interacting with while it’s running? Toss in a --start-maximized for visibility. Trying to watch debug traffic or spec results in a terminal while chromedriver is running, or perhaps multiple browser windows running together? Set --window-size to a small value so you can keep things in sight (and, if rendering is wholly unnecessary, there’s always --headless).



If you want to use a proxy with your browser, that can be passed in as a flag to the browser. This does, however, result in a lot of SSL errors if you’re setting up a man-in-the-middle proxy (to analyze traffic, for instance). In normal browser operation, this throws up a blocking page to make sure the user knows their secured traffic is passing through a third party, but that can be an inconvenience if your automated browser needs to access https resources. Using --ignore-certificate-errors gets around this issue handily, allowing the browser to continue without that roadblock (though the page will still be marked insecure on the address bar).


Starts the browser in incognito mode. This prevents the cookie and local storage of browser windows from carrying over into one another, which is useful if you have tests running in parallel, for instance.


The devtools pane opens at start up. This is useful for checking for  errors in the javascript console, and is extra, extra useful if you’re chasing down problems with network requests in testing, as it will start recording immediately, rather than needing to grab the window, open devtools and then reload the page (possibly interrupting your own tests or other app activities).


This is a somewhat unusual one. Google’s account managing functions in chrome and chromium are built as gaia apps, and will try to ping the Google servers to sync account information. This can leads to some spurious open network connections if your automated application is monitoring those to check page loads. Routing this connection to a random external url will cause chrome to fail to boot, but if you really want to suppress this connection, you can set this value to a chrome:// url with a fake address. This will cause the browser to talk to itself when trying to sync, which will stop that external connection (but also break that piece of browser functionality).

These are just a small, small subset of available flags and options. A fairly complete list is available at this site for all your automation needs. Hopefully some of these will prove useful on your next browser automation project. Safe building!

Alex Bullen

Alex is Tinfoil Security's Top-Shelf Programmer (and fetcher of things from high shelves). A former psychology wonk and recent App Academy grad, Alex endeavors to treat every challenge as an opportunity to improve his code-fu. When not busily building blocks of precisely put code, you can find him reading fantasy novels or practicing kung fu.

Disclosing Vulnerabilities: How to Avoid Becoming The Next Data Breach Headline!

When it comes to disclosing vulnerabilities to enterprise companies, they seem to prefer the “hear, speak, see no evil” strategy, and the conversation often ends up looks something like this:

Me: Hey, we found a vulnerability on your site, wanted to let you know so you can fix it.
You: Cool thanks for letting us know, but we’re going to try to sue you for telling us.
Me: ???

You can see how this would discourage those who find vulnerabilities from disclosing them to enterprise companies, however, if we take a look at some of the major data breaches that have happened over the last few years, you can see how there is a need to encourage transparency in order to address vulnerabilities, rather than ignoring them or even threatening those who bring them to light. 

For example, you may have heard about Panera Bread leaking millions of customers’ data, recently. It’s a story that is depressingly familiar: the initial report of the vulnerability was dismissed as a scam, then it was ignored, then “fixed” by a token patch that did little to actually prevent the data from being exposed. For at least eight months, nearly 37 million Panera Bread customers had their data exposed for anyone to collect.

Why does this happen? Why do companies have such a visceral reaction when this kind of news is presented to them? We have seen similar reactions when we have disclosed vulnerabilities to large enterprise companies (some very well known)! After one particular disclosure, we even got threatened with a lawsuit for letting a company know they had a vulnerability, even though we helped them by providing a suggestion on how they can fix it.

Let’s be clear here, even if a company is not a current customer, we reach out to them immediately after finding a vulnerability to let them know, so they can get the issue fixed. All too often, however, we get treated with hostility and anger: “How dare you tell us we have a problem!”.

This is a very dangerous mindset and culture that seems to exist at the leadership level of some of these companies.

So how can we change this?

It has to start at the top. CISOs need to adopt a better mindset and culture around handling incoming help. Yes, some people may use it as a sales tactic, but it does not negate the fact that the vulnerability is there. All disclosures should be taken seriously and, dare we say, welcomed.

Some companies are great; they even have bug bounty programs that offer rewards to an individual for finding issues for them. Here at Tinfoil, we see it as our obligation to be good stewards of the community and always share any potential threats regardless of whether the target is a current customer, and with no obligation to become one. We just want you to have safe and secure applications! Why? Because chances are, you hold the personal data of some of our team members and customers.

Tinfoil Security has decided to start a campaign every time there is an avoidable breach with the #UseTinfoil that will be applied when we know a tool like ours could’ve been used to avoid your data from falling into the wrong hands!

Peter Ludlum

Peter is a Software Engineering Intern at Tinfoil Security. A recent graduate of App Academy, he enjoys nothing more than bringing beautiful (and functional) web pages to life. When he isn't coding, Peter is usually lost in a book or strumming out a new tune on the ukulele.

Building values to love your team

I love my team. I go home and say it out loud nearly every single day. Yes, some days in a startup are frustrating - a sale might go poorly or we’re set back on an engineering timeline, but by the time I leave I have a solution and it’s often because my team works together to come up with a unique way to solve any problem.

I started Tinfoil with the explicit goal of learning something new every day. Some days I learn much more than other days, and the few days I don’t learn something new it’s always my fault. I thrive off of surrounding myself with individuals with a similar drive, especially when all come from very different backgrounds and experiences. Every founder I’ve met has a different goal: some want to learn new things; some want to become famous, or become rich, or change the world, or invent. An organization’s blood is determined by these early goals.

A mismatched employee at the early stages will slowly affect everybody. Somebody has to not only be a fit for Tinfoil, but Tinfoil should also be a fit for them. To help any prospective employee understand us, we created values to codify what makes somebody successful (and love it) at Tinfoil.

Tinfoil’s values guide every employee to make a decision quickly as to whether or not somebody may be a fit for our company. Our values are created as a team and reassessed each year,  during our annual retreat. This is the one work item over a long camping / hiking / bonding weekend. We have a concrete structure around creating and reassessing our values, and so far it’s worked wonderfully. Our approach is to avoid anything that is based solely on emotion and anything that could have a potential HR implication. The other main goal for us was to have our values be debatable; we needed to be able to create a cogent and acceptable argument for the opposite value. Every team values something different, and a value like“innovation” is difficult to argue against (what startup doesn’t want to be innovative?).

Our values today (after iteration over a few years) are:

  • Collaborative
  • Community
  • Curiosity
  • Hacking
  • Integrity 

We are friends with some startup founders who have similar values and some with those that are completely opposite. Collaborative, for example, can be opposed by folks who enjoy making decisions on their own to expedite product development. Curiosity encourages us to try new things (like new programming languages that may be useful), rather than sticking to what we already know. In contrast, many organizations choose to be a Python shop through and through, helping to propagate knowledge and have all engineers on the same page.

Each value we create gets expanded upon with bullet points of things we care about. We often give examples of the value and always denote anti-value behaviors and opposite values.

For example:


  • We strive to build an environment where we’re always willing to pay it forward, even if we have no expectation of any return.
  • We are actively generous to any community we are a part of.
  • We like to lead and help build communities, rather than just support ourselves or follow along.
  • We strive to open-source as much as we can, without compromising company IP.

Examples: VPN builder, rails check, Poodle check, talks we give at conferences, hackathon advisory, donation of time or money to charity, SVLG, consulting with people on open-source or our customers, answering stack exchange questions, etc.

Anti-community behavior

  • Tagging along in a community just for the name.
  • Dismissing the fact that local non-tech communities have an impact on your life.
  • Looking down on any body in a group that you do not ascribe to. (For example, education, class, wealth, experience, race, etc.)

Opposite value: focused entirely on money, self-serving, unwilling to pay it forward 

If somebody on our team starts exhibiting an anti-value behavior, our rule is for anybody to be able to call you out (I even encourage interns to call me out if I’m working against what we care about as a team), you acknowledge, and then we all move on.

Tinfoil is an extremely close-knit team. I care so much about everybody having a voice and empowering them to make important decisions to shape our team. If they can weed out poor culture fits earlier on, it saves us much more time and every hiring decision always becomes easier. Since we implemented our values, we’ve had far fewer debates over whether or not somebody would thrive at Tinfoil. We’re not about survival, but building you up to where you love your job, team, and what you’re learning; we want you to grow, learn from us, and teach us. 

Ainsley Braun

Ainsley Braun is the co-founder and CEO of Tinfoil Security. She's consistently looking for interesting, innovative ways to improve the way security is currently implemented. She spends a lot of her time thinking about the usability and painpoints of security, and loves talking with Tinfoil's users. She also loves rowing and flying kites.