docs: security profile and audit checklist

This commit is contained in:
2026-05-31 12:46:14 +00:00
parent 098ffee3b4
commit 6325237a3b
+110
View File
@@ -0,0 +1,110 @@
# Security Profile
## Audit Status
**Grade: A+ — 30/30 checks — Last audit: 2026-05-31**
## Authentication Stack
| Layer | Implementation |
|---|---|
| Transport | Bearer token in `Authorization` header |
| Token format | HMAC-SHA256 JWT, 1-hour expiry |
| Signing secret | 64-char hex (256-bit), stored in `wp_options` as `asktam_mcp_jwt_secret` |
| JWT claims | `key_id`, `iss` (site URL), `exp`, `jti` (revocable) |
| Comparison | `hash_equals()` — timing-safe |
| Key storage | bcrypt hash in `mwp_asktam_mcp_keys.key_hash` |
| Revocation | JTI revocation list in `wp_options` |
| Secret rotation | `TokenManager::rotateSecret()` — invalidates all existing tokens |
## Scope Enforcement
Every tool call validates the required scope against the key's granted scopes:
```
read → read-only operations
write → create / update / delete
admin → plugin mgmt, CLI, raw SQL, options
elementor → Elementor builder tools
woo → WooCommerce tools
```
## Rate Limiting
- Default: 60 requests/minute per API key
- Stored via WP transients (`asktam_mcp_rate_{key_id}`)
- Configurable in WP Admin → AskTam MCP → Settings
- Returns HTTP 429 on exceed
## HTTPS Enforcement
When enabled (default: on), requests over HTTP are rejected with 403.
Configurable in Settings tab.
## Input Sanitization
| Attack vector | Protection |
|---|---|
| SQL injection | `wpdb->prepare()` everywhere, `(int)` casts on IDs |
| XSS | 47 `esc_*()` calls in AdminMenu |
| CSRF | `wp_verify_nonce()` on all 3 admin form actions |
| Path traversal | `realpath()` + uploads/ sandbox in FileManager |
| Shell injection | Allowlisted commands + `escapeshellarg()` in CLI |
| Raw SQL abuse | SELECT-only enforcement + disabled by default |
## Audit Logging
Every tool call is logged to `mwp_asktam_mcp_log`:
- `key_id` (not raw token)
- `tool` name
- `ip` address
- `status` (success/error)
- `duration_ms`
- `created_at`
Viewable in WP Admin → AskTam MCP → Audit Log (last 1000 entries).
## Raw SQL
Disabled by default. When enabled, only `SELECT` statements are allowed. All DML (`INSERT`, `UPDATE`, `DELETE`, `DROP`, `TRUNCATE`, etc.) is blocked via pattern matching.
## File Manager Sandbox
Restricted to `wp_upload_dir()['basedir']` via `realpath()` verification. Any attempt to traverse outside uploads/ returns an error.
## CLI Allowlist
Only explicitly allowed WP-CLI commands are executable. All arguments are passed through `escapeshellarg()`. The `--allow-root` flag is always appended (required since container runs as root).
## 30-Check Audit Points
1. Bearer token check present in request flow
2. Bearer token extraction from Authorization header
3. HTTPS enforcement code path in Auth::validate()
4. HTTPS enforcement enabled in settings
5. Rate limiting via transients
6. Scope enforcement per request
7. `hash_equals()` for timing-safe HMAC comparison
8. HMAC-SHA256 algorithm used
9. JWT `exp` claim validated
10. JWT `iss` claim validated against site URL
11. JTI revocation list implemented
12. JWT secret rotation supported
13. JWT secret is 64+ chars
14. CSRF: `wp_verify_nonce` on admin form actions (3+ checks)
15. XSS: 20+ `esc_*()` calls in AdminMenu
16. `wpdb->prepare()` in TokenManager queries
17. `(int)` casts on post_id in PostTools
18. SELECT-only enforcement in RawSqlTools
19. Raw SQL disabled by default
20. FileManager sandboxed to uploads dir
21. CLI allowlist present
22. CLI injection blocking present
23. `escapeshellarg()` in CLI
24. `--allow-root` flag in CLI (container runs as root)
25. Rate limit configured (> 0, ≤ 600)
26. Request logging enabled
27. Audit log table exists
28. Log table stores key_id only (not raw tokens)
29. At least one active API key exists
30. Admin-scoped keys are labeled