Back to blog
Security & Complianceadvanced

Penetration Testing Basics โ€” How to Test Your Own API's Security

Learn the difference between vulnerability scanning and penetration testing, then use OWASP ZAP and Burp Suite to test authentication, authorization, injection, and rate limiting in your own APIs.

LearnixoApril 15, 202610 min read
SecurityPenetration TestingOWASP ZAPBurp SuiteIDORSQL InjectionAPI Security
Share:๐•

Vulnerability Scanning vs. Penetration Testing

These terms are often used interchangeably, but they describe different activities with different depth and purpose.

Vulnerability scanning is automated. A tool crawls your application, checks known patterns (CVE signatures, misconfiguration patterns, outdated software versions), and reports findings. It is fast, cheap, and produces a lot of output โ€” including false positives. It finds the low-hanging fruit: missing security headers, known CVE in a library, exposed admin panel.

Penetration testing is manual (or semi-automated with human-guided tooling). A tester actively tries to exploit vulnerabilities to demonstrate real-world impact. It requires understanding the application's logic, not just its surface. A scanner cannot find IDOR (it doesn't know that orderId=456 belongs to a different user). A human tester can.

The workflow for testing your own API:

  1. Automated scan first โ€” find and fix obvious issues
  2. Manual testing โ€” probe business logic, authorization, authentication
  3. Document findings โ€” reproducible steps, severity, remediation
  4. Retest after fixes โ€” verify the vulnerability is actually closed

Legal Requirements First

This cannot be overstated: only test systems you own or have explicit written permission to test.

Testing a system without permission โ€” even if you discover a real vulnerability โ€” is illegal in most jurisdictions. This includes:

  • Systems run by third parties your company uses
  • Production systems of your own company unless you have written authorization from appropriate management
  • Other users' accounts on your own system (you own the platform, not their data)

For your own development/staging API: you have blanket permission. For production: get written authorization before running any active scanning tools.

Bug bounty programs provide explicit scope and legal safe harbor for testing. If a company runs one (HackerOne, Bugcrowd), their scope document defines what you are and aren't authorized to test.

Setting Up OWASP ZAP

OWASP ZAP (Zed Attack Proxy) is a free, open-source security scanning and manual testing tool. It sits between your browser/client and your API as a proxy, recording traffic and scanning it.

Installation

Download from zaproxy.org. Available for Windows, macOS, Linux.

Automated API Scan with ZAP

ZAP can scan an API directly using its OpenAPI/Swagger spec:

Bash
# Docker-based ZAP scan against a local API with OpenAPI spec
docker run --rm \
  -v $(pwd)/zap-reports:/zap/wrk/:rw \
  ghcr.io/zaproxy/zaproxy:stable \
  zap-api-scan.py \
  -t http://host.docker.internal:5000/swagger/v1/swagger.json \
  -f openapi \
  -r zap-report.html \
  -x zap-report.xml

ZAP crawls all documented endpoints, sends fuzzing payloads, and reports findings. Common automated findings:

  • Missing security headers
  • Information disclosure in error responses
  • Cookie flags (SameSite, Secure, HttpOnly)
  • SQL injection patterns
  • CORS misconfigurations

Using ZAP as a Proxy for Manual Testing

Configure your HTTP client to use ZAP as proxy (localhost:8080). All requests flow through ZAP, which records them in the Sites tree. You can then:

  • Re-send and modify requests in the Request editor
  • Spider the API from observed traffic
  • Active scan specific endpoints you've identified manually
  • Fuzz specific parameters

Burp Suite Community Edition for Manual Testing

Burp Suite is the industry standard for manual web/API security testing. The Community edition (free) includes the core tools needed for most manual testing.

Setup

Download from portswigger.net. Configure your HTTP client or browser to use Burp's proxy at localhost:8080. Import Burp's CA certificate into your trust store to intercept HTTPS.

For API testing without a browser, configure curl or your test HTTP client:

Bash
# Route curl through Burp proxy
curl --proxy http://localhost:8080 \
     --cacert ~/burp-ca.crt \
     -H "Authorization: Bearer $TOKEN" \
     https://localhost:5001/api/orders/123

Intercepting and Modifying Requests

