| name | apply-api-client-security |
| description | Use when your application consumes external or third-party APIs — configuring HTTP clients, validating TLS certificates, handling secrets, and sanitizing responses from upstream services. |
| source | OWASP API Security Top 10 2023 API10 (Unsafe Consumption of APIs); OWASP Transport Layer Security Cheat Sheet; CWE-295; CWE-918 |
| tags | ["security","owasp","api-client","http-client","certificate-validation","upstream-trust","developer"] |
Apply API Client Security
Configure outbound HTTP clients with strict TLS verification, timeouts, response size limits, and response sanitization — treating data from third-party APIs as untrusted until validated.
Why This Is Best Practice
Adopted by: OWASP API Security Top 10 2023 API10 (Unsafe Consumption of APIs) is a dedicated category for client-side API security failures. AWS SDK, Google Cloud Client Libraries, and Azure SDK all enforce TLS certificate validation by default. NIST SP 800-52 Rev 2 requires certificate validation for all TLS connections. PCI DSS v4.0 Requirement 6.3.3 mandates secure configurations for components that connect to payment APIs.
Impact: Disabling TLS certificate verification (verify=False in Python requests, rejectUnauthorized: false in Node.js) enables man-in-the-middle attacks on all API calls — attackers on the same network can intercept credentials and response data. OWASP documents cases where third-party APIs injected malicious data into responses that was then rendered to users without sanitization, enabling XSS via upstream compromise. Unlimited response sizes allow external APIs to send gigabyte responses that exhaust server memory.
Why best: Trusting upstream APIs completely is the common approach — it's fast to implement but creates a dependency chain attack surface. Treating third-party API responses as potentially hostile (validating schema, sanitizing content, enforcing limits) provides defense-in-depth against upstream compromise.
Sources: OWASP API Security Top 10 2023 API10; CWE-295; CWE-918; NIST SP 800-52 Rev 2
Steps
-
Never disable TLS certificate verification:
import requests
response = requests.get('https://api.example.com', verify=False)
response = requests.get('https://api.example.com')
response = requests.get('https://api.example.com', verify='/path/to/custom-ca.pem')
const https = require('https');
const agent = new https.Agent({ rejectUnauthorized: false });
fetch('https://api.example.com', { agent });
fetch('https://api.example.com');
-
Set connection and read timeouts — never wait indefinitely on external APIs:
import requests
response = requests.get(
'https://api.example.com/data',
timeout=(5, 30)
)
response = requests.get(url, stream=True, timeout=(5, 30))
content = b''
MAX_BYTES = 10 * 1024 * 1024
chunk response.iter_content():
content += chunk
(content) > MAX_BYTES:
response.close()
ValueError()
Rules
verify=False in any language is never acceptable in production — use custom CA bundles if your infrastructure uses internal CAs.
- Always validate HTTP status codes before parsing response bodies — a 200 does not mean the body matches the expected schema.
- Retry logic must include jitter and exponential backoff — thundering herds after upstream recovery cause secondary outages.
- Third-party API responses that contain user-controlled data (e.g., social login display names) must be treated as untrusted user input.
Common Mistakes
- Propagating upstream 500 errors directly to clients — leaks implementation details; translate to generic errors (see
design-error-handling).
- Using
requests.get(url) without timeout in a web server — one slow upstream API call blocks the request worker indefinitely.
- Logging full API responses — may contain PII or secrets from the upstream service.
- Assuming HTTPS means the server is legitimate — HTTPS only proves the domain is valid; the domain itself may be compromised. Validate the response content too.