
Reverse-engineering the Starbucks ordering API - nickplee
https://blog.tendigi.com/starbucks-should-really-make-their-apis-public-6b64a1c2e923
======
dsacco
Solid writeup. From someone who does/did a lot of this professionally:

1\. Android typically _is_ easier for this kind of work (you don't even need a
rooted/jailbroken device, and it's all Java/smali),

2\. That said, instead of installing an entire framework like Xposed that
hooks the process to bypass certificate pinning, you can _usually_ just
decompile the APK and nop out all the function calls in the smali related to
checking if the certificate is correct, then recompile/resign it for your
device (again, easier on Android than iOS),

3\. Request signing is increasingly implemented on APIs with any sort of
business value, but you can _almost always_ bypass it within an hour by
searching through the application for functions related to things like "HMAC",
figuring out exactly which request inputs are put into the algorithm in which
order, and seeing where/how the secret key is stored (or loaded, as it were),

4\. There is no true way to protect an API on a mobile app. You can only make
it more or less difficult to secure. The best you can do is a frequently
rotated secret key stored in shared libraries with weird parameters attached
to the signing algorithm. To make up for this savvy companies typically reduce
the cover time required (i.e. change the secret key very frequently by
updating the app weekly or biweekly) or by using using a secret key with
several parts generated from components in .so files, which are significantly
more tedious to reverse.

~~~
saghm
> That said, instead of installing an entire framework like Xposed that hooks
> the process to bypass certificate pinning, you can usually just decompile
> the APK

I remember reading something years back about Java decompiling, and I believe
it said that all Java code is decompileable except for inner classes and
nested try-catch. Assuming my memory and the source are correct (which might
not be the case), why hasn't it become standard practice for developers who
don't want their app reverse-engineered to just put every class inside a
wrapper class? I can imagine there could even be tools for doing this at
compile time so that you wouldn't need to manually deal with the indirection
when writing the code.

~~~
micaksica
The standard way of frustrating decompilers for Android applications is with
heavy obfuscation using ProGuard [1] or DexGuard. [2] IMO, DexGuard is a real
pain in the ass to reverse around. If you aren't dealing with heavy
obfuscation, decompiling APKs is trivial using jadx. [3]

In general I have found that most smaller app developers don't obfuscate at
all, and often you can find hardcoded keys/secrets in these smaller
applications.

[1] [https://developer.android.com/studio/build/shrink-
code.html](https://developer.android.com/studio/build/shrink-code.html)

[2]
[https://www.guardsquare.com/en/dexguard](https://www.guardsquare.com/en/dexguard)

[3] [https://github.com/skylot/jadx](https://github.com/skylot/jadx)

~~~
djmips
One reason is they have to test and potentially debug the obsfucated build
which is a lot of extra work!

------
joombaga
I did this with the Papa John's webapp a while back (which was waaaay simpler
btw). They limited duplicate toppings to (I think) 3 of the same, but
"duplicate_item" was just a numerical property on the (e.g.) "bacon" object.
Turns out you could just add multiple "bacon" members to the toppings array to
exceed the limit, and they didn't charge for duplicates, so I ordered a pizza
with like 50 bacons.

It definitely didn't have 50x worth of bacon, but it did have more than 3x,
maybe 5x-6x. The receipt was hilariously long though.

~~~
13of40
Whenever you do this sort of thing, you should check whether they do negative
number checking on the client side or in the API itself. You might be able to
get 1 pizza with bacon and (1) pizza without, all for the price of the bacon.

~~~
Lazare
I work for a company with a similar sort of online ordering API, and a while
back some enterprising person discovered that:

1\. Our API had a hidden "tip" feature; we hadn't exposed it in the storefront
yet, mostly because the developer in charge of it had left with it halfway
finished, and nobody had picked it back up yet

2\. But the API itself was perfectly functional. If you manually submitted an
order to the API with a tip set, it would be reflected in the total, would
bill your credit card, etc.

3\. Except (I'm sure you saw where this is going...) the now-departed dev
hadn't added any validation yet. So if you ordered $40 worth of pizza and
added a negative $39 tip...boom, nearly free pizza.

We learned a few valuable lessons for the cost of buying a college student a
couple of pizzas. :)

~~~
dx034
At least you didn't sue them for that. There are enough companies that
would've done so..

~~~
Lazare
We actually debated offering them a job interview when they graduated. If
they'd contacted us first, we might have. :) Instead we just fixed the bug,
called it a day, and spent a few days reviewing the rest of the API for weak
validation.

------
rkunnamp
The dominos ordering app in India had a terrible flow a while back. Once the
products are added to the cart and proceeded to checkout , the flow was as
follows

1\. First a payment collection flow is initiated from the browser (asking
Credit Card details, pin etc)

2\. The payment confirmation comes to the browser

3\. The browser then places the order(the pizza information) to another api
end point, marking the payment part as 'Paid'

The thing is, one could add as many pizzas to the cart in a different tab,
while the original tab proceeds to payment. The end result is, you get to pay
only for pizzas that were initially in the cart, but could get any number of
pizzas. For literally Rs100 one could order thousands of rupees worth pizza.

I discovered it accidentally and did report to them. Neither did they
acknowledge nor did they send me a free pizza :(

They later fixed this, by not allowing to load the cart in a different tab.
But there is a high chance that there could be another hack even now. Since I
had wowed not to eat junk food anymore, there was not much incentive for me to
spend any more time on it.

~~~
qb45
Surely you can do just point 3 if you know how the API works. Atrocious
design.

There used to be some "secure pendrive" which worked similar way: an app asks
you for password, checks if it matches hash stored on the drive and optionally
commands the drive to unlock itself. What could possibly go wrong? ;)

------
rconti
Excellent writeup.

I have to take issue with the "Starbucks app is great" line, though. I think
I've had more problems with it (on iOS) than any other app. It's the only app
that (for a period of many months if not a year) was regularly unable to find
my location. Even if I opened up Maps or some other location enabled app and
found my location before launching Starbucks, it would just bomb out.

Overall the app seems to have tons of 'issues'. It's been better the past few
months though. And it beats the hell out of standing in a 10 minute line. I
honestly wouldn't stop at Starbucks anymore if it wasn't for the app.

~~~
vinhboy
Agreed. I'd also add that the UI for the app needs work. Ordering coffee for a
group of people becomes a huge hassle on my phone because their search is not
precise, and customizing drinks is a bit cumbersome.

I spent like 20 minutes trying to order 5 drinks the other day. It was also
annoying because when I left the app, all my selected items disappeared.

~~~
CodeWriter23
I'd call that an app fail, taking more time to use the app than writing a list
then walking there and back with your complete order.

~~~
sluggg
I would not. Ordering a large group of people drinks from Starbucks is not in
any way the primary use case for the application. For it's primary use cases
(paying at the store, rewards redemption/collection, ordering yourself drinks
ahead) it works perfectly.

Additionally, if you think about the feature set that would be required to
facilitate bulk orders then you will quickly find that it would be quite large
feature set and require it's own flow. The app does has new-ish features that
allow you to re-order prior orders and save drinks, which would help for these
bulk orders, so they are making progress. If you are consistently ordering
drinks for a large group of your co-workers then you can save their drink
preferences and boom.

~~~
CodeWriter23
Of course it's not the primary use case and the app guarantees it never will
be. I can say from my experience that sending a gopher to get everyone coffee
was a twice daily occurrence at our family business in NYC. Shave 5 minutes
off that time and we would go to Starbucks instead of the deli.

------
zitterbewegung
If an open API existed yes there would be more integrations. Of course you
would have to hire engineers to perform upkeep. Eventually if the ordering API
isn't profitable you get a bunch of sunk costs and have to reassign people.
Its not just "make this open" and POOF. Also your access could be revoked by
unofficially using the API and or they could just change it at any time.

~~~
shortstuffsushi
Why would their API need to be anything more than what they offer to their
app? Why not something like "hey, you can use our stuff, but you've only got
access to the same set of APIs we use in our app, and we may change it as we
please.

Oh btw, here are the auto-gen'd docs, hope those are enough"

That seems like it'd be both developer friendly and no more maintenance than
they're already putting into it. The alternative is basically things like
this, where someone figures it out anyway, and we have to wait and see if they
play it cool, or take it badly and make the author take it down.

~~~
BHSPitMonkey
You can't make breaking changes to a public API, for starters. Needing to
maintain/version APIs you want to deprecate has a nonzero cost.

~~~
TeMPOraL
Of course you can.

All you have to do is to say: "this is our internal API, we're releasing
endpoint documentation for curious developers, but be warned, that the API
may, and will, change without any notice".

I.e. when you're releasing something in the open, you don't _have_ to take
social responsibilities if you don't want to. It's not a binary choice between
"release a fully supported product" and "don't release at all".

~~~
noway421
That makes me wonder, are the any commercial/consumer products that use that
model?

------
heywire
I've always wondered the legality of doing things like this. Have there been
cases where someone was taken to court (in the US) for reversing an API from a
mobile app or website? Assuming no malicious action or intent of course. Could
the CFAA be used in this case, even if the intent was just to understand the
API for personal use?

With so many IoT devices out there relying on 3rd party web services that may
or may not be around a year from now, I expect that the right to inspect and
understand these APIs will become more and more important. Not to mention
wanting the ability to build interactions between devices where the
manufacturer may not have interest (IFTTT, etc).

------
spike021
Maybe a bit off-topic, but APIs always make me wonder a bit when they can be
reverse-engineered or.. for lack of a better word, misused.

I know of one website (site A) that sells items for sports and uses an API of
a sports website (site B) to provide current statistics and other information.

Thing is, that sports website's API is now deprecated for public use, there's
no way to request a token, and from what I can tell it's only to be used for
commercial purposes/paid for by companies.

But, I can easily find the API token being used on site A, dig through the
private/deprecated docs for the API of site B, and use any of their endpoints
and data for pretty much whatever I want.

At least, this was the case roughly 4-6 months ago and I haven't looked into
it since; perhaps they've changed it since then.

But I wonder how this works. Wouldn't it be a misuse of their API and
something they wouldn't want allowed? Usually sports statistics APIs are
fairly expensive, and the fact that some random person like me can get access
easily for free seems unfair to site B, especially when they don't want normal
people using their API anymore.

~~~
semi-extrinsic
I was looking at statistics for prices of sold houses in a European country a
few years back, and started digging around in the javascript of a local
newspaper that every month listed the local sold properties. Turned out in the
API they were using from the central statistics provider, they just had
"days=30" and "municipalities=[list]". They weren't even caching it, every
user visiting the page was another (identical) request to the API provider.
And sure enough, sending a request with "days=3650","municipalities=[{all of
them}] gave me 10 years of data with every property sale in the country.

~~~
zilian
I am really curious which country you're referring to.

~~~
semi-extrinsic
Norway.

------
pilif
I wonder why they went such great lengths to prevent unauthorized clients
(which also is a thing that’s fundamentally impossible. All you can do is
making it harder for attackers). What would be so bad about third party apps
ordering coffee?

Generally, it’s a good idea to be protective, but between cert pinning and
that encrypted device fingerprint and the time based signature, this adds a
lot of points of possible failure to an app you want to be as available as
possible to as many people as possible.

What information this API has access to is so precious to warrant all of this?

~~~
evan_
I'm thinking they probably don't want unauthorized clients just to lower the
support threshold. Some percentage of people _will_ hold them responsible for
broken third-party clients.

------
bpicolo
Opening up an API like this is ripe for abuse, so taking care makes sense. Bad
actors translate directly to lost money.

A real method of securing APIs would be a godsend, but in current tech it's
just not possible. This is the one place where mediocre security-by-obscurity
is your only choice =(

~~~
KekDemaga
"2,147,483,647 large coffees? I'd better get to work." \- some poor Starbucks
employee

~~~
Jonovono
lol. Reminds me of high school. Constructing a green roof garden. We have the
genius (see stupid) idea to go order 20 large waters from McDonalds. They just
tell us to drive forward and park. Sure enough, not long later, they bring us
20 large waters!

~~~
smt88
An interaction that cost McDonald's under $2 and is something you won't forget
and will actually tell others about. Sounds like they got their money's worth.

------
coworkerblues
I want to write a similar write-up for a company which basically does
everything over HTTP with their own half-baked hardcoded AES key in app for
sending credit card info. and that their confirmation checkup is stupid (for
SMS) and can be bypassed.

The problem is that their site TOS forbids reverse engineering, and I am
afraid their lawyers will go after me instead of fixing the security issues
(even if I just contact them), any tips for me ?

~~~
Rjevski
Disclose everything over Tor. The kind of scum that sends credit card data
over insecure homebrew crypto deserves no mercy.

~~~
coworkerblues
I would rather contact them anonymously first, also I don't understand exactly
how Tor would help me with this.

~~~
Rjevski
Tor will help you by hiding your identity so they will be in a dead end when
they sue and try to find you.

And yes of course you can also use Tor to contact them anonymously before
disclosing.

~~~
coworkerblues
Besides telling me to use tor (which I know in general what it is), is there a
guide I can use ? (i.e. how to send / recv anonymous email on tor from a non
tor address ?) or something ???

~~~
Rjevski
Use the following to get a live OS with Tor preinstalled:
[https://tails.boum.org](https://tails.boum.org)

Once you're in there you can just use the Tor browser to create an email
account at any email provider (make sure you give them fake details though)
and send your email.

~~~
coworkerblues
Which email provider allow opening an account from a known tor exit node ?

------
leesalminen
If there was a IoT button in my kitchen that could order my usual morning
order, well I'm not sure. I may or not shout out in joy.

~~~
turdnagel
Looks like someone already did: [https://www.ryanpickren.com/starbucks-
button](https://www.ryanpickren.com/starbucks-button)

~~~
pat_space
Awesome read. Just enough info given to attempt yourself, without giving it
away.

~~~
leesalminen
Agreed. I just found my new weekend project (though maybe not for a couple
weeks because the AWS IoT buttons are sold out).

------
chx
Xposed is the reason I am never getting anything but an unlockable Android
phone.

------
supergeek133
The way to solve this is just for companies to give out their API in a public
manner. You'll almost never be able to secure it from scrapers.

We experienced this at Honeywell, when I first started here we were blocking
users that scraped the app instead of giving them public access and teach them
how to use it correctly.

------
nailer
From:
[https://github.com/tendigi/starbucks](https://github.com/tendigi/starbucks)

> Once you've obtained your client_id, client_secret

I'd like to use this module. Question for author: what's the fastest way to
get a CLIENT_ID and CLIENT_SECRET?

~~~
webfreak7
I'm wondering the same thing. Is there any way to do this?

------
shortstuffsushi
This is super interesting, especially the part about trying to reverse
engineer their "security" measure. I did something like this a few months back
for Paylocity, a time logging system that has a terrible web interface. After
trying to talk with their sales people about them potentially offering an API,
I was told "no API, just this mobile app." Turns out the mobile app is just an
Ionic app with all of the resources (all, including tests and even test
environment logins and stuff) baked in. Super easy to grab their API out of
that (literally called /mobileapi/), but then the trouble was figuring out how
they generated their security token header, which was also a little dance of
timestamps and faked device info.

The best part was when I contacted them afterwards and warned them about the
extra pieces of info they had baked in, their response was basically "yeah,
we're aware that there's more in there than there should be, but it's not a
priority." Oh well, they just have all of my personal information.

------
masthead
This was already done before, in 2016

[https://www.ryanpickren.com/starbucks-
button](https://www.ryanpickren.com/starbucks-button)

------
IshKebab
Probably would have been easier to decompile the Android app than the iOS one.
Even if they use proguard the code is much easier to read.

------
King-Aaron
Good article, though one main take-home for me is some killer new Spotify
playlists to run at work :D

------
Lxr
What's the motivation for Starbucks to make it this difficult to reverse?

~~~
sigjuice
Limit the potential of fraud and abuse?

~~~
ryandrake
I'd argue that time spent actually securing it against abuse would yield more
actual value than time spent obfuscating, but this is almost a religious
argument that has been carrying on since before I was born.

------
catshirt
eyyyyy tendigi. hi Jeff! looks like you're doing some cool stuff.

\- your old downstairs guy

~~~
jeffsoto
Yo dude!

------
jorblumesea
Interesting, what's with the hardcoded new relic ID? Writeup didn't mention
it, I assume that's analytics/monitoring related? Does it need to be set?

------
turdnagel
Is there a good guide out there on reverse engineering mobile apps for iOS &
Android?

~~~
seanp2k2
Use [http://www.telerik.com/fiddler](http://www.telerik.com/fiddler) or
[https://www.charlesproxy.com](https://www.charlesproxy.com) or
[https://mitmproxy.org](https://mitmproxy.org)

If they pin certs it can be harder but not impossible.

~~~
_e
Xposed Framework module to disable SSL cert pinning
[https://github.com/Fuzion24/JustTrustMe](https://github.com/Fuzion24/JustTrustMe)

------
amelius
Can somebody explain what the use/fun of this is, if Starbucks can push an
update that invalidates the approach anytime they want?

------
wpovell
Is there a good place to learn how to do this sort of reverse engineering?

------
kyle-rb
HTCPCP implementation when?

------
dabockster
>issue tracking turned off

Well, now how am I supposed to tell him that Tully's is better?

------
githubcomyottu
Starbucks is nice for dessert, I wish they sold decent coffee though.

