[Web/API] Section 3 - API Vulnerabilities Beyond Access Control

 

1. Section Goal

This section covers API security issues beyond basic BOLA/BFLA.

Main focus:

  • Login/user enumeration

  • Password reset token weakness

  • OTP brute force

  • Token lifecycle issues

  • Token in URL

  • JWT tampering

  • Deprecated/old APIs

  • Webhook security

  • GraphQL security

  • Business logic abuse

  • Shadow/undocumented APIs


2. Login User Enumeration

What it is

User enumeration happens when the application reveals whether a username/email exists.

Example:

Existing email + wrong password → 401 Invalid password
Non-existing email              → 404 User not found

This tells attacker which email is valid.

Why it is a problem

Attacker can build a valid user list and then perform:

  • Credential stuffing

  • Password brute force

  • Phishing

  • Targeted account takeover attempts

Root cause

The backend gives different responses for different failed login cases.

Correct fix

For every failed login attempt, return same generic response.

Example:

Invalid credentials

Also add:

  • Rate limiting

  • Account lockout/throttling

  • Monitoring

  • MFA/risk-based challenge

  • Same timing behavior as much as possible

Memory line

Login errors should not reveal whether email or password was wrong.


3. Weak Password Reset Token

What it is

Password reset token is weak, guessable, long-lived, reusable, or not rate-limited.

Example:

https://app.example.com/reset-password?token=123456

Issues:

6-digit token
Valid for 24 hours
Unlimited attempts
Reusable
Not bound to user/session

Why it is a problem

Attacker can brute force or guess reset tokens and reset another user’s password.

Impact:

  • Account takeover

  • Privilege escalation if admin account is targeted

  • Loss of user trust

Root cause

Poor reset token design and missing server-side controls.

Correct fix

Password reset token should be:

  • Long

  • Cryptographically random

  • Short-lived

  • Single-use

  • User-bound

  • Stored hashed server-side

  • Rate-limited per user/IP/token

Important note:

Password reset tokens in URL are common and not automatically wrong. The issue is when the token is weak, long-lived, reusable, logged, or not rate-limited.

Memory line

Reset token = long, random, short-lived, single-use, user-bound, rate-limited.


4. OTP Brute Force / Missing Rate Limiting

What it is

OTP is valid, but backend allows unlimited attempts.

Example:

6-digit OTP
Valid for 10 minutes
No attempt limit
No rate limit

Why it is a problem

6-digit OTP has limited combinations.

If there is no attempt control, attacker can automate guesses.

Root cause

Backend does not enforce attempt limits, throttling, or lockout.

Correct fix

  • Limit OTP attempts

  • Rate limit per user/IP/session/device

  • Short OTP expiry

  • OTP should be random

  • OTP should be one-time use

  • Invalidate old OTP when new OTP is generated

  • Monitor failed attempts

Memory line

6-digit OTP is fine only with strict attempt limit, expiry, and one-time use.


5. Token Lifecycle / Missing Revocation

What it is

Access token remains valid even after:

Logout
Password change
Account disabled
Session terminated

Example:

Access token valid for 7 days.
User logs out.
Same old token still works.

Why it is a problem

If attacker steals the token, they can continue using it even after user/admin tries to stop access.

Impact:

  • Session hijacking

  • Account takeover continuation

  • Failed incident response

  • Disabled users still accessing APIs

Root cause

Backend only checks token signature/expiry, but does not check current session/user state.

Correct fix

  • Use short-lived access tokens

  • Use refresh token rotation

  • Maintain revocation/session store

  • Invalidate token after logout

  • Invalidate token after password change

  • Reject token if user is disabled

  • Use token/session version

  • Check password_changed_at

  • Monitor suspicious token use

Memory line

JWT signature valid does not mean session should still be trusted.


6. Access Token in URL

What it is

Bearer access token is passed in URL query parameter.

Example:

GET /api/v1/reports/export?access_token=eyJhbGciOi...

Why it is a problem

URLs leak easily into:

  • Browser history

  • Bookmarks

  • Referrer headers

  • Proxy logs

  • CDN logs

  • Web server access logs

  • Application logs

  • Analytics tools

  • Screenshots/shared links

If token leaks, attacker can replay it.

Root cause

Insecure token handling.

Correct fix

For API authentication:

Authorization: Bearer <token>

Also:

  • Do not put bearer tokens in URL

  • Redact tokens from logs

  • Use short-lived tokens

  • Rotate/revoke exposed tokens

  • Avoid logging authorization secrets

Important distinction

Access token should normally go in Authorization header.

Password reset token can be in URL if it is strong, short-lived, single-use, and user-bound.

Short-lived signed download URLs can be acceptable if scoped and time-limited.

Memory line

Bearer access token in URL = token leakage risk. Use Authorization header.


7. JWT Tampering / Missing Signature Validation

What it is

Attacker modifies JWT claims and backend accepts modified token.

Example:

Original token payload:

{
  "user_id": "1001",
  "role": "user"
}

Modified token payload:

{
  "user_id": "1001",
  "role": "admin"
}

If backend accepts this, JWT validation is broken.

Why it is a problem

Attacker can modify:

  • Role

  • User ID

  • Tenant ID

  • Permissions

  • Scope

Impact:

  • Privilege escalation

  • Cross-user access

  • Cross-tenant access

  • Admin access

Root cause

Backend is trusting decoded JWT claims without verifying signature and expected claims.

Correct fix

Backend must validate:

  • Signature

  • Expected algorithm

  • Expiry: exp

  • Issuer: iss

  • Audience: aud

  • Tenant/user/session state

  • Role/permission on backend

Reject:

  • Unsigned tokens

  • Tampered tokens

  • Unexpected algorithms

  • Expired tokens

  • Wrong issuer/audience

Memory line

JWT can be decoded by anyone; it is trusted only after signature and claims are validated.


8. Deprecated / Old API Exposure

What it is

Old API version is still reachable and has weaker security controls.

Example:

/api/v2/reports/export → 403 for normal user
/api/v1/reports/export → 200 and downloads all reports

Why it is a problem

Attacker bypasses the secured new API by directly calling old API.

Impact:

  • Unauthorized data export

  • Access control bypass

  • Sensitive data exposure

  • Compliance/privacy issue

Root cause

Poor API lifecycle management.

Common causes:

  • Old API not removed

  • Old API not secured like new API

  • Poor inventory

  • Legacy clients still using old API

  • Security testing focused only on current UI/API

Correct fix

  • Enforce same authorization on old API immediately

  • Deprecate/remove old API if not required

  • Maintain complete API inventory

  • Monitor legacy endpoint usage

  • Migrate old clients

  • Include old APIs in testing

  • Block old endpoints at gateway if obsolete

Memory line

Old API is not automatically vulnerable; old API with weaker controls is the issue.


9. Webhook Security

What is a webhook

Webhook is automatic server-to-server communication.

Example:

Payment provider backend → Application backend

Webhook means:

Third-party system tells your backend that an event happened.

Examples:

  • Payment successful

  • Refund processed

  • Shipment delivered

  • GitHub code pushed

  • User created in identity provider

Payment webhook example

POST /api/payment/webhook

{
  "order_id": "ORD-1001",
  "status": "paid",
  "amount": 5000
}

What is the vulnerability

Backend accepts webhook body without verifying it came from real provider.

Example attack:

Attacker/Postman/Burp → /api/payment/webhook
status=paid

Backend marks order as paid.

Why it is a problem

Attacker can:

  • Mark unpaid order as paid

  • Trigger shipment/service activation

  • Manipulate amount/status/order ID

  • Replay old webhook

  • Cause direct business loss

Root cause

Missing webhook trust validation.

Backend does not validate:

  • Provider signature

  • Timestamp freshness

  • Event ID uniqueness

  • Order ID

  • Amount

  • Currency

  • Payment status

  • Provider transaction ID

Correct fix

