543 lines
22 KiB
Markdown
543 lines
22 KiB
Markdown
# Security Audit Report — app.closebot.com
|
||
|
||
**Date:** February 7, 2026
|
||
**Auditor:** Clawdbot Security Scanner
|
||
**Scope:** app.closebot.com, api.closebot.com, closebot.com
|
||
**Authorization:** Fully authorized pentest by site owner (Jake Shore)
|
||
|
||
---
|
||
|
||
## 1. Executive Summary
|
||
|
||
### Severity Matrix
|
||
|
||
| Severity | Count | Findings |
|
||
|----------|-------|----------|
|
||
| 🔴 **CRITICAL** | 1 | API CORS wildcard `Access-Control-Allow-Origin: *` on authenticated endpoints |
|
||
| 🟠 **HIGH** | 3 | Missing security headers on API, Azure infrastructure info leak, No rate limiting on API |
|
||
| 🟡 **MEDIUM** | 4 | WordPress readme/license exposed, NEXT_LOCALE cookie lacks Secure flag, API Azure AppId leak, /bot endpoint method disclosure |
|
||
| 🔵 **LOW** | 3 | WordPress version exposed, Clerk auth status header leak, Build ID exposed in 404 pages |
|
||
| ℹ️ **INFO** | 6 | Tech stack details, positive findings |
|
||
|
||
### Overall Risk Rating: **HIGH**
|
||
|
||
The most critical finding is that **api.closebot.com has `Access-Control-Allow-Origin: *` on ALL endpoints**, including authenticated ones like `/bot`, `/bot/list`, `/bot/create`, `/bot/config`, `/bot/settings`, `/lead`, `/agency`. This is the same class of vulnerability found in SuperFunnels. While `Access-Control-Allow-Credentials` is NOT set (which prevents cookie-based CSRF), any token-based auth (Bearer tokens) sent from JS is still vulnerable to being used in cross-origin attacks if tokens are predictable or leaked.
|
||
|
||
---
|
||
|
||
## 2. Tech Stack
|
||
|
||
| Component | Technology | Details |
|
||
|-----------|-----------|---------|
|
||
| **Frontend (app)** | Next.js (App Router) | Build ID: `SJe3XKo4eqvdUfxjtlLi9`, deployment: `dpl_9XCbPqSSm7KFrRoZoDtTN7Cu7QhM` |
|
||
| **Frontend Hosting** | Vercel | IAD1 region (us-east-1), Vercel Security Checkpoint enabled |
|
||
| **Auth Provider** | Clerk | Headers: `x-clerk-auth-status`, `x-clerk-auth-reason` |
|
||
| **API Backend** | ASP.NET (Kestrel) | Running on Azure App Service |
|
||
| **API Hosting** | Azure App Service | `cb-api-zarqcgo3sph6q.azurewebsites.net` (West US 2) |
|
||
| **Marketing Site** | WordPress 6.9.1 | Hosted on Kinsta, behind Cloudflare |
|
||
| **CDN (marketing)** | Cloudflare | CF-Ray headers present |
|
||
| **DNS** | GoDaddy | `ns77.domaincontrol.com`, `ns78.domaincontrol.com` |
|
||
| **SSL** | Let's Encrypt (R13) | Valid Feb 5 – May 6, 2026 |
|
||
| **i18n** | Multi-language | EN, ES, PT, NL supported |
|
||
|
||
### Subdomain Map
|
||
|
||
| Subdomain | Status | Resolves To |
|
||
|-----------|--------|-------------|
|
||
| `app.closebot.com` | ✅ Active | Vercel (`592229a97db57f91.vercel-dns-017.com`) |
|
||
| `api.closebot.com` | ✅ Active | Azure (`cb-api-zarqcgo3sph6q.azurewebsites.net` → `20.115.232.12`) |
|
||
| `www.closebot.com` | ✅ Redirect | → `closebot.com` (Cloudflare `162.159.134.42`) |
|
||
| `closebot.com` | ✅ Active | Cloudflare/Kinsta WordPress |
|
||
| `admin.closebot.com` | ❌ No DNS | - |
|
||
| `dashboard.closebot.com` | ❌ No DNS | - |
|
||
| `ws.closebot.com` | ❌ No DNS | - |
|
||
| `mail.closebot.com` | ❌ No DNS | - |
|
||
| `staging.closebot.com` | ❌ No DNS | - |
|
||
| `dev.closebot.com` | ❌ No DNS | - |
|
||
|
||
### DNS Records
|
||
- **TXT:** Google site verification (`A_X4u_WKhr2Mp6maxNXC71DUBr-RlWzxCjrvyxnVrZo`), Microsoft verification (`MS=ms24389368`)
|
||
- **MX:** None configured (no email hosting on root domain)
|
||
- **NS:** GoDaddy (`ns77/ns78.domaincontrol.com`)
|
||
|
||
---
|
||
|
||
## 3. HTTP Security Headers
|
||
|
||
### app.closebot.com (Vercel/Next.js)
|
||
|
||
| Header | Status | Value |
|
||
|--------|--------|-------|
|
||
| `Strict-Transport-Security` | ✅ Present | `max-age=63072000` (~2 years) |
|
||
| `Content-Security-Policy` | ❌ **MISSING** | Not set |
|
||
| `X-Frame-Options` | ❌ **MISSING** | Not set |
|
||
| `X-Content-Type-Options` | ❌ **MISSING** | Not set |
|
||
| `X-XSS-Protection` | ❌ **MISSING** | Not set (deprecated, but still recommended) |
|
||
| `Referrer-Policy` | ❌ **MISSING** | Not set |
|
||
| `Permissions-Policy` | ❌ **MISSING** | Not set |
|
||
| `X-Powered-By` | ⚠️ Leaking | `Next.js` |
|
||
| `Server` | ⚠️ Leaking | `Vercel` |
|
||
|
||
**Severity: HIGH** — Missing CSP, X-Frame-Options, and X-Content-Type-Options on the frontend.
|
||
|
||
### api.closebot.com (Azure/Kestrel)
|
||
|
||
| Header | Status | Value |
|
||
|--------|--------|-------|
|
||
| `Strict-Transport-Security` | ❌ **MISSING** | Not set |
|
||
| `Content-Security-Policy` | ❌ **MISSING** | Not set |
|
||
| `X-Frame-Options` | ❌ **MISSING** | Not set |
|
||
| `X-Content-Type-Options` | ❌ **MISSING** | Not set |
|
||
| `X-XSS-Protection` | ❌ **MISSING** | Not set |
|
||
| `Referrer-Policy` | ❌ **MISSING** | Not set |
|
||
| `Permissions-Policy` | ❌ **MISSING** | Not set |
|
||
| `Server` | ⚠️ Leaking | `Kestrel` |
|
||
| `Request-Context` | ⚠️ Leaking | `appId=cid-v1:1593e7a7-a705-4f69-977e-49c3fa1d0aa3` |
|
||
| `x-ms-middleware-request-id` | ⚠️ Leaking | `00000000-0000-0000-0000-000000000000` |
|
||
|
||
**Severity: HIGH** — API has ZERO security headers. No HSTS, no CSP, nothing.
|
||
|
||
### closebot.com (WordPress/Cloudflare)
|
||
|
||
| Header | Status | Value |
|
||
|--------|--------|-------|
|
||
| `X-Content-Type-Options` | ✅ Present | `nosniff` |
|
||
| `Strict-Transport-Security` | ❌ **MISSING** | Not set |
|
||
| `Content-Security-Policy` | ❌ **MISSING** | Not set |
|
||
| `X-Frame-Options` | ❌ **MISSING** | Not set |
|
||
| `Referrer-Policy` | ❌ **MISSING** | Not set |
|
||
| `Permissions-Policy` | ❌ **MISSING** | Not set |
|
||
|
||
---
|
||
|
||
## 4. CORS Analysis — 🔴 CRITICAL
|
||
|
||
### api.closebot.com — WILDCARD CORS ON ALL ENDPOINTS
|
||
|
||
This is the **#1 finding** of the entire audit.
|
||
|
||
| Endpoint | Origin Tested | ACAO Header | Result |
|
||
|----------|--------------|-------------|--------|
|
||
| `/` (root) | `https://evil.com` | `*` | 🔴 **OPEN** |
|
||
| `/` (root) | `null` | `*` | 🔴 **OPEN** |
|
||
| `/` (root) | `https://evil.closebot.com` | `*` | 🔴 **OPEN** |
|
||
| `/bot` | `https://evil.com` | `*` | 🔴 **OPEN** |
|
||
| `/bot` (OPTIONS) | `https://evil.com` | `*` | 🔴 **OPEN** |
|
||
| Preflight | Any | Allows `Authorization,Content-Type` | 🔴 **OPEN** |
|
||
|
||
**Key facts:**
|
||
- `Access-Control-Allow-Origin: *` is returned for ANY origin
|
||
- Preflight `OPTIONS` returns `Access-Control-Allow-Headers: Authorization,Content-Type` — meaning cross-origin JS can send auth headers
|
||
- `Access-Control-Allow-Credentials` is NOT set (mitigating cookie-based attacks)
|
||
- However, if the API uses Bearer tokens (JWT) passed via `Authorization` header, any malicious website can make authenticated API calls if it obtains the token
|
||
|
||
**Impact:**
|
||
- If a user visits a malicious site while logged into Closebot, and the malicious site can obtain/guess the auth token, it can make full API calls to read/modify bot data, leads, agency settings
|
||
- The preflight allows `Authorization` header, so even "complex" requests are permitted cross-origin
|
||
|
||
**Recommendation:** Replace `*` with an explicit allowlist: `https://app.closebot.com`, `https://closebot.com`
|
||
|
||
### app.closebot.com — No CORS headers on most responses
|
||
|
||
The frontend (Vercel/Next.js) does NOT return CORS headers on normal pages. The 404 page returns `Access-Control-Allow-Origin: *` but this is a static error page and lower risk.
|
||
|
||
---
|
||
|
||
## 5. Cookie/Session Analysis
|
||
|
||
### app.closebot.com Cookies
|
||
|
||
| Cookie | Secure | HttpOnly | SameSite | Domain | Notes |
|
||
|--------|--------|----------|----------|--------|-------|
|
||
| `NEXT_LOCALE` | ❌ No | ❌ No | `Lax` | `/` path | ⚠️ Language preference — missing Secure flag |
|
||
| Clerk session cookies | Not directly observed (behind Vercel checkpoint) | — | — | — | Clerk typically handles this well |
|
||
|
||
**Severity: MEDIUM** — `NEXT_LOCALE` cookie is set without `Secure` flag, allowing it to be sent over HTTP if user visits `http://` URL. While HSTS mitigates this on the app domain, it's still a best practice violation.
|
||
|
||
### api.closebot.com Cookies
|
||
|
||
| Cookie | Secure | HttpOnly | SameSite | Domain | Notes |
|
||
|--------|--------|----------|----------|--------|-------|
|
||
| `TiPMix` | ✅ Yes | ✅ Yes | `None` | `.api.closebot.com` | Azure routing cookie |
|
||
| `x-ms-routing-name` | ✅ Yes | ✅ Yes | `None` | `.api.closebot.com` | Azure routing — value: `self` |
|
||
|
||
**Note:** `SameSite=None` on Azure routing cookies means they're sent cross-site. Combined with the CORS `*`, this is concerning — though these are infrastructure cookies, not auth tokens.
|
||
|
||
### closebot.com Cookies
|
||
|
||
| Cookie | Secure | HttpOnly | SameSite | Domain |
|
||
|--------|--------|----------|----------|--------|
|
||
| `__cf_bm` | ✅ Yes | ✅ Yes | `None` | `.closebot.com` |
|
||
|
||
Cloudflare bot management cookie — properly secured.
|
||
|
||
### Auth Mechanism
|
||
- **Clerk** is used for authentication (confirmed by `x-clerk-auth-status` and `x-clerk-auth-reason` headers)
|
||
- Unauthenticated requests get `x-clerk-auth-status: signed-out` and `x-clerk-auth-reason: session-token-and-uat-missing`
|
||
- Root `/` redirects to `/login?redirect_url=%2F` (307 redirect)
|
||
- `/register` exists and returns 200 — **open registration is available**
|
||
|
||
---
|
||
|
||
## 6. API/Endpoint Map
|
||
|
||
### app.closebot.com (Vercel/Next.js)
|
||
|
||
| Path | Status | Notes |
|
||
|------|--------|-------|
|
||
| `/` | 307 | Redirects to `/login` |
|
||
| `/login` | 200 | Login page (Clerk) |
|
||
| `/register` | 200 | Registration page (open signup) |
|
||
| `/signup` | 404 | Not used |
|
||
| `/pricing` | 403* | Protected (behind Vercel checkpoint after rate limit) |
|
||
| `/settings` | 403* | Protected |
|
||
| `/billing` | 403* | Protected |
|
||
| `/account` | 403* | Protected |
|
||
| `/bots` | 403* | Protected |
|
||
| `/conversations` | 403* | Protected |
|
||
| `/analytics` | 403* | Protected |
|
||
| `/docs` | 403* | Protected |
|
||
| `/.env` | 404 | ✅ Not exposed |
|
||
| `/.git` | 404 | ✅ Not exposed |
|
||
| `/robots.txt` | 404 | No robots.txt |
|
||
| `/sitemap.xml` | 404 | No sitemap |
|
||
| `/.well-known/security.txt` | 404 | No security.txt |
|
||
|
||
*403 responses are from Vercel Security Checkpoint (bot challenge), not actual authorization blocks.
|
||
|
||
### api.closebot.com (Azure/Kestrel)
|
||
|
||
| Path | Status | Methods | Notes |
|
||
|------|--------|---------|-------|
|
||
| `/bot` | 401/405 | GET, POST | **Core bot endpoint** — requires auth |
|
||
| `/bot/list` | 401 | — | List bots |
|
||
| `/bot/create` | 401 | — | Create bot |
|
||
| `/bot/all` | 401 | — | All bots |
|
||
| `/bot/settings` | 401 | — | Bot settings |
|
||
| `/bot/config` | 401 | — | Bot config |
|
||
| `/lead` | 401 | — | Lead management |
|
||
| `/agency` | 401 | — | Agency management |
|
||
| `/swagger` | 301 | — | Swagger redirect (leads to 404 — likely disabled in prod) |
|
||
| `/` | 404 | — | No root handler |
|
||
| All other paths | 404 | — | — |
|
||
|
||
**Severity: MEDIUM** — The `/bot` endpoint returns `Allow: GET, POST` in the 405 response, revealing accepted HTTP methods. The `/swagger` 301 redirect suggests Swagger UI was enabled at some point.
|
||
|
||
### closebot.com (WordPress)
|
||
|
||
| Path | Status | Notes |
|
||
|------|--------|-------|
|
||
| `/wp-admin` | 301 | Redirects to login — **exposed** |
|
||
| `/wp-login.php` | 200 | **Login page accessible** |
|
||
| `/wp-json` | 200 | REST API root — **accessible** |
|
||
| `/wp-json/wp/v2/users` | 401 | User enumeration blocked ✅ |
|
||
| `/.env` | 403 | Blocked ✅ |
|
||
| `/xmlrpc.php` | 403 | Blocked ✅ |
|
||
| `/wp-config.php` | 403 | Blocked ✅ |
|
||
| `/readme.html` | 200 | ⚠️ **Exposed** — WordPress readme |
|
||
| `/license.txt` | 200 | ⚠️ **Exposed** — WordPress license |
|
||
| `/feed/` | 200 | ⚠️ Exposes WordPress version `6.9.1` |
|
||
|
||
---
|
||
|
||
## 7. Client-Side Analysis
|
||
|
||
### Findings from HTML Source
|
||
|
||
1. **Vercel Security Checkpoint** — app.closebot.com serves a JS-based browser verification challenge (Astro framework) before allowing access. This is an effective bot protection layer.
|
||
|
||
2. **Next.js Build ID exposed**: `SJe3XKo4eqvdUfxjtlLi9` — visible in error pages and static chunk URLs. Low risk but provides deployment fingerprinting.
|
||
|
||
3. **Deployment ID exposed**: `dpl_9XCbPqSSm7KFrRoZoDtTN7Cu7QhM` — in chunk URLs.
|
||
|
||
4. **Internationalization**: Site supports EN, ES, PT, NL via `x-matched-path: /[locale]/login` routing.
|
||
|
||
5. **Clerk Auth Headers Leaked in Every Response**:
|
||
- `x-clerk-auth-status: signed-out`
|
||
- `x-clerk-auth-reason: session-token-and-uat-missing`
|
||
|
||
These headers confirm the auth provider and current auth state to any observer.
|
||
|
||
### Source Maps
|
||
- All `_next/static` resources are behind Vercel Security Checkpoint (return 403 challenge page to curl)
|
||
- Could not confirm/deny source map availability without browser access
|
||
- **Recommendation:** Ensure `.map` files are not deployed to production
|
||
|
||
### JS Bundles Analysis
|
||
- Could not analyze JS bundles directly due to Vercel Security Checkpoint blocking curl requests
|
||
- The checkpoint uses a Web Worker-based challenge system
|
||
- **Recommendation:** Verify no API keys, Firebase configs, or secrets are hardcoded in client bundles
|
||
|
||
---
|
||
|
||
## 8. Injection Test Results
|
||
|
||
### Host Header Injection
|
||
- **app.closebot.com**: ✅ **Protected** — Vercel returns 404 `DEPLOYMENT_NOT_FOUND` for invalid Host headers
|
||
- **X-Forwarded-Host**: ⚠️ Accepted without error (200 response) — Vercel passes it through to Next.js
|
||
|
||
### CRLF Injection
|
||
- **app.closebot.com**: ✅ **Not vulnerable** — URL-encoded `%0d%0a` is preserved in `Link` header as-is (not decoded), no header injection
|
||
|
||
### Path Traversal
|
||
- Standard path traversal payloads return 404 from Next.js catch-all router — **not vulnerable**
|
||
|
||
### SQL Injection / XSS
|
||
- Could not test input fields directly (behind Vercel Security Checkpoint)
|
||
- API endpoints return 401 without processing input — auth layer prevents unauthenticated injection testing
|
||
- **Recommendation:** Test authenticated injection scenarios during development
|
||
|
||
---
|
||
|
||
## 9. Rate Limiting Results
|
||
|
||
### app.closebot.com
|
||
|
||
| Endpoint | Requests | All Status Codes | Rate Limited? |
|
||
|----------|----------|-----------------|---------------|
|
||
| `/login` | 50 rapid | All `200` | 🔴 **NO** |
|
||
|
||
After approximately 50+ requests, Vercel Security Checkpoint started returning 403 challenges — this is Vercel's built-in protection, not application-level rate limiting.
|
||
|
||
**Note:** Vercel's checkpoint eventually kicked in (HTTP 403 with `x-vercel-mitigated: challenge`), providing some protection, but this is infrastructure-level, not application-level rate limiting.
|
||
|
||
### api.closebot.com
|
||
|
||
| Endpoint | Requests | All Status Codes | Rate Limited? |
|
||
|----------|----------|-----------------|---------------|
|
||
| `/` | 50 rapid | All `404` | 🔴 **NO** |
|
||
|
||
**Severity: HIGH** — No rate limiting on the API. This means:
|
||
- Brute-force attacks on auth tokens are possible
|
||
- API abuse/scraping is unrestricted
|
||
- DDoS amplification risk
|
||
|
||
**Recommendation:** Implement rate limiting (e.g., Azure API Management, custom middleware, or Cloudflare in front of the API).
|
||
|
||
---
|
||
|
||
## 10. Auth/Firebase Analysis
|
||
|
||
### Authentication System: Clerk
|
||
|
||
- **Provider**: Clerk (not Firebase)
|
||
- **Open Registration**: Yes — `/register` returns 200
|
||
- **Login**: `/login` with redirect support
|
||
- **Auth tokens**: Likely JWT via Clerk (standard Clerk flow)
|
||
- **Session management**: Clerk handles sessions via `__session` cookie and/or Bearer tokens
|
||
|
||
### No Firebase Found
|
||
- `/__/firebase/init.json` → 404
|
||
- No Firebase references detected in accessible HTML/JS
|
||
- Not applicable for Firebase-specific testing
|
||
|
||
---
|
||
|
||
## 11. Third-Party Risk
|
||
|
||
### External Scripts (from HTML source)
|
||
Due to Vercel Security Checkpoint, detailed JS bundle analysis was limited. From what was visible:
|
||
|
||
1. **Clerk** — Authentication provider (trusted, SOC 2 compliant)
|
||
2. **Next.js** — React framework (trusted)
|
||
3. **Vercel** — Hosting platform (trusted)
|
||
|
||
### WordPress Third-Party
|
||
- **Cloudflare** — CDN and bot protection
|
||
- **Kinsta** — WordPress hosting
|
||
|
||
### SRI (Subresource Integrity)
|
||
- Could not verify SRI on JS bundles due to Vercel checkpoint
|
||
- **Recommendation:** Ensure all third-party scripts have SRI hashes
|
||
|
||
---
|
||
|
||
## 12. Infrastructure Notes
|
||
|
||
### Origin IP Exposure
|
||
|
||
| Domain | Behind CDN? | Origin Exposed? |
|
||
|--------|-------------|-----------------|
|
||
| `app.closebot.com` | Vercel Edge | ✅ Protected (Vercel DNS) |
|
||
| `api.closebot.com` | ❌ **NO CDN** | 🔴 **YES — `20.115.232.12`** (Azure direct) |
|
||
| `closebot.com` | Cloudflare | ✅ Protected |
|
||
|
||
**Severity: HIGH** — `api.closebot.com` resolves directly to an Azure App Service IP with no CDN/WAF in front. The full Azure hostname `cb-api-zarqcgo3sph6q.azurewebsites.net` is exposed via DNS CNAME chain.
|
||
|
||
### Azure Information Leakage
|
||
- **App ID**: `cid-v1:1593e7a7-a705-4f69-977e-49c3fa1d0aa3` (via `Request-Context` header)
|
||
- **Azure hostname**: `cb-api-zarqcgo3sph6q.azurewebsites.net`
|
||
- **Datacenter**: `waws-prod-mwh-117` (West US 2, Microsoft West Hub)
|
||
- **Server**: Kestrel (ASP.NET)
|
||
- **Routing cookies**: TiPMix (Azure traffic routing)
|
||
|
||
### SSL/TLS
|
||
|
||
| Property | Value |
|
||
|----------|-------|
|
||
| **Certificate** | Let's Encrypt R13 |
|
||
| **Valid From** | Feb 5, 2026 |
|
||
| **Valid Until** | May 6, 2026 |
|
||
| **Subject** | `CN=app.closebot.com` |
|
||
| **SANs** | `app.closebot.com` only |
|
||
| **HSTS** | ✅ `max-age=63072000` (app only) |
|
||
| **HSTS Preload** | ❌ Not in preload list |
|
||
|
||
---
|
||
|
||
## 13. Positive Findings ✅
|
||
|
||
1. **Vercel Security Checkpoint** — Effective bot/scraping protection on the frontend
|
||
2. **Clerk Authentication** — Industry-standard auth provider, properly handling session states
|
||
3. **No sensitive files exposed** — `.env`, `.git`, `wp-config.php` all blocked
|
||
4. **WordPress user enumeration blocked** — `/wp-json/wp/v2/users` returns 401
|
||
5. **XML-RPC disabled** — Returns 403
|
||
6. **HSTS on app.closebot.com** — 2-year max-age
|
||
7. **Host header injection blocked** — Vercel properly rejects invalid hosts
|
||
8. **CRLF injection blocked** — Properly encoded
|
||
9. **No admin/staging subdomains exposed** — Clean subdomain surface
|
||
10. **WordPress .env/wp-config blocked** — Server-level blocks in place
|
||
11. **Azure API cookies properly secured** — Secure, HttpOnly flags set
|
||
12. **Cloudflare protecting WordPress** — Bot management, edge caching
|
||
|
||
---
|
||
|
||
## 14. Recommendations
|
||
|
||
### 🔴 CRITICAL — Fix Immediately
|
||
|
||
#### 1. API CORS Wildcard (`Access-Control-Allow-Origin: *`)
|
||
**Impact:** Any website can make cross-origin requests to your API, including authenticated endpoints
|
||
**Fix:**
|
||
```csharp
|
||
// In your ASP.NET API startup/program.cs
|
||
builder.Services.AddCors(options =>
|
||
{
|
||
options.AddDefaultPolicy(policy =>
|
||
{
|
||
policy.WithOrigins(
|
||
"https://app.closebot.com",
|
||
"https://closebot.com"
|
||
)
|
||
.AllowAnyMethod()
|
||
.AllowAnyHeader()
|
||
.AllowCredentials();
|
||
});
|
||
});
|
||
```
|
||
|
||
### 🟠 HIGH — Fix This Week
|
||
|
||
#### 2. Add Security Headers to API (api.closebot.com)
|
||
```csharp
|
||
// Middleware or web.config
|
||
app.Use(async (context, next) =>
|
||
{
|
||
context.Response.Headers.Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains");
|
||
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
||
context.Response.Headers.Add("X-Frame-Options", "DENY");
|
||
context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");
|
||
context.Response.Headers.Add("Content-Security-Policy", "default-src 'none'");
|
||
context.Response.Headers.Remove("Server");
|
||
context.Response.Headers.Remove("Request-Context");
|
||
context.Response.Headers.Remove("x-ms-middleware-request-id");
|
||
await next();
|
||
});
|
||
```
|
||
|
||
#### 3. Put API Behind CDN/WAF
|
||
- Add Cloudflare or Azure Front Door in front of `api.closebot.com`
|
||
- Hide the origin Azure IP
|
||
- Get DDoS protection and rate limiting
|
||
|
||
#### 4. Implement API Rate Limiting
|
||
```csharp
|
||
// ASP.NET rate limiting middleware
|
||
builder.Services.AddRateLimiter(options =>
|
||
{
|
||
options.AddFixedWindowLimiter("api", opt =>
|
||
{
|
||
opt.Window = TimeSpan.FromMinutes(1);
|
||
opt.PermitLimit = 60;
|
||
opt.QueueLimit = 0;
|
||
});
|
||
});
|
||
```
|
||
|
||
### 🟡 MEDIUM — Fix This Sprint
|
||
|
||
#### 5. Add Security Headers to app.closebot.com
|
||
In `next.config.js`:
|
||
```javascript
|
||
const securityHeaders = [
|
||
{ key: 'Content-Security-Policy', value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.clerk.accounts.dev; style-src 'self' 'unsafe-inline';" },
|
||
{ key: 'X-Frame-Options', value: 'DENY' },
|
||
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
||
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
|
||
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
|
||
];
|
||
|
||
module.exports = {
|
||
headers: async () => [{ source: '/:path*', headers: securityHeaders }],
|
||
poweredByHeader: false, // Remove X-Powered-By: Next.js
|
||
};
|
||
```
|
||
|
||
#### 6. Remove WordPress readme.html and license.txt
|
||
```bash
|
||
# On WordPress server
|
||
rm /path/to/wordpress/readme.html
|
||
rm /path/to/wordpress/license.txt
|
||
```
|
||
|
||
#### 7. Hide Azure Infrastructure Headers
|
||
Remove `Request-Context` and `x-ms-middleware-request-id` headers from API responses.
|
||
|
||
#### 8. Add `Secure` Flag to NEXT_LOCALE Cookie
|
||
In Next.js middleware or cookie configuration, ensure `Secure` flag is set.
|
||
|
||
### 🔵 LOW — Fix When Convenient
|
||
|
||
#### 9. Add `security.txt`
|
||
Create `/.well-known/security.txt` with responsible disclosure contact.
|
||
|
||
#### 10. Add `robots.txt`
|
||
Even if you don't want indexing, an explicit `robots.txt` is good practice.
|
||
|
||
#### 11. Hide WordPress Version
|
||
```php
|
||
// In functions.php
|
||
remove_action('wp_head', 'wp_generator');
|
||
```
|
||
|
||
#### 12. Remove Clerk Auth Headers from Responses
|
||
Configure Clerk middleware to not expose `x-clerk-auth-status` and `x-clerk-auth-reason` to external responses.
|
||
|
||
#### 13. HSTS Preload
|
||
Submit `app.closebot.com` to the HSTS preload list and add `includeSubDomains; preload` to the HSTS header.
|
||
|
||
---
|
||
|
||
## Appendix: Raw Test Data
|
||
|
||
### CORS Test Results (api.closebot.com)
|
||
```
|
||
Origin: https://evil.com → Access-Control-Allow-Origin: *
|
||
Origin: null → Access-Control-Allow-Origin: *
|
||
Origin: https://evil.closebot.com → Access-Control-Allow-Origin: *
|
||
OPTIONS (preflight) → Access-Control-Allow-Origin: *, Allow-Headers: Authorization,Content-Type
|
||
```
|
||
|
||
### API Endpoint Discovery Summary
|
||
```
|
||
Authenticated (401): /bot, /bot/list, /bot/create, /bot/all, /bot/settings, /bot/config, /lead, /agency
|
||
Redirect (301): /swagger
|
||
Not Found (404): everything else
|
||
```
|
||
|
||
### Rate Limiting Test (50 rapid requests)
|
||
```
|
||
app.closebot.com/login: 200 200 200 200 200 ... (all 200, then Vercel checkpoint kicks in)
|
||
api.closebot.com/: 404 404 404 404 404 ... (all 404, never rate limited)
|
||
```
|