153 lines
4.7 KiB
Markdown

---
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<String> 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_<SERVICE>_URL:<default-dev-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 — <API Name>
### 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
```