Penetration Testing for APIs — Complete Security Testing Guide
In this tutorial, you will learn about Penetration Testing for APIs. We cover key concepts, practical examples, and best practices to help you master this topic.
Penetration testing (pentesting) simulates real-world attacks against your API to identify security vulnerabilities before malicious actors exploit them. It goes beyond automated scanning with manual exploitation attempts.
What You'll Learn
You'll learn the API pentesting methodology, common testing tools, and how to conduct security assessments on your own APIs.
Why It Matters
Automated scanners miss complex logic flaws like broken authorization, business logic abuse, and race conditions. Manual pentesting catches what scanners cannot, providing the most thorough security evaluation.
Real-World Use
A pentest on a payment API discovered that changing the HTTP method from POST to PUT on a refund request bypassed authorization checks and allowed any authenticated user to refund any transaction.
flowchart TD
A[Reconnaissance] --> B[Endpoint Discovery]
B --> C[Authentication Testing]
C --> D[Authorization Testing]
D --> E[Input Validation]
E --> F[Rate Limit Testing]
F --> G[Business Logic]
G --> H[Data Exposure]
H --> I[Reporting]
I --> J[Remediation]
J --> K[Retesting]
Teacher's Mindset
Penetration testing is like hiring a professional burglar to test your home security. They will find weaknesses you never considered, like the unlocked window above the garage that your alarm system does not cover.
API Penetration Testing
# Simple API security scanner
import requests
import json
class APIScanner:
def __init__(self, base_url):
self.base_url = base_url
self.findings = []
def test_auth_bypass(self, endpoint):
urls = [
f"{self.base_url}{endpoint}",
f"{self.base_url}{endpoint}/",
f"{self.base_url}/{endpoint}/../admin"
]
for url in urls:
response = requests.get(url)
if response.status_code == 200:
self.findings.append({
"type": "Auth Bypass",
"url": url,
"status": response.status_code
})
def test_sql_injection(self, endpoint, params):
payloads = ["' OR '1'='1", "1; DROP TABLE users", "\" OR 1=1--"]
for payload in payloads:
test_params = {k: payload for k in params}
response = requests.get(
f"{self.base_url}{endpoint}",
params=test_params
)
if "error" not in response.text.lower():
self.findings.append({
"type": "Possible SQLi",
"payload": payload,
"response": response.text[:200]
})
def report(self):
print(f"Found {len(self.findings)} issues:")
for finding in self.findings:
print(f" {finding['type']}: {finding.get('url', finding.get('payload'))}")
# JWT attack testing
def test_jwt_weaknesses(token):
import jwt
attacks = {
"none_algorithm": jwt.encode({"sub": "admin"}, "", algorithm="none"),
"empty_secret": jwt.encode({"sub": "admin"}, "", algorithm="HS256"),
"algorithm_confusion": jwt.encode({"sub": "admin"}, None, algorithm="HS256")
}
for attack_name, forged_token in attacks.items():
response = requests.get(
"https://api.example.com/admin",
headers={"Authorization": f"Bearer {forged_token}"}
)
if response.status_code == 200:
print(f"Vulnerable: {attack_name}")
# Rate limit and brute force testing
def test_rate_limits(endpoint, requests_count=100):
import time
start = time.time()
success = 0
blocked = 0
for i in range(requests_count):
response = requests.get(endpoint)
if response.status_code == 429:
blocked += 1
else:
success += 1
elapsed = time.time() - start
print(f"Requests: {requests_count} in {elapsed:.1f}s")
print(f"Successful: {success}, Blocked: {blocked}")
if blocked == 0:
print("WARNING: No rate limiting detected!")
Common Mistakes
| Mistake | Why It's Wrong | Fix |
|---|---|---|
| Testing only known endpoints | Shadow APIs and hidden endpoints missed | Use endpoint discovery (swagger, common paths) |
| Automated scanning only | Logic flaws require human analysis | Combine automated and manual testing |
| Testing in production without warning | Can cause downtime or data corruption | Test in staging, or coordinate production tests |
| Skipping authentication tests | Weak auth is primary attack vector | Test: bypass, brute force, token manipulation |
| Not testing rate limiting | API abuse goes undetected | Verify rate limits block excessive requests |
Practice Questions
- What is the difference between SAST, DAST, and pentesting?
- What is a business logic vulnerability?
- Why should you test for IDOR (Insecure Direct Object Reference)?
- What tools are commonly used for API pentesting?
- How do you safely test an API without causing damage?
Challenge
Create a deliberately vulnerable API (using Flask). Write Python scripts to test for: auth bypass, SQL Injection, IDOR, rate limiting bypass, and JWT weaknesses. Document each finding.
FAQ
Mini Project
Set up a deliberately vulnerable API using the crAPI or DVWS-FastAPI intentionally vulnerable application. Perform a full pentest: recon, auth bypass, injection, IDOR, and rate limiting tests. Write a report.
What's Next
Learn about API Gateway security to centralize security controls.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro