Runtime Network Security Audit¶
Scope
This report documents the live runtime security assessment methodology and expected results for the Cosmian KMS server. The analysis targets the running server process over the network — complementing static source analysis and multi-framework compliance reports.
Run the analyser with:
Assessment Architecture¶
graph TD
A([Security Analyst]) -->|"runtime_security.sh"| B[Runtime Analyser]
B --> C[Reachability Probe]
B --> D[TLS Inspector]
B --> E[Certificate Chain]
B --> F[HTTP Headers]
B --> G[mTLS Verifier]
B --> H[KMIP Protocol Probes]
B --> I[Optional: nmap / sslyze / nuclei]
C --> J[(cbom/runtime/)]
D --> J
E --> J
F --> J
G --> J
H --> J
I --> J
J --> K[runtime_results.json]
J --> L[tls_analysis.txt]
J --> M[certificate.pem]
J --> N[http_headers.txt]
J --> O[kmip_probes.json]
Network & Attack Surface Map¶
graph LR
subgraph Internet ["Public Internet / Zero-trust network"]
C1([CLI client])
C2([Web UI])
C3([Enterprise app])
A([Attacker])
end
subgraph DMZ ["DMZ / Load Balancer"]
LB["TLS Termination or passthrough"]
end
subgraph KMS ["KMS Server Process"]
direction TB
P9998["Port 9998 — HTTPS/KMIP\n(main)"]
AUTH["Auth middleware\n(JWT / mTLS / API-key)"]
KMIP_ROUTE["KMIP 2.1 routes"]
UI_ROUTE["Web UI routes\n/ui/"]
HEALTH["Health — /version"]
end
subgraph DB ["Persistent Storage"]
SQL[(SQLite / PostgreSQL\nRedis-findex)]
end
C1 -- "HTTPS + mTLS / JWT" --> LB
C2 -- "HTTPS + auth cookie" --> LB
C3 -- "HTTPS + API key" --> LB
A -. "scan / probe" .-> LB
LB --> P9998
P9998 --> AUTH
AUTH --> KMIP_ROUTE
AUTH --> UI_ROUTE
AUTH --> HEALTH
KMIP_ROUTE --> SQL
Key attack surfaces
| Surface | Exposure | Mitigation |
|---|---|---|
| Port 9998 / KMIP endpoint | External | mTLS or JWT, TLS 1.2+ only |
| Web UI | External | Cookie auth, CSP header |
/version health endpoint |
External | Read-only, no secrets |
| Server certificate | Public | Auto-renew, SHA-256+, RSA-2048+ |
| Database | Internal only | Not exposed to network |
TLS Security Scorecard¶
graph LR
S3("SSLv3") -- REJECT --> N1["POODLE — CVE-2014-3566"]
T10("TLS 1.0") -- REJECT --> N2["BEAST / PCI-DSS deprecated"]
T11("TLS 1.1") -- REJECT --> N3["Deprecated — RFC 8996"]
T12("TLS 1.2") -- ACCEPT --> Y1["FIPS 140-3 minimum"]
T13("TLS 1.3") -- ACCEPT --> Y2["Preferred — PFS enforced"]
style S3 fill:#ef4444,color:#fff,stroke:#dc2626
style T10 fill:#ef4444,color:#fff,stroke:#dc2626
style T11 fill:#f97316,color:#fff,stroke:#ea580c
style T12 fill:#22c55e,color:#fff,stroke:#16a34a
style T13 fill:#16a34a,color:#fff,stroke:#15803d
style N1 fill:#fee2e2,color:#991b1b,stroke:#fca5a5
style N2 fill:#fee2e2,color:#991b1b,stroke:#fca5a5
style N3 fill:#ffedd5,color:#9a3412,stroke:#fdba74
style Y1 fill:#dcfce7,color:#166534,stroke:#86efac
style Y2 fill:#dcfce7,color:#166534,stroke:#86efac
| Protocol | Expected | Reason |
|---|---|---|
| SSLv3 | ❌ Rejected | POODLE attack (CVE-2014-3566) |
| TLS 1.0 | ❌ Rejected | BEAST, POODLE, deprecated PCI-DSS 3.2 |
| TLS 1.1 | ❌ Rejected | Deprecated per RFC 8996 |
| TLS 1.2 | ✅ Accepted | Minimum for FIPS 140-3 |
| TLS 1.3 | ✅ Accepted | Preferred — mandatory for new deployments |
pie title Accepted Cipher Suites by Category
"ECDHE-AESGCM (strong)" : 4
"DHE-AESGCM (PFS)" : 2
"AES256-SHA256 (compat)" : 1
"Weak / rejected" : 0
| Category | Example Cipher | Status | FIPS 140-3 | Forward Secrecy |
|---|---|---|---|---|
| ECDHE-RSA-AES256-GCM-SHA384 | TLS 1.2 ECDHE | ✅ Allowed | ✅ | ✅ |
| ECDHE-RSA-AES128-GCM-SHA256 | TLS 1.2 ECDHE | ✅ Allowed | ✅ | ✅ |
| TLS_AES_256_GCM_SHA384 | TLS 1.3 | ✅ Allowed | ✅ | ✅ |
| TLS_CHACHA20_POLY1305_SHA256 | TLS 1.3 | ✅ Allowed | ⚠️ non-FIPS only | ✅ |
| NULL / aNULL | Export | ❌ Rejected | ❌ | ❌ |
| RC4 / DES / 3DES | Legacy | ❌ Rejected | ❌ | ❌ |
| MD5-based | Legacy | ❌ Rejected | ❌ | ❌ |
| EXPORT-grade | Legacy | ❌ Rejected | ❌ | ❌ |
sequenceDiagram
participant C as Client
participant S as KMS Server (TLS 1.3)
C->>S: ClientHello (supported protocols, ciphers)
S-->>C: ServerHello (TLS 1.3, TLS_AES_256_GCM_SHA384)
S-->>C: Certificate (RSA-2048 / ECDSA-256, SHA-256 signed)
S-->>C: CertificateVerify
S-->>C: Finished
C->>S: [Optional] Certificate (mTLS)
C->>S: Finished
Note over C,S: Symmetric keys derived from ephemeral ECDHE
(Perfect Forward Secrecy)
C->>S: POST /kmip/2_1 (encrypted)
S-->>C: KMIP ResponseMessage (encrypted)
Certificate Chain Analysis¶
graph TB
ROOT["Root CA\nself-signed or public CA\nKey: RSA-4096 or EC-384\nSig: SHA-256"]
INTER["Intermediate CA optional\nKey: RSA-2048 or EC-256\nSig: SHA-256 Valid: 3 years"]
LEAF["KMS Server Certificate\nSAN: kms.example.com\nKey: RSA-2048 or EC-256\nSig: SHA-256 Valid: 1 year max"]
ROOT --> INTER
INTER --> LEAF
Certificate requirements
- Key algorithm: RSA ≥ 2048 bits or EC ≥ P-256
- Signature: SHA-256 minimum (SHA-1 rejected by modern browsers and RFC 9155)
- SAN: must match server hostname — bare CN no longer sufficient (RFC 2818)
- Expiry: warning at 30 days; auto-renewal recommended (ACME/Let’s Encrypt)
- OCSP stapling: recommended for client-side revocation checking
HTTP Security Headers¶
graph LR
subgraph Required ["Required Headers"]
HSTS["Strict-Transport-Security\nmax-age=31536000 includeSubDomains"]
XCTO["X-Content-Type-Options: nosniff"]
end
subgraph Recommended ["Recommended Headers"]
XFO["X-Frame-Options: DENY"]
CSP["Content-Security-Policy\ndefault-src self"]
CC["Cache-Control: no-store"]
end
subgraph Avoid ["Must not disclose"]
SRV["Server: omit or generic"]
CORS_W["CORS wildcard forbidden on KMIP"]
end
| Header | Expected Value | Importance | OWASP |
|---|---|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
Required | A05 |
X-Content-Type-Options |
nosniff |
Required | A05 |
X-Frame-Options |
DENY |
Recommended | A05 |
Content-Security-Policy |
default-src 'self'; script-src 'self' |
Recommended | A03 |
Cache-Control |
no-store on API routes |
Recommended | A02 |
Server |
Empty or generic | Avoid disclosure | A05 |
CORS on /kmip/* |
None or restricted origin | Required | A01 |
mTLS Authentication Model¶
sequenceDiagram
participant CLI as ckms CLI
participant KMS as KMS Server
participant DB as Database
CLI->>KMS: TLS ClientHello
KMS-->>CLI: ServerHello + Certificate
KMS-->>CLI: CertificateRequest (if mTLS mode)
CLI->>KMS: Certificate (client cert, signed by trusted CA)
CLI->>KMS: CertificateVerify
Note over CLI,KMS: TLS session established
CLI->>KMS: POST /kmip/2_1 (encrypted TTLV)
KMS->>KMS: Extract CN from client cert → username
KMS->>DB: Look up access control for user
KMS-->>CLI: KMIP Response
| Mode | How it works | When to use |
|---|---|---|
| mTLS | Client presents X.509 certificate signed by trusted CA | Internal services, CLI tooling |
| JWT (OAuth2) | Bearer token from Auth0 / Keycloak / OIDC provider | Web UI, end-user access |
| API key | Shared secret in header | Machine-to-machine, simple integrations |
| No auth | Disabled — dev/test only (--auth-type none) |
Local development only |
Warning
Never deploy with --auth-type none in production.
The KMS must enforce at least one authentication method on all KMIP routes.
KMIP Protocol Security Probes¶
flowchart TD
P1["Empty payload probe\nPOST /kmip/2_1 {}"] -->|"Expected: 400/422"| OK1([PASS])
P2["Oversized BatchCount\nBatchCount: 99999"] -->|"Expected: 400/422/413"| OK2([PASS])
P3["SQL injection in UID\n'OR 1=1; DROP TABLE"] -->|"Expected: 400/422/401"| OK3([PASS])
P4["70 MiB payload\nAbove 64 MiB limit"] -->|"Expected: 400/413"| OK4([PASS])
P5["Rate limit probe\n10 rapid requests"] -->|"429 expected for excess"| OK5([PASS / INFO])
style OK1 fill:#22c55e,color:#fff
style OK2 fill:#22c55e,color:#fff
style OK3 fill:#22c55e,color:#fff
style OK4 fill:#22c55e,color:#fff
style OK5 fill:#84cc16,color:#fff
| Probe | Payload | Expected HTTP | Risk if wrong |
|---|---|---|---|
| Empty KMIP request | {} |
400 or 422 | Server crash / 500 |
| OversizedBatchCount | BatchCount: 99999 |
400, 422, or 413 | DoS / OOM |
| SQL injection in UID | ' OR '1'='1'; DROP ... |
400, 422, 401 | SQL injection |
| 70 MiB payload | Random bytes | 400 or 413 | DoS / memory exhaustion |
| Rapid 10 requests | Empty KMIP | 422 (or 429 if rate-limit active) | Brute-force |
Threat Model (STRIDE)¶
mindmap
root((KMS Server\nAttack Surface))
Spoofing
Fake client certificate
JWT token forgery
MITM on HTTP
Tampering
Replay KMIP request
Key ID enumeration
Packet injection
Repudiation
Missing audit log
Log injection
Information Disclosure
TLS version downgrade
Server header leaks version
Error message leaks DB schema
Denial of Service
OversizedBatchCount
Large payload flood
TLS session exhaustion
Elevation of Privilege
CORS wildcard on KMIP
JWT scope confusion
Insecure object ownership
| Threat | STRIDE | Mitigation | Status |
|---|---|---|---|
| MITM — weak TLS version | Tampering | TLS 1.2+ enforced; SSLv3/TLS1.0/1.1 rejected | ✅ Mitigated |
| Weak cipher negotiation | Tampering | NULL/RC4/DES/EXPORT rejected by server | ✅ Mitigated |
| Certificate spoofing | Spoofing | mTLS or JWT required; CA pinning optional | ✅ Mitigated |
| SQL injection via UID | Tampering | Parameterised queries in all DB backends | ✅ Mitigated |
| OOM via large batch | DoS | BatchCount validated; payload size limit 64 MiB | ✅ Mitigated |
| Rate-based brute-force | DoS | Rate limiting middleware (configurable) | ⚠️ Configurable |
| Server version disclosure | Info Disclosure | Server header suppressed |
✅ Mitigated |
| CORS wildcard on KMIP | Elevation of Privilege | No CORS header on /kmip/* |
✅ Mitigated |
| Expired certificate | Spoofing | 30-day expiry warning in checker | ✅ Monitored |
| Insecure direct object refs | Elevation of Privilege | Object ownership enforced in DB | ✅ Mitigated |
Running the Analyser¶
Prerequisites¶
# Required (always present on Linux)
openssl version # ≥ 3.0
curl --version # ≥ 7.68
# Optional — enable richer analysis when installed
apt-get install nmap # port scan + TLS NSE scripts
pip3 install sslyze # deep TLS / cert-transparency analysis
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest # template scanner
Basic run (plain HTTPS)¶
bash .github/scripts/audit/runtime_security.sh \
--server-url https://localhost:9998 \
--insecure # skip cert verification on self-signed cert
Full run with mTLS¶
bash .github/scripts/audit/runtime_security.sh \
--server-url https://kms.prod.example.com:9998 \
--cert certs/client.crt \
--key certs/client.key \
--ca certs/ca.crt \
--report documentation/docs/certifications_and_compliance/audit/runtime_security_audit_latest.md
Output files¶
cbom/runtime/
├── runtime_results.json ← machine-readable summary (all checks + status)
├── tls_analysis.txt ← raw openssl s_client output
├── cert_details.txt ← openssl x509 -text of server certificate
├── certificate.pem ← server certificate in PEM format
├── http_headers.txt ← HTTP response headers
├── mtls_analysis.txt ← mTLS negotiation log
├── kmip_probes.json ← KMIP protocol probe results
├── nmap.txt ← nmap scan (if installed)
├── sslyze.json ← sslyze report (if installed)
└── nuclei.txt ← nuclei scan (if installed)
Exit codes¶
| Code | Meaning |
|---|---|
0 |
All checks passed |
1 |
One or more FAIL findings (critical) |
2 |
Tool error (missing required utility or bad arguments) |
Integration with CI¶
Add to .github/workflows/main_base.yml as a post-deploy smoke test:
- name: Runtime Security Scan
run: |
bash .github/scripts/audit/runtime_security.sh \
--server-url https://localhost:9998 \
--insecure \
--report cbom/runtime_security_report.md
env:
KMS_URL: https://localhost:9998
Relation to other security reports
| Report | Layer | Tool |
|---|---|---|
| OWASP Source Audit | Static — source code | scan_source.py + risk_score.py |
| Multi-framework Audit | Static — policy compliance | multi_framework.sh |
| Runtime Security Audit (this file) | Dynamic — running server | runtime_security.sh |