Attackers want your data and they're getting it from your API
Tim Bond
PHP UK Conference - 16 February, 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
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",
"ean": 0498044355635
},
// more items
]
Look at the payload: PUT /scan/nnn
{
"user_id": 1234,
"latitude": 51.5205,
"longitude": 0.091
}
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 2 days ago
- 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 UK]
By Tim Bond
Attackers want your data and they're getting it from your API [PHP UK]
PHP UK Conference - 16 February, 2023
- 93