Backend should:

  1. Verify webhook signature using provider secret.

  2. Verify timestamp freshness.

  3. Store and process event ID only once.

  4. Match order ID, amount, currency, and status with backend records.

  5. Optionally verify transaction directly with provider API.

  6. Reject unsigned/invalid/replayed requests.

  7. Log and monitor suspicious webhook attempts.

Proxy clarification

Real webhook is backend-to-backend, so user browser/Burp proxy usually should not see full provider webhook traffic.

User may see:

Payment successful
Order confirmed
Receipt generated

But normally should not see:

X-Signature
X-Timestamp
Webhook event ID
Full provider callback headers
Webhook secret

If full webhook details appear in browser/mobile proxy, investigate further.

Important distinction

Seeing order ID/payment status in frontend is not automatically a vulnerability.

It becomes vulnerable if backend trusts client-controlled payment status or unsigned webhook data.

Memory line

Webhook body is untrusted until signature, timestamp, event ID, and business values are verified.


10. GraphQL Security

What is GraphQL

GraphQL usually exposes one endpoint:

POST /graphql

Instead of many REST endpoints, client sends query/mutation inside the request body.

Example query:

query {
  user(id: "123") {
    name
    email
  }
}

Example mutation:

mutation {
  updateUserRole(userId: "456", role: "admin")
}

What is introspection

Introspection lets user ask GraphQL:

What queries exist?
What mutations exist?
What fields exist?
What arguments are required?

It is useful for developers and tools.

Security view on introspection

In production, public unauthenticated introspection should generally be disabled or tightly restricted.

Why?

Because it exposes the API map:

  • Hidden mutations

  • Admin operations

  • Internal object names

  • Sensitive fields

  • Deprecated operations

  • Argument names

But introspection is not the main vulnerability

Introspection reveals what exists.

Authorization controls who can use it.

Even if introspection is disabled, attacker may discover mutations through:

  • JavaScript files

  • Mobile app traffic

  • Old docs

  • GraphQL errors

  • Postman collections

  • Leaked schema

  • Guessing

What is resolver authorization

In GraphQL, each query or mutation is handled by a backend function called a resolver.

Example:

updateUserRole mutation → updateUserRole resolver/function
resetPassword mutation  → resetPassword resolver/function
getUser query           → getUser resolver/function

Resolver authorization means:

Before executing the resolver, backend checks whether logged-in user is allowed to perform that action.

Example checks:

  • Is user authenticated?

  • Is user admin?

  • Can this admin update this user?

  • Is target user in same tenant?

  • Is requested role change allowed?

Vulnerable example

mutation {
  updateUserRole(userId: "456", role: "admin")
}

Normal user sends this manually and it succeeds.

Why it is a problem

Impact:

  • Privilege escalation

  • Account takeover

  • Admin access

  • Tenant compromise

Root cause

Missing resolver-level authorization.

UI hides the action, but backend still allows it.

Correct fix

  • Enforce authorization on every resolver

  • Sensitive mutations require admin permission

  • Tenant checks on objects

  • Do not rely on UI hiding

  • Disable/restrict introspection in production

  • Add audit logs for sensitive mutations

  • Add rate limiting/monitoring

Memory line

Introspection reveals what exists. Resolver authorization controls who can use it.


11. Business Logic / API Abuse

What it is

A request is technically valid, but it violates business rules.

Example:

POST /api/v1/apply-coupon

{
  "cart_id": "CART-1001",
  "coupon_code": "WELCOME50"
}

Business rule:

WELCOME50 can be used only once per user.

But attacker sends same request repeatedly and discount applies again and again.

Why it is a problem

Attacker can:

  • Reduce cart value repeatedly

  • Get products for very low or zero price

  • Abuse coupons/promotions

  • Cause direct business loss

  • Automate abuse across accounts

Root cause

Backend does not enforce business rule or state validation.

Missing checks:

  • Has user already used this coupon?

  • Is coupon already applied to this cart?

  • Is coupon valid for this user?

  • Is discount within allowed limit?

  • Is cart total recalculated server-side?

