ekofyi
Security Research7 min read

How I analyze API security headers in 30 seconds

A quick checklist for reading HTTP response headers and spotting security misconfigurations before you even look at the response body.

When I hit an API endpoint for the first time, I don't look at the response body. I look at the headers. They tell you more about the backend's security posture in 30 seconds than the docs ever will.

This is the exact flow I run — usually with a single curl command before I even open the app.

The one-liner that starts everything

http
$ curl -sI https://api.target.com/v1/me -H "Authorization: Bearer <token>"

HTTP/2 200
server: nginx/1.19.0
x-powered-by: Express
content-type: application/json; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
set-cookie: session=abc123; Path=/
x-request-id: 7f3a2b1c
content-length: 1842

That's a real (sanitized) response I got from a fintech API last year. Let me walk through what I see — and what's wrong.

🚨 Red flag #1: Server version disclosure

http
server: nginx/1.19.0
x-powered-by: Express

Two pieces of information the attacker shouldn't have. Nginx 1.19.0 has known CVEs. X-Powered-By: Expressconfirms it's a Node.js backend — which narrows down the attack surface significantly. I now know to look for prototype pollution, SSRF via request libraries, and common Express misconfigurations.

💡 Fix

Strip version from server header in nginx config: server_tokens off;. Remove X-Powered-By in Express: app.disable('x-powered-by').

🚨 Red flag #2: Wildcard CORS + credentials

http
access-control-allow-origin: *
access-control-allow-credentials: true

This combination is a textbook vulnerability. It means: "any website can make authenticated requests to this API, and the browser will include cookies." In practice, browsers block * with credentials — but many APIs reflect the Origin header instead, which is even worse. I test this immediately:

bash
$ curl -sI https://api.target.com/v1/me \
  -H "Origin: https://evil.com" \
  -H "Authorization: Bearer <token>"

# If response includes:
# access-control-allow-origin: https://evil.com
# → Full CORS bypass. Any site can steal user data.

⚠️ Severity: High

Origin reflection with credentials is a P1 in most bug bounty programs. It allows full account takeover via a malicious page.

🚨 Red flag #3: Cookie without security flags

http
set-cookie: session=abc123; Path=/

This cookie is missing every security attribute that matters:

  • No Secure — cookie sent over HTTP too (interceptable on public WiFi)
  • No HttpOnly — JavaScript can read it (XSS → session theft)
  • No SameSite — cookie sent on cross-origin requests (CSRF possible)

Compare with what it should look like:

http
set-cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age=3600

🚨 Red flag #4: Missing security headers entirely

What's NOT in that response is just as telling:

Missing HeaderWhat it means
Strict-Transport-SecurityNo HSTS — downgrade attacks possible
X-Content-Type-OptionsMIME sniffing can turn JSON into executable
Content-Security-PolicyNo XSS mitigation at browser level
X-Frame-OptionsClickjacking possible

🚨 Red flag #5: No rate limiting indicators

No X-RateLimit-Limit, no X-RateLimit-Remaining, no Retry-After. This usually means there's no rate limiting at all. I confirm with a quick burst:

bash
$ for i in $(seq 1 100); do
  curl -so /dev/null -w "%{http_code}" \
    https://api.target.com/v1/users/$i \
    -H "Authorization: Bearer <token>"
done | sort | uniq -c

# Output:
  100 200

# 100 requests, all 200. No rate limiting. Enumeration is trivial.

The full checklist (my mental model)

text
┌─ HEADERS SECURITY CHECKLIST ─────────────────────┐
│                                                  │
│  [1] Server/X-Powered-By → version leak?         │
│  [2] CORS → origin reflected? credentials?       │
│  [3] Set-Cookie → Secure? HttpOnly? SameSite?    │
│  [4] HSTS → present? max-age > 1 year?           │
│  [5] CSP → present? restrictive?                 │
│  [6] X-Content-Type-Options → nosniff?           │
│  [7] Rate limit headers → present?               │
│  [8] Cache-Control → no sensitive data cached?   │
│                                                  │
└──────────────────────────────────────────────────┘

Why this matters for automation engineers

If you're building automation against an API, these headers tell you how much resistance you'll face. No rate limiting? Your scraper won't get blocked. Weak CORS? You might be able to piggyback on existing sessions. Leaky server headers? You know exactly what stack you're dealing with.

And if you're on the defensive side — these are the first things an attacker checks. Fix them before they find them.

Related posts

Written by Eko

If you found this useful, follow @ekofyi on X for more notes like this — or get in touch if you have a problem to solve.