--- name: security-best-practices description: Security review for banking APIs — headers, input validation, log masking, and trace integrity. source: community + V4 banking patterns (pinned 2026-03-19) --- # Security Best Practices for Banking APIs ## Overview Review and enforce security practices in API implementations, with focus on banking-grade requirements: input validation, header sanitization, log masking, and trace integrity. ## When to Use - Reviewing header handling in Resources and Adapters - Validating input sanitization on DTOs - Auditing log filters for sensitive data exposure - Verifying trace integrity (SecurityTracePort) - Pre-delivery security review ## Instructions ### 1. Input Validation **Every input field MUST have explicit validation:** ```java // ✅ Correct — validation codes tied to error catalog @NotNull(message = "VDE01") @Valid private CustomerReference customerReference; // ✅ Correct — pattern restricts to known values @NotNull(message = "VDE01") @Pattern(regexp = "^[VEJPGCR]$", message = "VDE02") private String customerIdType; // ❌ Incorrect — no validation private String customerIdType; // ❌ Incorrect — validation without error code @NotNull private String customerId; ``` **Checklist:** - [ ] All required fields have `@NotNull(message = "VDE01")` - [ ] Enum-like fields have `@Pattern` with `message = "VDE02"` - [ ] Nested objects have `@Valid` to cascade validation - [ ] `ValidationExceptionMapper` is present and registered as `@Provider` ### 2. Header Security **Required headers MUST be validated:** ```java // ✅ Correct — declared as required with description @Parameter(description = "Application ID", required = true) @HeaderParam("appId") String appId, // The MdcLoggingFilter handles null → "UNKNOWN_APP" fallback // But the Resource should still declare them as required ``` **Headers that MUST NOT be logged without masking:** - `authorization` - `cookie` - `x-api-key` - Any custom credentials header **Implementation in `RestClientLoggingFilter`:** ```java private static final Set SENSITIVE_HEADERS = Set.of("authorization", "cookie", "x-api-key"); // Mask: "authorization=[PROTECTED]" ``` ### 3. Log Masking **PII and sensitive data MUST be masked in production logs:** | Data Type | Log Level Allowed | Masking Rule | |---|---|---| | `customerId` | DEBUG only | Never in INFO | | `customerIdType` | DEBUG only | Never in INFO | | `deviceIp` | INFO | OK — used for fraud detection | | `traceId` / `deviceSessionReference` | INFO | OK — correlation ID | | `appId` | INFO | OK — non-sensitive identifier | | Authorization headers | NEVER | `[PROTECTED]` | **Rule:** In `%prod` profile, only `com.banesco` level INFO. Never expose PII at INFO level. ### 4. Trace Integrity **The SecurityTracePort MUST:** - Execute in `finally` block — ALWAYS, even if the main logic throws - Use `AsyncMdcRunner` to preserve MDC context in the async thread - Never block the main response thread - Handle its own exceptions silently (log ERROR, don't propagate) **Checklist:** - [ ] `writeTrace()` called in `finally` with `asyncRunner.run()` - [ ] `traceId` read from `RequestContext.getRequestId()`, not hardcoded - [ ] If `traceId` is null, fallback to `"UNKNOWN"` (not empty string) - [ ] `inputData` and `outputData` serialized with `ObjectMapper` (not `.toString()`) - [ ] Trace failure logged as ERROR but NEVER propagated to caller ### 5. ConfigMap Security **Environment variables for URLs MUST use the pattern:** ```yaml url: ${INTEGRATION__URL:} ``` - Production URLs NEVER hardcoded in `application.yml` - Dev URLs can be hardcoded as defaults for convenience - Credentials NEVER in `application.yml` — use Kubernetes Secrets ### 6. HTTP Security Headers **Response headers to consider:** - `traceId` in response (for client correlation) — ✅ implemented in `MdcLoggingFilter` - `Content-Type: application/json` — ✅ automatic with JAX-RS - `X-Content-Type-Options: nosniff` — add if exposed to browser clients - `Cache-Control: no-store` — banking APIs MUST NOT be cached ### 7. Security Review Output Format ```markdown ## Security Review — ### Input Validation - [ ] All fields validated with error codes - [ ] @Pattern on constrained fields ### Header Security - [ ] Sensitive headers masked in logs - [ ] Required headers declared in Resource ### Log Masking - [ ] No PII at INFO level in prod - [ ] SENSITIVE_HEADERS set includes all credential headers ### Trace Integrity - [ ] SecurityTracePort in finally with AsyncMdcRunner - [ ] TraceId not null/empty ### ConfigMap - [ ] No hardcoded prod URLs - [ ] No credentials in application.yml ### Overall Risk: LOW | MEDIUM | HIGH ```