Attackers want your data and they're getting it from your API
Tim Bond
php[tek] - May 17, 2023
Who am I?
- PHP developer
- API developer
- API connoisseur
- Frontend developer
(when I have to be) - Cyclocross racer
Who am I NOT?
- Lawyer
- Security expert
- Penetration tester
- Knower of everything
- Cat 1 cyclocross racer
What is this talk?
- Things I've seen
- Things I can talk about
- Written from a mobile app perspective
- Still applicable to web APIs
- Also applicable to M2M APIs
What is this talk NOT?
- A hacking workshop
- The answer to every question
- The only thing you should consider
- Targeting SaaS APIs (e.g. Stripe, Twilio)
- Going to have any more cyclocross references
What is considered an API?
- For the web app
- For the mobile app
- AJAX endpoints
- Partner integrations
- Internal integrations
- Webpages*
Why attack an API?
- Curiosity/data mining
- Espionage or "competitor analysis"
- Grudge
- No other way to get data
- Want to build their own frontend/bot
- API key application was rejected
- Automation
'Automating' comes from the roots 'auto-' meaning 'self-', and 'mating', meaning 'screwing'.
Image credit: XKCD "Automation", licensed Creative Commons Attribution-NonCommercial 2.5
TL;DR:
- Limit your API responses
- Only the data you need to make the app work
- Only who needs to see it
- Ongoing monitoring - automated and human-assisted
Code bugs > everything else
- Google+ breach in November 2018
- 52.5 million users
- Name
- Email address
- Occupation
- Gender
- Age
An example app
Obscurity != Security
Let's hack it!
- Set up a packet sniffer
- Counter measure: use HTTPS
- Set up a Man-In-The-Middle attack
- Counter measure: use certificate pinning
- Bypass certificate pinning
- Counter measure: dynamic integrity check
- Maybe: check for root
iOS Call Recorder app
[security researcher Anand] Prakash could view and modify the network traffic going in and out of the app. That meant he could replace his phone number registered with the app with the phone number of another app user, and access their recordings on his phone."
Let's hack it!
GET http://api.example.com/items
POST http://api.example.com/analytics
PUT http://api.example.com/scan/498044355635
POST http://api.example.com/analytics
Look at the payload: GET /items
[
{
"title": "Bleach",
"upc": 0498044355635
},
// more items
]
Look at the payload: PUT /scan/nnn
{
"user_id": 1234,
"latitude": 41.9972705,
"longitude": -87.8834467
}
How can we hack it?
- API has no authentication 🥳
- There are some analytics, but we can probably ignore that
- Upon opening the app, we get a list of all the products
- Just iterate over that list and send 10 PUT requests
- Put it in a cron job and you'll have a coupon every time you shop
- Set it up for your friends (just get their ID)
Let's keep exploring the API
- Try some other routes
- It looks RESTful, so how about
/user/123
- Or
/users
- It looks RESTful, so how about
- Any docs? Try URLs like
/docs
or/swagger.json
- Let's try a route we know doesn't exist, and see if we get a stack trace
Rate limiters
/user/123
/user/124
A case for UUIDs
- /users/123
- /users/124
- /users/FA091B58-B0E8-46E9-8DEE-592EBA4D62C4
- ??
Get User Example
// GET /api/users/{id}
public function getUser(string $id) {
return $this->database->getUser($id);
}
View Models to the Rescue
public function getUser(string $id) {
$info = $this->database->getUser($id);
if($this->currentUser->isAdmin()) {
return new AdminUserResponse($info);
} elseif($this->currentUser->id == $info->id) {
return new UserResponse($info);
} else {
return new OtherUserResponse($info);
//or throw new HttpNotAuthorizedException();
}
}
Write tests!
- Test every endpoint:
- Authenticated
- Unauthenticated
- Every role
- Every query string param
Web Application Firewall
- Protects against common exploits
- XSS, SQL injection, etc
- Designed to detect and stop scraping attacks
Maybe it's time for some auth
- Auth Basic
- Auth Digest
Maybe it's time for some auth
- API keys?
- Partner/User API keys
- Application API keys
- NEVER use static API keys
- Compromised as soon as they're used
- Generally easy to find in the source code
- Hard to rotate
Want a key? Check GitHub!
Token Authentication
- Assign a unique string to each user
- Better: assign to each user to each session
- Has a defined TTL
- Server can revoke or extend at any time
- In a way it's a per-user API key
- Now we can identify who makes each request
How it Started...
...How it's Going
Central Auth Server
Auth
Server
Search API
Map
API
JWT
JWT
- Know exactly who is making the request
- Can assign to known users and unknown
- Time limited
- Can't revoke one
- But you can revoke them all
JWT
- What to do when it expires?
- Force the user to log back in
- Use refresh tokens
Don't give access to everything
👈 No access to Purchase API
With valid auth,
users can still make
any request
Let 'em sniff, but kill repeatability
- HMAC
- Hash-based Message Authentication Code
- Gather the payload and sign the request
Signed
GET /users/123
API_KEY = NWTPk4
APP_ID = wQrDfM
Unsigned
GET /users/123
API_KEY = NWTPk4
APP_ID = wQrDfM
HMAC = APvNwF
🔒
Bonus: the API can returned signed URLs
Getting the (client side) signing key
- Look around the app source for a key
- Set a breakpoint just before the HTTP request
- See if the app logs to a console
- Browser console on web
- deviceconsole on iOS
- logcat on Android
Make the signing key dynamic
- Much more difficult to find in code
- Must be deterministic so server can duplicate
Example: concatenate:
- The string length of some class name
- The app version number
- The UTC date + hour
Reverse engineering the runtime
- Connect the app to a debugger
- Decompile the Android app
- Works on iOS too--ask the jailbreakers
Proxify the APIs
- App only uses one secret
- Proxy verifies that secret
- Proxy reaches out to the actual APIs on the user's behalf
- Much easier to rotate API auth for 3rd party systems
- But we're still dealing with a secret 😕
Secrets as a Service
API
Shared secret
Unauthenticated request
Secret token
API call ✅
Dynamic
Integrity
Check
Using OAuth 2
API 1
API
Gateway
Authorization request
Auth token
App Auth request
App token
OAuth 2 service
App auth service
API calls
Registered
app info
Registered
user info
API 2
API 3
Key 3
Key 1
Key 2
OAuth 2 flow
- Authorize both who (user) and what (app)
- Only time-limited, runtime tokens
- Don't think of OAuth2 as user auth—it's an access token generator
- Rotate secrets without touching devices
- API gateway does rate limiting and authentication
What about endpoints that can't have authentication?
- Can still authenticate the device
- Rate limiting
What about endpoints that can't have authentication?
- Can still authenticate the device
- Rate limiting
- Monitoring
- Heuristics
- IP
- User agent
- Time between requests
- Geo data
- Your API gateway might do this for you!
No API? No problem!
No API? No problem!
<?php
$crawler = $client->get('https://www.example.com/log-in');
$crawler->filter('#email')->sendKeys('tim.bond');
$crawler->filter('#password')->sendKeys('test123');
$crawler->filter('#log-in')->click();
$client->waitForStaleness('#log-in');
$client->waitFor('#price')->filter('#price')->getText();
But we have a CAPTCHA!
- Dozens of services will solve those
- Images recognized with OCR/AI and/or Humans
Conclusion
- Dumping an API via HTTP > DB via SSH
- If they want it bad enough they will try hard enough
- Reveal only what the current user needs
- Don't use static authentication
- Debug protection and code obfuscation thwart semi-determined attackers
- Authenticate the app (environment) as well as the user
Further Reading
- OWASP API Security Top 10 2019
- 2023 edition being written as of February
- Hacking APIs: Breaking Web Application Programming Interfaces by Corey Ball
- Web Security Academy: Free Online Training from PortSwigger
- OWASP Mobile Application Security Verification Standard (MASVS)
Questions
Attackers want your data and they're getting it from your API [php[tek]]
By Tim Bond
Attackers want your data and they're getting it from your API [php[tek]]
php[tek] - May 17, 2023
- 83