Burp's Proxy โ†’ Intercept tab captures requests before they're sent. You can modify any part โ€” headers, body, path parameters โ€” then forward the modified request.

Use case: capture a legitimate GET /api/orders/123 request, change 123 to 456, forward, observe response. If the response returns order 456 (belonging to another user) โ€” that's IDOR.

Repeater

Repeater lets you send the same request multiple times with modifications. Workflow:

  1. Intercept a request
  2. Send to Repeater (Ctrl+R)
  3. Modify and resend as many times as needed
  4. Compare responses side by side

This is your primary tool for manually testing authorization: send a request with a valid token, then with an invalid token, then with no token, then with a different user's token.

Intruder โ€” Fuzzing Inputs

Intruder automates sending many variations of a request. In Community edition, it is rate-limited (which actually works in your favor for responsible testing).

Use case โ€” fuzzing an ID parameter:

  1. Send GET /api/orders/ยง123ยง to Intruder
  2. Mark 123 as the payload position (ยง)
  3. Set payload list: sequential numbers 1โ€“1000
  4. Start attack โ€” Intruder sends 1000 requests, each with a different ID
  5. Sort responses by status code and length โ€” 200 responses with non-empty bodies indicate resources you can access

Testing Authentication

Test Matrix for Auth Endpoints

For each protected endpoint, test systematically:

Bash
# 1. Valid token โ€” should succeed
curl -H "Authorization: Bearer $VALID_TOKEN" https://api/orders/123
# Expected: 200 OK

# 2. No token โ€” should fail
curl https://api/orders/123
# Expected: 401 Unauthorized

# 3. Malformed token
curl -H "Authorization: Bearer not.a.real.token" https://api/orders/123
# Expected: 401 Unauthorized

# 4. Expired token
curl -H "Authorization: Bearer $EXPIRED_TOKEN" https://api/orders/123
# Expected: 401 Unauthorized

# 5. Token for different audience (alg confusion test)
# Create a JWT signed with HS256 using the RS256 public key as HMAC secret
# Expected: 401 Unauthorized

Testing for Algorithm Confusion (JWT)

Algorithm confusion attacks exploit APIs that trust the alg header in the JWT:

Python
import jwt
import base64

# Read the RS256 public key (which is public knowledge)
with open("public_key.pem") as f:
    public_key = f.read()

# Create a token signed with HS256 using the public key as the HMAC secret
malicious_payload = {"sub": "user-123", "role": "admin"}
malicious_token = jwt.encode(
    malicious_payload,
    public_key,
    algorithm="HS256"
)

print(malicious_token)
# Send this token to the API โ€” if it accepts it, the API has algorithm confusion vulnerability

Expected result: 401 Unauthorized. If the API returns 200, it is accepting an HS256 token when it should only accept RS256 โ€” a critical authentication bypass.

Testing for alg: none

Bash
# Craft a JWT with no signature (alg: none)
# Header: {"alg":"none","typ":"JWT"}
# Payload: {"sub":"admin-user-id","role":"admin"}
# Signature: (empty)

NONE_TOKEN="eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbi11c2VyLWlkIiwicm9sZSI6ImFkbWluIn0."
curl -H "Authorization: Bearer $NONE_TOKEN" https://api/admin/users
# Expected: 401 Unauthorized

Testing Authorization โ€” IDOR

IDOR (Insecure Direct Object Reference) is one of the most common and impactful API vulnerabilities. The test is simple: access a resource belonging to another user.

Setup

You need two test accounts: User A and User B, both created in your test environment.

Bash
# Authenticate as User A
TOKEN_A=$(curl -s -X POST https://api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"usera@test.com","password":"TestPass1!"}' \
  | jq -r '.accessToken')

# Authenticate as User B
TOKEN_B=$(curl -s -X POST https://api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"userb@test.com","password":"TestPass1!"}' \
  | jq -r '.accessToken')

# User A creates an order
ORDER_ID=$(curl -s -X POST https://api/orders \
  -H "Authorization: Bearer $TOKEN_A" \
  -H "Content-Type: application/json" \
  -d '{"productId":1,"quantity":2}' \
  | jq -r '.id')

# User B tries to access User A's order โ€” this should be denied
curl -s -w "\nHTTP Status: %{http_code}" \
  -H "Authorization: Bearer $TOKEN_B" \
  https://api/orders/$ORDER_ID
# Expected: HTTP Status: 403 or 404
# FAIL if: HTTP Status: 200 with User A's data

Test IDOR on every resource type in your API: users, orders, invoices, files, messages. Also test write operations โ€” can User B update or delete User A's order?

Testing Injection โ€” SQL Injection and SQLMap

Manual SQL Injection Test

Insert SQL metacharacters and observe behavior:

Bash
# Test string parameter for SQL injection
curl "https://api/products?search=laptop'"
# If response is 500 Internal Server Error with SQL error โ€” vulnerable
# If response is 400 Bad Request or empty results โ€” likely protected

# Test with classic payload
curl "https://api/products?search=laptop' OR '1'='1"
# If this returns ALL products โ€” vulnerable to injection

SQLMap (Automated SQL Injection Testing)

SQLMap is a specialized tool for detecting and exploiting SQL injection. Use it on your own API:

Bash
# Test a GET endpoint with a potentially injectable parameter
sqlmap -u "https://api/products?search=laptop" \
  -H "Authorization: Bearer $TOKEN" \
  --level=2 \
  --risk=1 \
  --batch \
  --output-dir=./sqlmap-results

# Test a POST endpoint
sqlmap -u "https://api/search" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data='{"query":"laptop"}' \
  --level=2 \
  --batch

Expected result: SQLMap reports "not injectable" for all parameters. Any confirmed injection finding is critical.

Command Injection Testing

If your API calls system commands (file conversion, image processing, report generation), test for command injection:

Bash
# Basic command injection payloads
curl -X POST https://api/convert \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"filename":"report.pdf; whoami"}'

curl -X POST https://api/convert \
  -H "Content-Type: application/json" \
  -d '{"filename":"report.pdf | sleep 5"}'
# If the response takes 5+ extra seconds โ€” time-based command injection

Testing Rate Limiting

Rate limiting is often declared but not actually enforced. Test it:

Bash
#!/bin/bash
# Send 20 rapid requests and count 429 responses
SUCCESS=0
RATE_LIMITED=0

for i in $(seq 1 20); do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -H "Authorization: Bearer $TOKEN" \
    https://api/orders)
  
  if [ "$STATUS" -eq "200" ]; then
    SUCCESS=$((SUCCESS + 1))
  elif [ "$STATUS" -eq "429" ]; then
    RATE_LIMITED=$((RATE_LIMITED + 1))
  fi
done

echo "Success: $SUCCESS, Rate limited: $RATE_LIMITED"
# If RATE_LIMITED = 0 after 20 rapid requests โ€” rate limiting is not working

Test separately:

  • Rate limiting on the login endpoint (more restrictive โ€” 5 per minute per IP)
  • Rate limiting on the general API (looser โ€” 100 per minute per user)
  • Whether rate limiting resets correctly after the window expires

Responsible Disclosure vs. Bug Bounty Programs

If you find a vulnerability in someone else's system (even accidentally):

Responsible disclosure: Contact the organization directly through their security contact (security@company.com, HackerOne private disclosure). Give them a reasonable time to fix before publishing (typically 90 days, following Google Project Zero standard). Provide enough detail to reproduce but don't publish exploit code while the vulnerability is unpatched.

Bug bounty programs: Companies like HackerOne and Bugcrowd host formal programs with:

  • Explicit authorized scope (what you can and cannot test)
  • Legal safe harbor (protection from prosecution for in-scope testing)
  • Monetary rewards for valid findings
  • Clear disclosure timeline

If a company has a bug bounty program, use it. The explicit scope and legal protection make it far safer than informal disclosure.

Summary

Testing your own API's security is a mandatory part of the development process, not optional. Start with automated ZAP scanning to catch obvious issues. Use Burp Suite Repeater for methodical manual testing of authentication and authorization. Test IDOR explicitly with two test accounts. Run SQLMap against any parameter-driven queries. Verify rate limiting actually works with a simple load script. Document every finding with reproduction steps, expected vs. actual behavior, and severity. Fix, retest, close.

Enjoyed this article?

Explore the Security & Compliance learning path for more.

Found this helpful?

Share:๐•

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.