SHADOW AI
The API is the primary boundary through which clients, applications, AI agents, workers, integrations, and external systems interact with a secure AI platform.
A modern AI platform may expose APIs for:
Authentication
User management
Projects
File uploads
AI generation
Image processing
Video processing
Document ingestion
Search
RAG
Memory
Agent execution
Payments
Notifications
Administration
Analytics
Every API endpoint therefore represents a potential security boundary.
A secure API architecture must enforce security consistently rather than relying on individual developers to remember every control for every endpoint.
This chapter establishes a reusable API security architecture covering:
Authentication middleware
Authorization middleware
API keys
OAuth/OIDC
Request validation
Rate limiting
Abuse prevention
Resource limits
CORS
CSRF
Idempotency
Replay protection
API versioning
Secure error handling
Security telemetry
API security testing
A typical secure request pipeline is:
Client
↓
TLS
↓
Edge / CDN
↓
WAF / Abuse Controls
↓
API Gateway
↓
Request ID
↓
Request Size Limits
↓
Authentication
↓
Authorization
↓
Rate Limit
↓
Input Validation
↓
Idempotency / Replay Controls
↓
Application Service
↓
Repository / External Service
↓
Response Validation
↓
Security Logging
↓
Client
The exact ordering may vary for individual controls, but the principle is consistent:
Security should be enforced systematically before sensitive business operations execute.
API traffic should use HTTPS.
TLS protects data while moving between:
Client
↕
API
It protects against network-level interception of:
authentication credentials
session cookies
API keys
user data
AI prompts
generated content metadata
Production APIs should not expose sensitive functionality over unencrypted HTTP.
HTTP can generally be redirected to HTTPS at the edge where appropriate.
The API gateway provides a centralized enforcement point.
Potential responsibilities include:
TLS termination
Request routing
Authentication integration
Rate limiting
Request-size limits
IP controls
WAF integration
API version routing
Observability
Abuse detection
However, the gateway should not become the only authorization boundary.
Application services must still enforce business authorization.
Authentication middleware establishes the identity associated with a request.
Conceptually:
Request
↓
Credential / Session
↓
Verification
↓
Identity Context
The resulting context might contain:
userId
tenantId
sessionId
roles
scopes
authenticationMethod
The identity context should be generated from trusted authentication data.
A request context can be represented conceptually as:
interface AuthContext {
userId: string;
tenantId: string;
sessionId?: string;
roles: string[];
scopes: string[];
authenticationMethod: string;
}
This context should be treated as server-generated security state.
Client-supplied values should not override it.
Authentication answers:
Who is calling?
Authorization answers:
Is this caller allowed to perform this operation?
Authorization can operate at multiple levels:
Global permission
Tenant permission
Resource permission
Object ownership
Field permission
Action permission
For example:
Authenticated user
↓
Tenant member
↓
Project member
↓
Project editor
↓
Allowed to update project
RBAC assigns permissions through roles.
Example:
Viewer
Editor
Manager
Administrator
A simplified model:
Viewer
→ project.read
Editor
→ project.read
→ project.update
Manager
→ project.read
→ project.update
→ project.delete
Administrator
→ broader administrative permissions
Roles should not automatically provide unrelated permissions.
For complex applications, permissions can be more precise.
Examples:
project.read
project.update
project.delete
generation.create
generation.read
generation.cancel
billing.read
billing.manage
user.manage
This allows policies to be composed more precisely than relying entirely on broad roles.
An endpoint may require both:
permission = project.update
and:
project belongs to user's tenant
Therefore:
Permission Check
+
Resource Scope Check
=
Authorization Decision
This helps prevent IDOR/BOLA vulnerabilities.
A typical API request may follow:
1. Receive request
2. Generate/request request ID
3. Validate protocol basics
4. Apply request-size limits
5. Authenticate
6. Establish tenant context
7. Apply rate limits
8. Authorize
9. Validate endpoint input
10. Apply idempotency where required
11. Execute business logic
12. Validate response
13. Record security telemetry
Not every endpoint requires every step, but the application should have explicit policies rather than accidental behavior.
Some endpoints may intentionally be public.
Examples:
GET /health
GET /version
POST /auth/login
POST /auth/register
Public endpoints still require:
input validation
rate limiting
abuse controls
resource limits
safe error handling
Public does not mean unrestricted.
Authentication endpoints deserve special protection because they are attractive to automated attackers.
Examples:
POST /auth/login
POST /auth/register
POST /auth/password-reset
POST /auth/mfa/verify
Controls can include:
stricter rate limits
credential abuse detection
request-size limits
generic authentication errors
monitoring
progressive delays
API keys are useful for machine-to-machine or developer integrations.
A key can conceptually contain:
key identifier
secret value
owner
tenant
scopes
createdAt
expiresAt
revokedAt
The secret should not be stored unnecessarily in plaintext.
Where practical, store a protected representation that allows verification without exposing the original secret.
A useful API-key architecture separates:
Key ID
+
Secret
The key ID helps identify which credential is being used.
The secret provides authentication.
This allows the server to efficiently locate metadata without storing the entire secret in searchable plaintext.
API keys should have minimal permissions.
For example:
generation:create
generation:read
is preferable to:
admin:*
for a key used only by a generation service.
Least privilege reduces the impact of credential compromise.
Keys should support:
creation
expiration
rotation
revocation
A key should not remain valid indefinitely unless there is a documented reason and compensating controls.
Users should be able to revoke compromised keys.
A safe rotation workflow is:
Create new key
↓
Deploy new key
↓
Verify new key
↓
Revoke old key
This allows services to migrate without unnecessary downtime.
For production systems, overlapping validity for a controlled migration period may be useful.
OAuth 2.0 is an authorization framework.
OpenID Connect builds an identity layer on top of OAuth 2.0.
These technologies can support:
delegated authorization
external login
enterprise identity
third-party integrations
The platform should distinguish authentication from delegated authorization.
For browser-based applications, a common architecture uses an authorization-code-based flow with appropriate protections.
Conceptually:
User
↓
Application
↓
Identity Provider
↓
Authorization
↓
Authorization Code
↓
Application Backend
↓
Token Exchange
↓
Authenticated Session
Modern implementations should use PKCE where applicable to protect the authorization flow.
When accepting an identity from an OIDC provider, the platform should validate relevant token properties such as:
issuer
audience
signature
expiration
nonce
authorization context
The application should not simply trust arbitrary identity claims received from the client.
Users may connect multiple identity providers to one account.
For example:
Account
├── Password
├── Google identity
└── Enterprise identity
Account linking should require sufficient authentication to prevent an attacker from attaching their own external identity to a victim's account.
Cross-Origin Resource Sharing controls browser-origin access.
A secure API should explicitly define allowed origins.
For example:
https://app.example.com
rather than broadly allowing arbitrary origins.
When cookies or credentials are involved, CORS configuration becomes particularly sensitive.
Cookie-based authentication requires protection against cross-site request forgery.
Controls can include:
SameSite cookies
CSRF tokens
Origin validation
Referer validation where appropriate
avoiding state changes through GET
strict CORS configuration
State-changing operations should normally use methods such as:
POST
PUT
PATCH
DELETE
rather than GET.
Every API endpoint should validate:
body
query parameters
path parameters
headers
content type
request size
Validation should occur before business logic.
Example:
POST /projects
may require:
{
"name": "string",
"description": "string"
}
The server should reject unexpected or invalid input.
Static TypeScript types do not validate external network input.
This is unsafe to assume:
type CreateProjectInput = {
name: string;
};
because an HTTP request can still contain:
{
"name": 123,
"unexpected": "value"
}
Runtime schemas should validate actual network data.
Common approaches include schema-validation libraries or equivalent server-side validation systems.
For security-sensitive operations, prefer explicit fields.
Instead of accepting arbitrary JSON:
any field
define:
name
description
visibility
and reject or ignore unauthorized fields according to the API contract.
This helps prevent mass-assignment vulnerabilities.
Endpoints should verify expected content types.
For example:
application/json
multipart/form-data
An endpoint designed for JSON should not blindly parse arbitrary content.
Content-type enforcement can also reduce unexpected parser behavior.
Every endpoint should have an appropriate maximum request size.
This is especially important for AI applications because users may submit:
images
videos
documents
audio
prompts
batch requests
Different endpoints should have different limits.
For example:
Authentication → small
Metadata → small
Document upload → larger
Video upload → much larger
Large media should generally use controlled object-storage upload mechanisms rather than forcing huge payloads through every API server.
Rate limiting controls request frequency.
A simple model is:
requests
↓
counter
↓
threshold
↓
allow / reject
But production systems need more sophisticated dimensions.
Possible keys include:
IP
user
tenant
API key
endpoint
resource
operation
Different endpoint categories should use different policies.
Strict protection.
Moderate limits.
Stricter resource controls.
Potentially expensive; require stronger quotas.
Limit both request rate and resource consumption.
Low volume with strong access control.
A token bucket is a common rate-limiting model.
Conceptually:
Bucket capacity = N
Tokens refill over time
Each request consumes token(s)
No tokens → request delayed/rejected
This allows controlled bursts while maintaining an average rate.
The actual configuration should be based on load testing.
In a multi-instance platform:
API instance A
API instance B
API instance C
a local in-memory counter is insufficient.
Otherwise an attacker may distribute requests across instances.
A shared mechanism such as a centralized cache or gateway-level limiter can maintain consistent limits.
When a client exceeds a limit, the API should provide a controlled response.
For example:
HTTP 429 Too Many Requests
The response may include appropriate retry information without exposing internal implementation details.
AI requests can consume significantly different amounts of resources.
For example:
100 short text requests
may be much cheaper than:
100 high-resolution video generations
Therefore, simple request counts may be insufficient.
A more advanced model can consider:
requests
tokens
compute units
media duration
resolution
model cost
queue workload
Quotas define usage over a longer period.
Examples:
daily generations
monthly tokens
storage capacity
video minutes
API calls
document pages
Rate limits control short-term frequency.
Quotas control longer-term consumption.
Both may be required.
A user may send a small number of requests that each consume substantial resources.
Therefore, concurrency controls are useful.
Example:
User
├── Generation 1
├── Generation 2
├── Generation 3
└── Generation 4
The platform may limit the number of simultaneously running expensive jobs.
This prevents one account from monopolizing worker capacity.
Rate limiting alone does not detect every abuse pattern.
Signals can include:
unusual request bursts
repeated failures
high-cost operations
account creation spikes
unusual API-key usage
suspicious geographic changes
repeated endpoint probing
abnormal AI generation patterns
These signals can feed a risk engine.
A conceptual architecture is:
Request
↓
Rate Limit
↓
Behavior Signals
↓
Risk Evaluation
↓
Normal → Allow
Suspicious → Challenge / Restrict
High Risk → Block / Investigate
Automated controls should have safeguards against false positives.
Some operations should not execute twice if the same request is accidentally retried.
Examples:
Create payment
Create generation job
Create export
Create subscription
An idempotency key can identify the logical operation.
Conceptually:
POST /generations
Idempotency-Key: abc123
The server records the result associated with the key.
If the same request arrives again, it can return the existing result instead of creating another operation.
An idempotency record can contain:
key
userId
tenantId
request fingerprint
status
response reference
createdAt
expiresAt
The key should be scoped appropriately.
One user's idempotency key should not interfere with another user's request.
Replay attacks occur when a previously valid request or credential is reused unexpectedly.
Controls can include:
short-lived credentials
nonce values
timestamps
request signatures
idempotency keys
token rotation
server-side state
The appropriate mechanism depends on the API type.
Machine-to-machine APIs may use signed requests.
Conceptually:
Request
+
Timestamp
+
Nonce
↓
Signature
↓
Server verification
The server validates:
signature
timestamp window
nonce uniqueness
credential status
request scope
This can provide stronger request integrity for certain integrations.
APIs should have a controlled versioning strategy.
For example:
/api/v1/projects
/api/v2/projects
or an equivalent header-based strategy.
Versioning helps manage:
security changes
schema changes
breaking changes
migration periods
Deprecated API versions should have documented retirement dates.
When retiring an API:
Announce
↓
Monitor usage
↓
Provide migration path
↓
Restrict new clients
↓
Disable old version
Old insecure endpoints should not remain indefinitely simply for backward compatibility.
API errors should be predictable but not overly informative.
A structured error can be:
{
"error": {
"code": "INVALID_REQUEST",
"message": "The request could not be processed.",
"requestId": "..."
}
}
Avoid returning:
database stack trace
internal file path
secret value
SQL query
provider credential
Consistent status codes help clients behave correctly.
Examples:
200 OK
201 Created
202 Accepted
204 No Content
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict
413 Payload Too Large
429 Too Many Requests
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
The application should define consistent semantics for these codes.
A useful distinction is:
401
→ Authentication is missing or invalid.
403
→ Authentication exists, but the operation is not permitted.
The exact response strategy may vary where minimizing information disclosure is important.
API responses may benefit from appropriate security headers.
Depending on the architecture, examples include:
Strict-Transport-Security
X-Content-Type-Options
Content-Security-Policy
Referrer-Policy
The exact headers should be determined by whether the endpoint serves browser content, API responses, or both.
Security-relevant API logs can include:
requestId
timestamp
route
method
status
userId
tenantId
authentication method
latency
resource identifier
security decision
Avoid logging sensitive payloads by default.
Particularly avoid:
password
session token
API key secret
MFA secret
reset token
payment credentials
private document content
Every request should have a correlation/request identifier.
Example:
Request ID:
req_01H...
This identifier can connect:
API log
↓
service log
↓
database event
↓
queue job
↓
worker log
↓
security alert
This is extremely useful during incident investigation.
Important metrics include:
request count
error rate
latency
429 rate
401 rate
403 rate
5xx rate
authentication failures
authorization failures
request size
queue latency
resource consumption
AI-specific metrics may include:
token usage
generation duration
model failures
provider errors
media processing duration
Security monitoring should detect patterns such as:
Repeated 401s
Repeated 403s
Large number of 404s
Sudden API-key activity
High 429 volume
Abnormal export activity
Unusual administrative operations
These signals can feed the security operations system.
A WAF can provide an additional edge security layer for common web attacks.
Potential protections include:
malicious request patterns
abnormal request rates
known attack signatures
bot controls
IP reputation signals
A WAF should complement application security rather than replace it.
Prompt injection primarily targets AI processing rather than ordinary HTTP authentication.
Nevertheless, API design can reduce impact by separating:
User input
System policy
Tool authorization
Trusted application state
An API should never allow user-controlled prompt content to directly determine privileged backend actions without policy enforcement.
Agent APIs require additional controls.
For example:
POST /agents/run
should not simply execute arbitrary tools.
The flow should be:
Authentication
↓
Authorization
↓
Agent policy
↓
Allowed tools
↓
Input validation
↓
Execution limits
↓
Human approval if required
↓
Tool execution
File-upload APIs require additional security controls.
The API should validate:
authentication
authorization
file size
declared type
detected type
filename
upload destination
tenant
resource ownership
Uploaded files should not automatically become trusted application content.
Expensive AI operations should often be asynchronous.
Instead of:
POST /generate
↓
wait 90 seconds
↓
return result
use:
POST /generate
↓
create job
↓
return job ID
Then:
GET /generations/{id}
can return job state.
This improves resilience and makes resource controls easier.
Every asynchronous job should have an authorization relationship.
Conceptually:
Job
├── id
├── userId
├── tenantId
└── status
A user should not be able to retrieve another user's job merely by guessing its ID.
A complete API security test program should include:
missing credentials
invalid credentials
expired credentials
revoked credentials
cross-user access
cross-tenant access
privilege escalation
object-level access
invalid types
unexpected fields
oversized inputs
malformed JSON
invalid identifiers
rate-limit behavior
concurrency
repeated requests
expensive operations
batch operations
session fixation
session reuse
logout
revocation
token rotation
| Test | Expected Result |
|---|---|
| No authentication | Rejected |
| Expired session | Rejected |
| Valid authentication | Accepted if authorized |
| Wrong tenant resource | Rejected |
| Wrong object owner | Rejected |
| Insufficient role | Rejected |
| Oversized request | Rejected |
| Invalid JSON | Rejected |
| Invalid field | Rejected |
| Excessive requests | Rate limited |
| Duplicate idempotent request | No duplicate operation |
| Expired API key | Rejected |
| Revoked API key | Rejected |
| Invalid OAuth token | Rejected |
HTTPS enforced.
TLS configuration is managed securely.
Sensitive data is never intentionally transmitted over plaintext HTTP.
Authentication middleware exists.
Session/token validation is centralized.
API keys are scoped.
OAuth/OIDC tokens are validated correctly.
Service identities are authenticated.
Authorization is separate from authentication.
Tenant context is trusted.
Object-level authorization exists.
Privileged operations require explicit permissions.
Administrative APIs are separately protected.
Request bodies are validated.
Query parameters are validated.
Path parameters are validated.
Content types are validated.
Request sizes are bounded.
Rate limiting exists.
Quotas exist where appropriate.
Concurrency limits exist for expensive operations.
Abuse detection exists.
Expensive AI operations are controlled.
Idempotency is implemented where necessary.
Retry-safe operations are defined.
API versioning exists.
Errors are standardized.
Timeouts are configured.
Request IDs exist.
Authentication failures are monitored.
Authorization failures are monitored.
Rate-limit events are monitored.
Administrative operations are audited.
The resulting architecture can be represented as:
┌───────────────┐
│ Client │
└───────┬───────┘
│
▼
┌───────────────┐
│ HTTPS │
└───────┬───────┘
│
▼
┌───────────────┐
│ CDN / WAF │
└───────┬───────┘
│
▼
┌───────────────┐
│ API Gateway │
└───────┬───────┘
│
▼
┌───────────────┐
│ Request ID │
└───────┬───────┘
│
▼
┌───────────────┐
│ Rate Limit │
└───────┬───────┘
│
▼
┌───────────────┐
│ Authentication│
└───────┬───────┘
│
▼
┌───────────────┐
│ Authorization │
└───────┬───────┘
│
▼
┌───────────────┐
│ Input │
│ Validation │
└───────┬───────┘
│
▼
┌───────────────┐
│ Idempotency / │
│ Replay Guard │
└───────┬───────┘
│
▼
┌───────────────┐
│ Application │
│ Service │
└───────┬───────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
Database Queue AI Provider
│ │ │
└────────────┼────────────┘
│
▼
┌───────────────┐
│ Response │
│ Validation │
└───────┬───────┘
│
▼
┌───────────────┐
│ Audit / │
│ Telemetry │
└───────┬───────┘
│
▼
Client
The most important API security principles are:
Authenticate every protected request.
Authorize every sensitive operation.
Do not treat authentication as authorization.
Enforce tenant and object boundaries server-side.
Validate all external input at runtime.
Use explicit allowlists for sensitive fields and operations.
Bound request sizes and computational resources.
Use layered rate limiting and abuse controls.
Scope and rotate API keys.
Validate OAuth/OIDC tokens correctly.
Use secure cookie and CSRF protections for browser sessions.
Use idempotency for operations where retries could duplicate effects.
Protect asynchronous jobs with ownership and authorization checks.
Keep sensitive information out of logs and error responses.
Monitor authentication, authorization, abuse, and administrative events.
Test cross-user and cross-tenant access continuously.
The API security layer transforms authentication and database protections into a consistent, enforceable security boundary.
A mature implementation does not rely on one mechanism. Instead, it combines:
TLS
+
Gateway Controls
+
Authentication
+
Authorization
+
Tenant Isolation
+
Input Validation
+
Rate Limiting
+
Resource Controls
+
Idempotency
+
Audit Logging
+
Monitoring
This layered architecture reduces the probability that a single implementation mistake becomes a platform-wide compromise.
The next stage is to address the API's most important external integration boundary: third-party identity and delegated authorization.
Next: Chapter 108 — Secure OAuth 2.0 & OpenID Connect Integration: Identity Providers, PKCE, Token Validation, Account Linking, Session Federation & SSO Security
Comments
Post a Comment