Files
ocdp-go/docs/security/bugs-security.md
Ivan087 7f238a3168 refactor: full-stack restructure with multi-tenancy, workspace management, and K8s diagnostics
- Add Workspace domain (entity, repository, service, handler, DTO)
- Add multi-tenant K8s client with tenant binding and quota management
- Add K8s diagnostics client (instance diagnostics)
- Add authorization middleware (authz package)
- Restructure frontend to feature-based architecture (features/)
- Add User Management page in configuration
- Add AccessDenied page and route guards
- Refactor shared components (form inputs, layout, UI)
- Update Tailwind config for new design system
- Add comprehensive documentation (docs/, tasks/, plans)
- Improve cluster service with better kubeconfig handling
- Add tests for crypto, config, helm client, tenant binding
2026-05-12 16:15:14 +08:00

7.8 KiB

OCDP Security Audit Report

Date: 2026-05-11 Target: http://10.6.80.114:18080 API Base: http://10.6.80.114:18080/api/v1


Finding 1: User Enumeration via Login Error Messages

Field Value
Test Authentication Error Disclosure
Severity Medium
Endpoint POST /api/v1/auth/login
Status Confirmed

What I Did

# Non-existent user
curl -s -X POST http://10.6.80.114:18080/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"nonexistent_user_xyz","password":"test123"}'

# Existing user with wrong password
curl -s -X POST http://10.6.80.114:18080/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"wrongpassword"}'

Expected

Both requests should return the same generic error message (e.g., "Invalid credentials") to prevent username enumeration.

Actual

  • Non-existent user: {"error":"Login failed","message":"user not found","code":401}
  • Existing user: {"error":"Login failed","message":"invalid password","code":401}

The error messages are different, allowing an attacker to determine whether a username exists in the system.

Impact

An attacker can enumerate valid usernames by observing the error message difference. This is the first step in a targeted brute force or credential stuffing attack.

Recommendation

Return identical error messages for both cases, e.g., "Invalid username or password".


Finding 2: No Rate Limiting on Login Endpoint

Field Value
Test Brute Force Protection
Severity Medium
Endpoint POST /api/v1/auth/login
Status Confirmed

What I Did

for i in $(seq 1 10); do
  curl -s -o /dev/null -w "%{http_code}" \
    -X POST http://10.6.80.114:18080/api/v1/auth/login \
    -H "Content-Type: application/json" \
    -d '{"username":"admin","password":"wrongpassword"}'
done

Expected

After a threshold (e.g., 5 failed attempts), the server should return HTTP 429 Too Many Requests or temporarily lock the account.

Actual

All 10 rapid sequential attempts returned HTTP 401. No rate limiting, no account lockout, no progressive delay.

Impact

An attacker can brute force passwords without restriction. Combined with Finding 1 (user enumeration), the attack surface is increased.

Recommendation

  • Implement rate limiting on the login endpoint (e.g., max 5 attempts per minute per IP).
  • Consider account lockout after N failed attempts.
  • Add progressive response delays after repeated failures.

Finding 3: Server Version Disclosure

Field Value
Test Information Disclosure
Severity Low
Endpoint All (HTTP response headers)
Status Confirmed

What I Did

curl -s -D - http://10.6.80.114:18080/ | head -10

Expected

Server header should be generic (e.g., Server: nginx) or removed entirely.

Actual

Server: nginx/1.27.5

Impact

Knowing the exact nginx version helps attackers target known vulnerabilities for that specific version.

Recommendation

Disable or obfuscate the Server header in nginx configuration:

server_tokens off;

Finding 4: Permissive CORS Policy

Field Value
Test CORS Misconfiguration
Severity Low
Endpoint All API endpoints
Status Confirmed

What I Did

curl -s -D - http://10.6.80.114:18080/api/v1/auth/login \
  -X POST -H "Content-Type: application/json" \
  -d '{"username":"test","password":"test"}'

Expected

CORS Access-Control-Allow-Origin should be restricted to the application's origin (e.g., http://10.6.80.114:18080) rather than allowing all origins.

Actual

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
Access-Control-Max-Age: 86400

Impact

Any website can make cross-origin requests to the API. If a user is logged in, a malicious site could potentially make authenticated API calls on their behalf (CSRF-style attack, though mitigated by the Bearer token requirement).

Recommendation

Restrict Access-Control-Allow-Origin to the specific frontend origin(s) instead of *.


Finding 5: Missing Security Headers

Field Value
Test Security Headers Audit
Severity Low
Endpoint All
Status Confirmed

What I Did

curl -s -D - http://10.6.80.114:18080/ | head -20

Expected

Security headers should include:

  • Strict-Transport-Security
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Content-Security-Policy

Actual

None of these security headers are present in responses.

Impact

Increases attack surface for clickjacking, MIME-type confusion, and XSS attacks.

Recommendation

Add the following headers to nginx configuration:

add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "0" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;

Finding 6: /health Endpoint Returns HTML Instead of Health Status

Field Value
Test Health Endpoint Behavior
Severity Low
Endpoint GET /health
Status Confirmed

What I Did

curl -s http://10.6.80.114:18080/health

Expected

A health check endpoint should return a structured JSON response (e.g., {"status":"healthy"}) with HTTP 200.

Actual

Returns the full index.html SPA page with HTTP 200:

<!doctype html>
<html lang="en">
  <head>
    <title>OCDP Platform</title>
    ...

Impact

Not a direct vulnerability, but misconfigured health checks can cause false positives in monitoring/load balancer health checks. It also means the SPA is served at /health, which is unexpected.

Recommendation

Implement a dedicated health endpoint that returns {"status":"ok"} with appropriate content type, or remove the /health route if not needed.


Tests Passed (No Issues Found)

Test Result
1. Unauthenticated Access PASS - All business endpoints return 401
2. JWT Token Manipulation PASS - Tampered tokens, alg=none, invalid formats all rejected (401)
3. XSS/SQLi Testing PASS - Script injection, SQLi patterns safely handled
4. IDOR - Instance Access PASS - No instances deployed to test; cluster/registry isolation confirmed working
5. Sensitive Data Masking PASS - Cluster certs/keys and registry passwords masked as ••••••••
6. Self-Registration PASS - Registration endpoint requires authentication (401)
7. Path Traversal PASS - Path traversal attempts return index.html (not /etc/passwd)
8. Admin Permission Escalation PASS - Regular users blocked from admin endpoints (403)

Summary

Severity Count Findings
Critical 0
High 0
Medium 2 User enumeration, No rate limiting
Low 4 Server version disclosure, Permissive CORS, Missing security headers, /health returns HTML
Total 6

The platform's core security controls (authentication, JWT validation, authorization, sensitive data masking) are properly implemented. The main areas for improvement are authentication hardening (rate limiting, user enumeration) and HTTP security hardening (headers, CORS).