111 lines
3.7 KiB
Markdown
111 lines
3.7 KiB
Markdown
# 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
|