docs: security profile and audit checklist
This commit is contained in:
@@ -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
|
||||||
Reference in New Issue
Block a user