Correct fix

  • Enforce coupon rules server-side

  • Bind coupon usage to user/account

  • Prevent duplicate application on same cart

  • Mark coupon as used after successful use

  • Recalculate discount server-side

  • Add transaction/locking to prevent race conditions

  • Monitor abuse patterns

Memory line

Business logic flaw = request is valid technically, but violates business rules.


12. Shadow / Undocumented API

What it is

API endpoint exists but is not documented, not visible in UI, or not included in normal testing/inventory.

Example:

GET /api/internal/admin/export-all-users

Normal user token returns all users.

Why it is a problem

Undocumented APIs may be missed from:

  • Security testing

  • Monitoring

  • Access-control review

  • API inventory

  • Gateway policy enforcement

  • Documentation

Important correction

Undocumented does not automatically mean vulnerable.

The issue is:

Undocumented/internal API + weak/missing authorization

Impact

Attacker can:

  • Discover hidden endpoints from JS/mobile/docs/logs

  • Access internal/admin functions

  • Export user data

  • Dump emails, names, roles, tenant info

  • Cause privacy/compliance issue

Root cause

  • Incomplete API inventory

  • Internal endpoint publicly reachable

  • Missing backend authorization

  • Sensitive routes exposed in JavaScript

  • Poor API governance

  • Endpoint excluded from security review

Correct fix

  • Enforce authorization on undocumented/internal APIs

  • Return 403 for normal users

  • Remove or restrict internal APIs

  • Maintain complete API inventory

  • Include all APIs in testing

  • Review JS/mobile apps for hidden routes

  • Monitor access to sensitive export endpoints

  • Apply gateway controls consistently

Memory line

Undocumented does not automatically mean vulnerable; undocumented plus weak authorization is the issue.


13. Quick Classification Table

ScenarioCorrect classification
Different login errors reveal valid emailUser enumeration
6-digit reset token, no rate limitWeak password reset token
OTP has unlimited attemptsOTP brute force / missing rate limiting
Token works after logout/password change/disablePoor token lifecycle / missing revocation
Access token in URLInsecure token handling
JWT role changed to admin and acceptedJWT validation failure
v1 old API bypasses v2 authorizationDeprecated API with weak authorization
Fake payment webhook marks order paidMissing webhook signature validation
GraphQL admin mutation works for normal userMissing resolver-level authorization
Coupon applied repeatedlyBusiness logic flaw / API abuse
Hidden internal API exports all usersShadow API with broken authorization

14. Common Interview Answer Template

Use this structure:

1. This is <vulnerability name>.
2. The backend is trusting <client input/token/object/action/webhook> without validating <authz/signature/state/business rule>.
3. Attacker can <real impact>.
4. Root cause is missing server-side <control>.
5. Fix is <specific backend control>, not only frontend/UI restriction.

Example:

This is an insecure webhook implementation. The backend trusts the webhook body without verifying that it came from the real payment provider. An attacker can send a fake payment confirmation and mark unpaid orders as paid. The root cause is missing signature, timestamp, replay, and business validation. The fix is to verify provider signature, check timestamp freshness, process event ID only once, and match order ID, amount, currency, and status with backend records.

15. Section 3 Final Memory Pack

Login errors should not reveal valid users.

Reset token = long, random, short-lived, single-use, user-bound, rate-limited.

6-digit OTP is acceptable only with strict attempt limits and expiry.

JWT signature valid does not mean session should still be trusted.

Bearer access tokens should not be placed in URLs.

JWT can be decoded by anyone; trust only after signature and claims validation.

Old API is not automatically vulnerable; old API with weaker controls is the issue.

Webhook body is untrusted until signature, timestamp, event ID, and business values are verified.

Webhook = third-party backend automatically calls your backend when an event happens.

GraphQL introspection reveals what exists; resolver authorization controls who can use it.

Business logic flaw = technically valid request violates business rules.

Undocumented API is not automatically vulnerable; undocumented plus weak authorization is the issue.

Dangerous body field accepted = mass assignment.

Normal user calling admin function = BFLA.

Wrong object ID access = BOLA.

Tenant check first, then user/role/object permission check.


Comments