Sharing the Philosophy Behind Shopify's Bug Bounty

Jaime Woo

Bug bounties have become commonplace as companies realize the advantages to distributing the hunt for flaws and vulnerabilities among talented people around the world. We're no different, launching a security response program in 2012 before evolving it into a bug bounty with HackerOne in 2015. Since then, we've seen meaningful results including nearly 400 fixes from 250 researchers, to the tune of bounties totalling over half a million dollars.

Security is vital for us. With the number of shops and volume of info on our platform, it's about maintaining trust with our merchants. Entrepreneurs are running their businesses and they don't want to worry about security, so anything we can do to protect them is how we measure our success. As Tobi recently mentioned on Hacker News, “We host the livelihoods of hundreds of thousands of other businesses. If we are down or compromised all of them can't make money.” So, we have to ensure any issue gets addressed.

We believe part of this success comes from the high minimum payout we offer. This wasn't by accident: we wanted to make sure researchers to always know it’d be worth their time, even to uncover bugs that are minimal impact or difficult to exploit. Our minimum bounty is $500, and we saw it as an investment in getting the best researchers interested in our platform. Not all hackers may have heard of our platform, notes Andrew Dunbar, our Director of Risk and Compliance, so we’re willing to overspend so it’s worthwhile for them to learn and familiarize themselves with us.

We also think it has to do with the level of transparency in the process and in our thinking. After resolution, we aim for full public disclosure of issues, and we believe that this level of communication has helped us build a good reputation in the community. In fact, we've even had researchers apply for jobs. A lot of companies don't want to expose or disclose vulnerabilities, because it's seen as a weakness, but we don't see value in that. In addition, we work very hard to explain why something is or isn't an issue, because it's an educational opportunity for people researching the platform.

Bugs come in all types. We want to share some of the vulnerabilities our bug bounty surfaced with bounties ranging in the low thousands up to $20,000.

The case of the invoice swap

The fall of 2015, researcher dvl discovered a simple hack that exposed merchant information. It was a bug that allowed users to view the monthly invoice of other stores by changing the store ID in the URL. This revealed personal information, since the invoice contained the full name and address of the store owner and all line items for the invoice. Our engineering team resolved the issue that same day, and we publicly disclosed it two days later with the researcher scoring a $4,000 bounty.

The case of the sneaky catfish

Along a similar vein, researcher zombiehelp54 discovered last summer a way to catfish our support agents, noting “an attacker can steal users' livechat access token and use it to login and chat with the support agents as them.” This bug could lead to someone potentially gaining improper access to sensitive user information. Again, the same day, our team crafted a fix by strengthening user validation, using a white list containing only the URLs that are actually used by our other applications, published the issue, and zombiehelp54 received a $1,500 bounty.

The case of the remote code execution

Sometimes the bugs run deeper, especially as we opened up bug bounty to shopify-scripts. Researcher isra17 discovered a vulnerability that opened us up to a crash or denial of service.

The function to_h will call the C function mrb_ary_to_h. This will iterate through the elements of the array. If an element is not of type Array it will call attempt to call to_ary method of that object. If to_ary does not return an array, the function will raise a ruby exception with the class name in the exception message.

However, the code does not properly check that the array length was not modified during the call of to_ary. The vulnerability is triggered when the array is shrunk during call to to_ary, letting mrb_ary_to_h read an out of bound object to get an element classname. A crash or or denial of service can be triggered by neutering the array in the to_ary call. A mrb_obj_iv_set call done on the controlled class pointer can be used to have a memory write leading to RCE.

This bug was considered critical, the top level of severity, and its discovery led to a bug bounty of $20,000.

It’s humbling to have researchers discover bugs. We share them because contrary to what a lot of companies believe, we believe it engenders more trust and faith from our merchants, not less. Something inevitably falls through the cracks and having researchers discover and share them with us, rather than exploiting them, is a win-win situation. We've slowly continued rolling out what bug bounty hunters can touch in Shopify's platform. If you’re interested in participating, you can check us out on HackerOne.