🔒 OWASP API Security Testing with Python
Overview: This comprehensive guide covers Python-based API testing techniques, including
the creation of Man-in-the-Middle (MITM) proxy tools, API fuzzing, and automated security testing
frameworks.
🐍 Why Python for API Security Testing?
Python has become the go-to language for API security testing due to its extensive libraries, ease of use,
and powerful automation capabilities. With tools like the requests module and integration with ChatGPT for
code generation, you can create sophisticated testing frameworks with minimal effort.
Key Advantages:
- Extensive HTTP library support (requests, urllib, httpx)
- Easy integration with security tools like Burp Suite
- Rapid prototyping and automation capabilities
- AI-assisted code generation with ChatGPT
- Virtual environment support for isolated testing
🛠️ Man-in-the-Middle (MITM) Proxy Tool
A custom MITM proxy tool built with Python can intercept, analyze, and manipulate API requests and responses.
This 270-line tool (generated with ChatGPT assistance) provides comprehensive features for API security
testing.
MITM Proxy Architecture
Client Application
→
Python MITM Proxy
→
Target API Server
Intercept & Analyze
↓
Modify & Fuzz
↓
Log & Report
MITM Proxy Features:
- Session Management: Save and load testing sessions
- Preference Storage: Save custom configurations (form data, PUT requests, proxy
settings)
- Request History: Track all API interactions
- API Fuzzing: Automated payload injection
- Burp Suite Integration: Automatic certificate import
- CSV Export: Export findings to CSV format
- JSON Preference Storage: Persistent configuration
Required Python Modules:
import requests # HTTP library for API communication
import tkinter # GUI components for the proxy interface
import os # Operating system interactions
import csv # CSV file handling for exports
import json # JSON data processing
⚙️ Setting Up Your Python Environment
Creating a Virtual Environment:
Virtual environments allow you to isolate project dependencies and maintain multiple testing configurations.
python -m venv api_testing_env
source api_testing_env/bin/activate # Linux/Mac
api_testing_env\Scripts\activate # Windows
Installing Essential Modules:
pip install requests
pip install urllib3
pip install httpx
🔧 Making API Requests with Python
Basic GET Request:
import requests
response = requests.get('https://api.example.com/endpoint')
print(response.status_code)
print(response.json())
POST Request with JSON Data:
import requests
url = 'https://api.example.com/endpoint'
headers = {'Content-Type': 'application/json'}
data = {'key': 'value', 'parameter': 'test'}
response = requests.post(url, headers=headers, json=data)
print(response.json())
Advanced Request with All Parameters:
import requests
method = 'POST'
url = 'https://api.example.com/endpoint'
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_TOKEN_HERE'
}
json_data = {'username': 'testuser', 'password': 'testpass'}
proxies = {'http': 'http://localhost:8080', 'https': 'http://localhost:8080'}
response = requests.request(method, url, headers=headers, json=json_data, proxies=proxies)
print(response.text)
📊 HTTP Methods and Request Types
| HTTP Method |
Purpose |
Python Implementation |
| GET |
Retrieve data |
requests.get(url) |
| POST |
Submit data |
requests.post(url, data=data) |
| PUT |
Update resource |
requests.put(url, data=data) |
| DELETE |
Remove resource |
requests.delete(url) |
| PATCH |
Partial update |
requests.patch(url, data=data) |
🎯 API Fuzzing Techniques
API fuzzing involves sending multiple attack vectors to test for vulnerabilities. The MITM proxy tool allows
you to define fuzzing parameters and automate the testing process.
Fuzzing Process:
Define Attack Vectors
→
Configure Target Endpoint
→
Execute Fuzzing
→
Analyze Responses
Example Fuzzing Setup:
# Target URL with fuzzing placeholder
url = 'https://api.example.com/search?query=FUZZ'
# Attack vectors
attack_vectors = [
"' OR '1'='1",
"<script>alert('XSS')</script>",
"../../../etc/passwd",
"{{7*7}}",
"${7*7}"
]
# Replace FUZZ with each attack vector
for vector in attack_vectors:
test_url = url.replace('FUZZ', vector)
response = requests.get(test_url)
print(f"Vector: {vector} | Status: {response.status_code}")
⚠️ Critical Warning: Rate Limiting
Be extremely careful with automated fuzzing! If an API has a rate limit (e.g., 5
requests per second) and your attack vector list contains more than 5 entries, you will exceed the
limit. This can result in:
- IP blocking or account suspension
- Legal consequences for violating terms of service
- Incomplete or inaccurate test results
Always verify and respect API rate limits before conducting fuzzing tests.
🔐 Authentication and Headers
Setting Custom Headers:
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
'User-Agent': 'Python-Security-Testing/1.0',
'X-API-Key': 'your-api-key-here'
}
response = requests.get(url, headers=headers)
Bearer Token Authentication:
auth_token = 'your_bearer_token_here'
headers = {'Authorization': f'Bearer {auth_token}'}
response = requests.get(url, headers=headers)
📦 Request Body Types
Form Data (application/x-www-form-urlencoded):
data = {
'username': 'testuser',
'password': 'testpass',
'remember': 'true'
}
response = requests.post(url, data=data)
JSON Data (application/json):
json_data = {
'username': 'testuser',
'password': 'testpass',
'profile': {
'email': '
[email protected]',
'role': 'admin'
}
}
response = requests.post(url, json=json_data)
File Upload (multipart/form-data):
files = {'file': open('test.txt', 'rb')}
data = {'description': 'Test file upload'}
response = requests.post(url, files=files, data=data)
files['file'].close()
🔍 Response Handling and Error Management
Checking Response Codes:
response = requests.get(url)
if response.status_code == 200:
print("Success!")
data = response.json()
elif response.status_code == 401:
print("Unauthorized: Invalid credentials")
elif response.status_code == 404:
print("Not Found: Endpoint doesn't exist")
elif response.status_code == 500:
print("Server Error: API malfunction")
Comprehensive Error Handling:
try:
response = requests.get(url, timeout=5)
response.raise_for_status() # Raises HTTPError for bad status codes
data = response.json()
except requests.exceptions.Timeout:
print("Request timed out")
except requests.exceptions.ConnectionError:
print("Connection error occurred")
except requests.exceptions.HTTPError as e:
print(f"HTTP error occurred: {e}")
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
Invalid URL Error Handling:
Python's requests module provides helpful error messages and suggestions:
# Example: Invalid URL without schema
url = "api.example.com/endpoint"
try:
response = requests.get(url)
except requests.exceptions.InvalidURL:
print("Error: Invalid URL, no schema supplied")
print("Perhaps you meant: http://api.example.com/endpoint")
🔄 Session Management and Persistence
Creating a Persistent Session:
session = requests.Session()
# Set default headers for all requests in this session
session.headers.update({'User-Agent': 'Python-Testing/1.0'})
# First request - establishes cookies
response1 = session.get('https://api.example.com/login')
# Subsequent requests automatically include cookies
response2 = session.get('https://api.example.com/profile')
response3 = session.post('https://api.example.com/update', json={'field': 'value'})
Cookie Management:
from http.cookiejar import CookieJar
session = requests.Session()
session.cookies = CookieJar()
# Access cookies
for cookie in session.cookies:
print(f"Cookie: {cookie.name} = {cookie.value}")
💾 Data Persistence and Export
Saving Request History to JSON:
import json
request_history = []
# Capture requests
for i in range(5):
response = requests.get(f'https://api.example.com/item/{i}')
request_history.append({
'url': response.url,
'status_code': response.status_code,
'response_time': response.elapsed.total_seconds()
})
# Save to file
with open('request_history.json', 'w') as f:
json.dump(request_history, f, indent=4)
Exporting Results to CSV:
import csv
# Prepare data
results = [
{'endpoint': '/api/users', 'status': 200, 'vulnerability': 'None'},
{'endpoint': '/api/admin', 'status': 403, 'vulnerability': 'Access Control'},
]
# Write to CSV
with open('api_test_results.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['endpoint', 'status', 'vulnerability'])
writer.writeheader()
writer.writerows(results)
🚀 Advanced Techniques
Streaming Large Files:
with requests.get(url, stream=True) as response:
response.raise_for_status()
with open('large_file.bin', 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
Downloading and Saving Files:
file_url = 'https://api.example.com/download/report.pdf'
response = requests.get(file_url)
if response.status_code == 200:
with open('downloaded_report.pdf', 'wb') as f:
f.write(response.content)
print("File downloaded successfully")
Custom Transport Adapters:
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(total=3, backoff_factor=0.3)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
🏗️ Building Your Own Testing Framework
Framework Development Strategy:
Start Simple
→
Make Basic Requests
→
Add Storage
→
Expand Features
→
Automate Testing
Start with simple GET and POST requests. As your needs grow, add request storage, then implement automated
testing logic. Over time, your framework will evolve into a comprehensive testing suite tailored to your
specific requirements.
✅ Best Practices and Security Guidelines
Essential Best Practices:
- Never Hardcode Credentials: Use environment variables or secure vaults for API keys
and tokens
- Respect Rate Limits: Always check and comply with API rate limiting policies
- Implement Error Handling: Build robust error handling for long-running scripts
- Use Virtual Environments: Isolate dependencies for each project
- Add Persistence: Save session data and request history for later analysis
- Test Responsibly: Only test APIs you have permission to test
- Document Your Code: Add comments explaining complex logic
- Version Control: Use Git to track changes in your testing scripts
⚠️ Critical Reminder
The MITM proxy tool described in this guide is extremely powerful. The rate limiting bypass capability
can cause serious issues:
- Only use on systems you own or have explicit permission to test
- Always implement rate limiting in your own scripts
- Be aware of legal implications of unauthorized testing
- Document all testing activities for compliance purposes
📚 Python Libraries Reference
| Library |
Purpose |
Use Case |
| requests |
HTTP client library |
Most common API interactions, recommended for general use |
| urllib/urllib3 |
Core HTTP library |
Low-level HTTP operations, used when requests doesn't suffice |
| httpx |
Modern async HTTP client |
Async/await operations, HTTP/2 support |
| zeep |
SOAP client |
Testing SOAP-based APIs |
| aiohttp |
Async HTTP framework |
High-performance concurrent requests |
🎓 Learning Path Recommendations
Beginner Level:
- Learn basic Python syntax and data structures
- Master the requests library fundamentals
- Practice making GET and POST requests
- Understand HTTP status codes and headers
Intermediate Level:
- Implement session management and cookie handling
- Build basic authentication workflows
- Create request/response logging systems
- Develop simple API testing scripts
Advanced Level:
- Design MITM proxy tools
- Implement automated vulnerability scanners
- Build comprehensive testing frameworks
- Create custom reporting and analysis tools
- Integrate with CI/CD pipelines
🤖 Using AI for Code Generation
Modern AI tools like ChatGPT can significantly accelerate development of API testing tools. The 270-line MITM
proxy tool referenced in this guide was generated with AI assistance, demonstrating the power of combining
human expertise with AI capabilities.
Tips for AI-Assisted Development:
- Clearly describe your requirements and use cases
- Request specific features (session management, error handling, etc.)
- Ask for code comments and documentation
- Iterate and refine the generated code
- Always review and test AI-generated code thoroughly
- Combine multiple AI-generated components for complex tools
🔗 Integration with Security Tools
Burp Suite Integration:
Python scripts can integrate with Burp Suite by using its proxy and importing certificates:
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080'
}
# Disable SSL verification warnings
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
response = requests.get(url, proxies=proxies, verify=False)
Certificate Handling:
# Use Burp Suite certificate
cert_path = '/path/to/burp-certificate.pem'
response = requests.get(url, verify=cert_path)
# Or use custom certificate
response = requests.get(url, cert=('/path/to/client.crt', '/path/to/client.key'))
📈 Monitoring and Performance
Measuring Response Times:
import time
start_time = time.time()
response = requests.get(url)
end_time = time.time()
response_time = end_time - start_time
print(f"Response time: {response_time:.2f} seconds")
# Or use built-in elapsed time
print(f"Elapsed time: {response.elapsed.total_seconds():.2f} seconds")
🎯 Practical Examples
Complete API Security Testing Script:
import requests
import json
from datetime import datetime
class APISecurityTester:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
self.results = []
def test_endpoint(self, endpoint, method='GET', data=None):
url = f"{self.base_url}{endpoint}"
try:
response = self.session.request(method, url, json=data)
result = {
'timestamp': datetime.now().isoformat(),
'endpoint': endpoint,
'method': method,
'status_code': response.status_code,
'response_time': response.elapsed.total_seconds()
}
self.results.append(result)
return response
except Exception as e:
print(f"Error testing {endpoint}: {e}")
return None
def save_results(self, filename='test_results.json'):
with open(filename, 'w') as f:
json.dump(self.results, f, indent=4)
# Usage
tester = APISecurityTester('https://api.example.com')
tester.test_endpoint('/api/users')
tester.test_endpoint('/api/login', method='POST', data={'username': 'test'})
tester.save_results()