Move X-BanGUI-Request header name/value to backend/app/utils/constants.py as single source of truth. Add GET /api/v1/config/security-headers endpoint. Update csrf middleware, frontend api client, and docs to use shared constants.
177 lines
7.3 KiB
Markdown
177 lines
7.3 KiB
Markdown
# Security — Guidelines and Implementation
|
|
|
|
Security considerations and implementation details for BanGUI.
|
|
|
|
---
|
|
|
|
## HTTP Security Headers
|
|
|
|
BanGUI implements defense-in-depth against client-side attacks by sending security-related HTTP response headers on all responses.
|
|
|
|
### Headers Implemented
|
|
|
|
| Header | Value | Purpose |
|
|
|---|---|---|
|
|
| `Content-Security-Policy` | `default-src 'self'` | Prevents XSS attacks by restricting script, style, font, image, and other resource origins to `self` only. Browsers refuse to load resources from other origins. |
|
|
| `X-Frame-Options` | `DENY` | Prevents clickjacking attacks by forbidding the page from being embedded in `<iframe>` tags on any origin. |
|
|
| `X-Content-Type-Options` | `nosniff` | Prevents MIME-type sniffing attacks by forcing browsers to respect the declared `Content-Type`. Blocks execution of misidentified scripts. |
|
|
| `X-XSS-Protection` | `1; mode=block` | Enables browser XSS filters (legacy header for older browsers). Modern browsers prioritize CSP. |
|
|
|
|
### Implementation
|
|
|
|
**Backend:** The `SecurityHeadersMiddleware` in `backend/app/main.py` adds these headers to every HTTP response, including error responses and non-API routes.
|
|
|
|
```python
|
|
response.headers["Content-Security-Policy"] = "default-src 'self'"
|
|
response.headers["X-Frame-Options"] = "DENY"
|
|
response.headers["X-Content-Type-Options"] = "nosniff"
|
|
response.headers["X-XSS-Protection"] = "1; mode=block"
|
|
```
|
|
|
|
**Frontend:** The `<meta http-equiv="Content-Security-Policy" content="default-src 'self'" />` tag in `frontend/index.html` provides an additional defense layer in case the backend headers are ever stripped (e.g., by a proxy).
|
|
|
|
### CSP Policy Details
|
|
|
|
The current policy `default-src 'self'` means:
|
|
|
|
- **Allowed:** Inline scripts, stylesheets, fonts, images, and other resources from the same origin (`self`)
|
|
- **Blocked:** Resources from external domains, inline event handlers, `eval()`, and `setTimeout(string)`
|
|
|
|
**Why no `'unsafe-inline'`?**
|
|
- `'unsafe-inline'` defeats CSP's primary purpose (XSS prevention) by allowing arbitrarily-embedded scripts
|
|
- All scripts and styles must be in separate files (never inline), which is best practice anyway
|
|
- The frontend build system (Vite) automatically handles asset bundling and file separation
|
|
|
|
**If external CDN resources are needed:**
|
|
1. Explicitly add the CDN origin to the CSP policy, e.g.: `default-src 'self' https://cdn.example.com`
|
|
2. Document the CDN addition with a justification comment
|
|
3. Ensure the CDN certificate chain is valid and trusted
|
|
4. Consider using Subresource Integrity (SRI) to verify resource authenticity
|
|
|
|
### Verification
|
|
|
|
To verify headers are being sent correctly:
|
|
|
|
1. **Chrome DevTools:**
|
|
- Open DevTools (F12)
|
|
- Go to Network tab
|
|
- Reload the page
|
|
- Click on any request and open the Response Headers section
|
|
- Look for `Content-Security-Policy`, `X-Frame-Options`, `X-Content-Type-Options`, `X-XSS-Protection`
|
|
|
|
2. **Command line (curl):**
|
|
```bash
|
|
curl -I http://localhost:8000/
|
|
curl -I http://localhost:5173/
|
|
```
|
|
|
|
3. **Online tools:**
|
|
- Use [securityheaders.com](https://securityheaders.com) or [csp-evaluator.withgoogle.com](https://csp-evaluator.withgoogle.com)
|
|
|
|
### Future Improvements
|
|
|
|
- **Stricter CSP:** If functionality allows, tighten to `default-src 'none'` and explicitly allow individual resources
|
|
- **SRI (Subresource Integrity):** Add integrity attributes to external script/style tags to prevent tampering
|
|
- **Preload headers:** Use `Link: <...>; rel=preload` to optimize critical resource delivery
|
|
- **HSTS:** Consider adding `Strict-Transport-Security` for production deployments to force HTTPS
|
|
|
|
---
|
|
|
|
## CSRF Protection
|
|
|
|
BanGUI protects cookie-authenticated state-mutating requests (POST, PUT, DELETE, PATCH) with a custom header check. Requests using the session cookie must include the header `X-BanGUI-Request: 1`. Bearer token authentication is exempt since tokens in headers are not CSRF-vulnerable.
|
|
|
|
### Single Source of Truth
|
|
|
|
The header name and value are defined once in `backend/app/utils/constants.py` (`CSRF_HEADER_NAME` and `CSRF_HEADER_VALUE`) and consumed by:
|
|
|
|
- `backend/app/middleware/csrf.py` — validates the header on incoming requests
|
|
- `frontend/src/api/client.ts` — attaches the header to state-mutating fetch calls
|
|
- `frontend/src/utils/constants.ts` — mirrors the values for type-safe import
|
|
|
|
### Endpoint
|
|
|
|
**`GET /api/v1/config/security-headers`** — returns the CSRF header name and value to authenticated clients:
|
|
|
|
```json
|
|
{
|
|
"csrf_header_name": "X-BanGUI-Request",
|
|
"csrf_header_value": "1"
|
|
}
|
|
```
|
|
|
|
This allows the frontend to discover the required header at runtime. Both frontend and backend constants must remain in sync — a build-time check is recommended when updating either constant.
|
|
|
|
### Header Rationale
|
|
|
|
The custom header is required because browsers block cross-site requests from setting custom headers without a CORS preflight, which BanGUI rejects for non-allowed origins.
|
|
|
|
---
|
|
|
|
## Session Security
|
|
|
|
See `backend/app/middleware/csrf.py` and `backend/app/middleware/rate_limit.py` for CSRF protection and rate limiting.
|
|
|
|
---
|
|
|
|
## Password Security
|
|
|
|
- Passwords are hashed with SHA256 on the frontend before transmission
|
|
- The backend never stores plain-text passwords
|
|
- See `backend/app/services/auth.py` for authentication implementation
|
|
- **Common password prevention:** The setup validator rejects a list of ~75 common plaintext passwords that pass structural complexity checks (e.g., `Password1!`). The list is embedded in `backend/app/models/setup.py` and is checked case-insensitively.
|
|
|
|
---
|
|
|
|
## Database Security
|
|
|
|
- The SQLite database contains no sensitive data (no passwords, API keys, or tokens stored)
|
|
- Database queries use parameterized statements to prevent SQL injection
|
|
- See `backend/app/repositories/` for data access patterns
|
|
|
|
---
|
|
|
|
## Regex (ReDoS) Protection
|
|
|
|
BanGUI validates all user-supplied regex patterns before they are compiled or stored.
|
|
|
|
### How It Works
|
|
|
|
1. **Static analysis** via [regexploit](https://github.com/doyensec/regexploit) detects catastrophic backtracking patterns before compilation
|
|
2. **Timeout enforcement** stops compilation if it exceeds 2 seconds (prevents hanging on pathological patterns)
|
|
3. **Length limit** (1000 characters) prevents memory exhaustion via bloated patterns
|
|
|
|
### Protected Endpoints
|
|
|
|
All endpoints that accept regex patterns validate them:
|
|
- Filter configuration (`prefregex`, `failregex`, `ignorregex`)
|
|
- Action configuration (any regex used in actions)
|
|
- Direct config editing
|
|
|
|
### ReDoS Pattern Examples
|
|
|
|
Patterns with nested quantifiers on overlapping text are blocked:
|
|
|
|
| Pattern | Why Blocked |
|
|
|---------|-------------|
|
|
| `(a+)+b` | Plus inside plus — exponential backtracking |
|
|
| `([a-z]+)*d` | Quantifier inside quantifier |
|
|
| `(x+)+y` | Nested quantifiers |
|
|
| `a[bcd]*e[bcd]*e` | Multiple unbounded quantifiers |
|
|
|
|
### Legitimate Complex Patterns
|
|
|
|
Not all complex patterns are blocked. Email and IP validation patterns typically pass:
|
|
|
|
```python
|
|
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" # OK
|
|
r"^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" # OK
|
|
```
|
|
|
|
### If Your Pattern Is Rejected
|
|
|
|
1. Rewrite to avoid nested quantifiers on the same text
|
|
2. Use atomic groups or possessive quantifiers: `(?>a+)+b` instead of `(a+)+b`
|
|
3. Test locally with Python's `re` module before deploying
|
|
4. If you believe the pattern is safe, check with [regexploit](https://github.com/doyensec/regexploit) directly
|