API Security Testing
APIs are the most targeted attack surface in modern applications because they're directly accessible, often less protected than the UI layer, and bypass many client-side controls. API security testing is a critical SDET skill — every REST endpoint must be tested for proper authentication, authorization, input validation, and rate limiting.
Comprehensive API Security Test Suite
import requests
import pytest
BASE = "https://staging.myapp.com"
class TestAPISecurityComprehensive:
"""
Security test suite for POST /api/orders endpoint
(can be adapted for any API endpoint)
"""
@pytest.fixture(scope="class")
def auth_headers(self):
r = requests.post(f"{BASE}/api/auth/login",
json={"email": "alice@test.com", "password": "Test@1234"})
return {"Authorization": f"Bearer {r.json()['token']}"}
# ── AUTHENTICATION TESTS ──────────────────────────────────
def test_endpoint_requires_authentication(self):
"""No auth header → 401"""
r = requests.post(f"{BASE}/api/orders", json={"items": []})
assert r.status_code == 401
def test_expired_token_is_rejected(self):
"""Expired JWT → 401"""
expired_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjEsImV4cCI6MTYwMDAwMDAwMH0.invalid"
r = requests.post(f"{BASE}/api/orders",
headers={"Authorization": f"Bearer {expired_token}"},
json={"items": []})
assert r.status_code == 401
# ── INPUT VALIDATION ──────────────────────────────────────
def test_sql_injection_in_order_product_id(self, auth_headers):
"""SQL injection payload in productId → 400"""
r = requests.post(f"{BASE}/api/orders",
headers=auth_headers,
json={"items": [{"productId": "1 OR 1=1", "quantity": 1}]})
assert r.status_code == 400
assert "sql" not in r.text.lower() # No SQL error details
def test_negative_quantity_rejected(self, auth_headers):
"""Negative quantity should be rejected"""
r = requests.post(f"{BASE}/api/orders",
headers=auth_headers,
json={"items": [{"productId": "P001", "quantity": -5}]})
assert r.status_code == 400
def test_extremely_large_quantity_rejected(self, auth_headers):
"""Overflow-attempt quantity rejected"""
r = requests.post(f"{BASE}/api/orders",
headers=auth_headers,
json={"items": [{"productId": "P001", "quantity": 9999999999}]})
assert r.status_code in [400, 422]
# ── RATE LIMITING ─────────────────────────────────────────
def test_rate_limiting_enforced(self, auth_headers):
"""API should rate limit after N requests"""
responses = []
for _ in range(100):
r = requests.post(f"{BASE}/api/orders",
headers=auth_headers,
json={"items": [{"productId": "P001", "quantity": 1}]})
responses.append(r.status_code)
if r.status_code == 429:
break # Rate limit hit — test passes
assert 429 in responses, "Rate limiting not enforced after 100 rapid requests"
# ── SENSITIVE DATA EXPOSURE ───────────────────────────────
def test_successful_order_response_redacts_payment_data(self, auth_headers):
"""Order creation response must not expose card details"""
r = requests.post(f"{BASE}/api/orders",
headers=auth_headers,
json={"items": [{"productId": "P001", "quantity": 1}],
"payment": {"cardToken": "tok_test"}})
if r.status_code == 201:
body = r.json()
assert "cardNumber" not in str(body)
assert "cvv" not in str(body).lower()
assert "cardToken" not in str(body) # Token should not echo backCommon Mistakes
- Missing rate limit testing — unthrottled APIs can be used for brute force attacks, data harvesting, and DoS; always test that rate limiting exists
- Not testing mass assignment — POST /api/users with extra field 'role': 'admin' might be accepted if the API doesn't whitelist allowed fields
- Checking only the happy path response for data exposure — test error responses too; stack traces and detailed DB errors are frequently exposed in error responses
- No security tests for file upload endpoints — MIME type validation, filename sanitization, and file size limits must be explicitly tested
Tip
Tip
Practice API Security Testing in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
OAuth 2.0 + rate limiting.
Practice Task
Note
Practice Task — (1) Write a working example of API Security Testing from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with API Security Testing is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- APIs are the most targeted attack surface in modern applications because they're directly accessible, often less protected than the UI layer, and bypass many client-side controls.
- Missing rate limit testing — unthrottled APIs can be used for brute force attacks, data harvesting, and DoS; always test that rate limiting exists
- Not testing mass assignment — POST /api/users with extra field 'role': 'admin' might be accepted if the API doesn't whitelist allowed fields
- Checking only the happy path response for data exposure — test error responses too; stack traces and detailed DB errors are frequently exposed in error responses