what auditors really want to see
Security auditors open an OpenAPI document with a clear checklist in mind: are all endpoints authenticated? Are error responses fully documented? Is input validated? Whoever understands this perspective builds API documentation that speeds up audits, reduces findings, and builds trust.
Table of Contents
- 1. The audit perspective: what auditors look for first
- 2. Security schemes: the first field checked
- 3. Documenting public endpoints explicitly
- 4. Modeling error responses completely
- 5. Mapping input validation in OpenAPI
- 6. Marking sensitive data in schemas
- 7. OWASP API Security Top 10 and OpenAPI
- 8. Rate limiting and quota in documentation
- 9. Audit findings: avoidable vs. unavoidable
- 10. Summary
- 11. FAQ
1. The audit perspective: what auditors look for first
A security auditor who receives an OpenAPI document does not start at the top and read through linearly. They carry a mental checklist derived from the most common API security problems, usually aligned with the OWASP API Security Top 10. The first thing they look at is the security configuration: are there security schemes at all? Are they defined globally or per endpoint? Which endpoints explicitly declare security: [], meaning no authentication protection?
The second thing they check is the response definitions: are 401 and 403 responses documented for all authenticated endpoints? Are 422 or 400 responses shown for validation failures? Missing responses are not a cosmetic problem, they signal that the API may not handle validation errors correctly and could potentially leak sensitive information in error messages.
The third thing they check is the schemas: are integer fields defined without minimum/maximum? Strings without maxLength? Arrays without maxItems? These gaps in input validation documentation hint at possible injection attack vectors. An OpenAPI document that fills these fields in completely significantly reduces the auditor's workload and shows that the development team is familiar with the relevant security requirements.
2. Security schemes: the first field checked
The security scheme setup is the first thing an auditor analyzes in detail. Missing security schemes on endpoints that return user data are an immediate high-severity finding. Schemes that are too weak (e.g. HTTP Basic Auth without enforced TLS) or schemes without an expiration mechanism (e.g. API keys without rotation documentation) are medium-severity findings. An auditor expects the OpenAPI documentation to contain this information explicitly, not to have to look it up in the runtime configuration.
Particularly critical: endpoints that perform sensitive operations (changing a password, retrieving payment data, admin functions) should explicitly signal higher requirements in the documentation, for example through specific scopes or a description that documents MFA requirements. OpenAPI itself has no MFA type, but the security scheme's description field and the endpoint description are the right place for it.
# Security-focused OpenAPI, what auditors want to see
openapi: 3.1.0
info:
title: Shop API, Security Documented
version: 2.0.0
description: |
## Security Overview
This API uses JWT Bearer authentication for all endpoints.
Tokens expire after 3600 seconds. Refresh tokens expire after 7 days.
All endpoints enforce TLS 1.2+. Rate limiting: 100 requests/minute per token.
## Sensitive Operations
Endpoints under /admin require the admin:* scope.
Password-change endpoints require re-authentication within the last 5 minutes.
# Global security, explicitly set, not implied
security:
- BearerAuth: []
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: |
JWT with RS256 signing. Claims: sub, roles, exp, iat, jti.
Issued by: https://auth.mironsoft.de
JWKS endpoint: https://auth.mironsoft.de/.well-known/jwks.json
Token lifetime: 3600s. Refresh: POST /auth/refresh (7-day refresh token).
Revocation: POST /auth/revoke (immediate effect via jti blacklist).
paths:
/users/{id}/password:
put:
summary: Change user password
description: |
Requires re-authentication within the last 300 seconds.
Returns 403 if the re-auth window has expired.
security:
- BearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [currentPassword, newPassword]
properties:
currentPassword:
type: string
format: password
minLength: 8
maxLength: 128
newPassword:
type: string
format: password
minLength: 12
maxLength: 128
description: Must contain uppercase, lowercase, digit and special char.
responses:
'204': { description: Password changed }
'400': { description: Validation error, weak or malformed password }
'401': { description: Missing or expired token }
'403': { description: Re-auth window expired or wrong current password }
'429': { description: Too many password-change attempts, try again in 15m }
3. Documenting public endpoints explicitly
Endpoints without authentication are not inherently a problem, health checks, public product listings, and login endpoints must be reachable without credentials. What auditors want to see is that these endpoints are deliberately and explicitly marked as public, not simply forgotten. An explicit security: [] combined with a description that explains why the endpoint is public shows that the API designer has thought through the security model.
A common audit finding: an endpoint that returns sensitive data (e.g. product prices including margin data) has no security scheme, not because it is meant to be public, but because it was forgotten. In an OpenAPI specification, this difference is not visible, both look like security: []. The solution is complete documentation of all public endpoints along with their justification in the description.
4. Modeling error responses completely
Complete error response definitions are one of the most telling signals in an OpenAPI specification from an audit perspective. An API that only documents the success case for every endpoint gives no indication of how it handles errors, and that is exactly what auditors want to know. Incomplete error responses hint at possible information disclosure problems: if the API has no defined error messages, it might be returning stack traces, database errors, or internal paths.
The minimum requirement from an audit standpoint: for every authenticated endpoint, 401 (missing token) and 403 (wrong permission) must be documented. For endpoints with input validation, 422 or 400 must be documented, including the schema of the error message. For admin endpoints, 404 instead of 403 should be considered (a security recommendation: for sensitive resources, respond with 404 rather than 403 on missing authorization, so as not to confirm their existence).
# Complete error responses, what auditors expect
components:
schemas:
# Standard error envelope, reused across all error responses
ApiError:
type: object
required: [error]
properties:
error:
type: object
required: [code, message]
properties:
code:
type: string
description: Machine-readable error code (e.g. VALIDATION_FAILED)
example: VALIDATION_FAILED
message:
type: string
description: Human-readable error message. Never includes stack traces.
example: The request body is invalid.
details:
type: array
description: Field-level validation details (422 responses only)
items:
type: object
required: [field, message]
properties:
field: { type: string }
message: { type: string }
# Rate limit error
RateLimitError:
allOf:
- $ref: '#/components/schemas/ApiError'
- type: object
properties:
retryAfter:
type: integer
description: Seconds until the rate limit resets
responses:
Unauthorized:
description: Missing, invalid or expired authentication token.
headers:
WWW-Authenticate:
schema: { type: string }
example: 'Bearer realm="api.mironsoft.de", error="invalid_token"'
content:
application/json:
schema: { $ref: '#/components/schemas/ApiError' }
example:
error: { code: INVALID_TOKEN, message: Token is expired or malformed. }
Forbidden:
description: Authenticated but insufficient permissions.
content:
application/json:
schema: { $ref: '#/components/schemas/ApiError' }
example:
error: { code: INSUFFICIENT_SCOPE, message: Required scope not present in token. }
UnprocessableEntity:
description: Request body failed validation.
content:
application/json:
schema: { $ref: '#/components/schemas/ApiError' }
TooManyRequests:
description: Rate limit exceeded.
headers:
Retry-After: { schema: { type: integer } }
X-RateLimit-Limit: { schema: { type: integer } }
X-RateLimit-Remaining: { schema: { type: integer } }
content:
application/json:
schema: { $ref: '#/components/schemas/RateLimitError' }
5. Mapping input validation in OpenAPI
Input validation constraints in OpenAPI schemas are not just for documentation, they are a security signal to auditors. Fields without maxLength on string types, without maximum on integer types, or without maxItems on array types indicate that input limits may not be implemented at all. That is an attack surface for resource exhaustion attacks and injection attempts.
Particularly relevant from an audit perspective: format fields signal that specific format validation is taking place. format: email shows that email addresses are validated. format: uuid on ID fields shows that ID injection through integer enumeration is prevented. pattern on critical fields (e.g. phone numbers, postal codes) shows that regex validation is in use. Documenting all of these fields in OpenAPI schemas costs little effort but makes audits considerably more efficient.
6. Marking sensitive data in schemas
OpenAPI 3.1 (based on JSON Schema) has the writeOnly and readOnly keywords, which matter in security audits. Passwords, tokens, and other sensitive credentials should be marked as writeOnly: true, signaling that these fields only appear in the request body and are never returned in responses. Fields that appear in the response but never in the request (e.g. internal IDs, timestamps) should have readOnly: true.
The format: password keyword is also relevant: it indicates that the field contains a password and that tooling should treat it accordingly (e.g. masking in Swagger UI). Sensitive fields such as credit card numbers or social security numbers can be marked with a custom extension like x-sensitive: true, this extension has no native OpenAPI semantics, but it is a clear signal for security tooling and human reviewers.
7. OWASP API Security Top 10 and OpenAPI
The OWASP API Security Top 10 (2023) lists the most common API security problems and has direct counterparts in OpenAPI documents. API1 (Broken Object Level Authorization) can be addressed through complete documentation of 401/403 responses and explicit scope requirements per endpoint. API3 (Broken Object Property Level Authorization) is relevant in OpenAPI modeling: if different roles are allowed to see different fields, that should be documented through separate response schemas.
API8 (Security Misconfiguration) is the most common finding that can be derived directly from OpenAPI documents: missing security schemes, undocumented public endpoints, missing error responses. An auditor assessing API8 reads the OpenAPI document and compares it against the running API. Discrepancies between documentation and implementation are an immediate finding, the documentation is then either outdated or the implementation does not match the design.
| OWASP API Finding | OpenAPI Signal | Addressed in OpenAPI | Severity |
|---|---|---|---|
| API1: Broken Object Auth | No 403 for endpoints with ID parameters | Document 403 response + scope requirement | High |
| API3: Excessive Data Exposure | Response schema contains sensitive fields without readOnly | Set writeOnly/readOnly + format: password correctly | Medium |
| API4: Resource Exhaustion | No maxLength/maxItems in schemas | Consistently document constraints | Medium |
| API8: Security Misconfiguration | Missing security schemes or undocumented public endpoints | Global security + explicit security: [] for public | High |
8. Rate limiting and quota in documentation
Rate limiting is not a native mechanism in OpenAPI 3.x, but auditors expect it to be documented. The recommendation: document rate limiting headers (X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After) in the 429 response definitions. That shows auditors that rate limiting is implemented and that the API communicates it. Some teams use custom extensions like x-rate-limit: 100/min at the operation level, these have no native semantics but are a clear signal for security tools and auditors.
An often overlooked detail: different rate limits for different roles (e.g. 100 requests per minute for standard users, 1000 for premium) should be documented in the description of the endpoint or the security scheme. That prevents misunderstandings during testing and gives auditors the information they need to assess denial-of-service risks.
# Rate limiting, documented via responses and headers
paths:
/products/search:
get:
summary: Search products
description: |
Rate limit: 100 requests/minute per authenticated token.
Unauthenticated requests: 10 requests/minute per IP.
Exceeding the limit returns 429 with Retry-After header.
security:
- BearerAuth: []
- {} # also allows unauthenticated (with lower rate limit)
parameters:
- name: q
in: query
required: true
schema:
type: string
minLength: 2
maxLength: 200 # prevent abuse via very long search queries
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100 # cap: prevent bulk scraping via high limit values
default: 20
responses:
'200':
description: Search results
headers:
X-RateLimit-Limit:
description: Requests allowed per minute for this token/IP
schema: { type: integer }
X-RateLimit-Remaining:
description: Requests remaining in current window
schema: { type: integer }
X-RateLimit-Reset:
description: Unix timestamp when the rate limit window resets
schema: { type: integer }
'400':
description: Query too short or too long
$ref: '#/components/responses/UnprocessableEntity'
'429':
description: Rate limit exceeded
$ref: '#/components/responses/TooManyRequests'
10. Summary
Security audits for REST APIs become considerably more efficient with complete OpenAPI documentation, and the number of findings drops. The most important outcome of careful, audit-oriented modeling: auditors spend less time figuring out how the API works and more time assessing whether it works securely. Fully defining security schemes, explicitly marking public endpoints, documenting error responses for all relevant status codes, and mapping input validation constraints into schemas, these are the four pillars of an audit-ready OpenAPI specification.
The long-term benefit goes beyond individual audits: an OpenAPI document that fully documents security requirements serves as a living security concept. New developers on the team immediately see which authentication, which scopes, and which error cases are expected. Automated security tools like 42Crunch or OWASP Zap can validate against the OpenAPI specification and automatically detect discrepancies between documentation and implementation.
OpenAPI Security Audit Readiness, At a Glance
Security Schemes
Fully documented: type, format, expiration, revocation. Global security object set. Public endpoints with explicit security: [] and justification.
Error Responses
401, 403, 422, 429 for all relevant endpoints. Consistent error schema without stack traces. WWW-Authenticate header for 401.
Input Validation
maxLength, minimum, maximum, maxItems for all input fields. format: uuid for IDs. format: password for credentials. writeOnly for sensitive fields.
OWASP Mapping
API1, API3, API4, API8 directly addressable through OpenAPI documentation. Automated validation with 42Crunch or OWASP Zap.