Authentication and API Keys
Authentication and API Keys
The following files were used as context for generating this wiki page:
- .github/workflows/ghcr-publish.yml
- README.md
- RELEASE_3.0.0.md
- RELEASE_SUMMARY.md
- docs/V3_COMPLETE_GUIDE.md
Purpose and Scope
This page documents the authentication mechanisms available in the WSHawk Web Management Dashboard, including password-based authentication for browser access and API key authentication for programmatic access. For information about launching the dashboard and configuring ports, see Dashboard Overview and Launch. For REST API endpoint reference, see REST API Reference.
Overview
The WSHawk Web Management Dashboard supports two independent authentication mechanisms:
| Mechanism | Use Case | Configuration Method | Access Type |
|-----------|----------|---------------------|-------------|
| Password Authentication | Interactive browser login | WSHAWK_WEB_PASSWORD | Web UI + API |
| API Key Authentication | Programmatic/headless access | WSHAWK_API_KEY | API only |
Both mechanisms use SHA-256 hashing with salted storage to protect credentials. If neither is configured, the dashboard operates in open mode (suitable only for local testing on localhost).
Sources: README.md:121-136
Web Dashboard Password Authentication
Configuration
The web dashboard password is configured via the WSHAWK_WEB_PASSWORD environment variable. This password protects both the web UI and the REST API endpoints.
# Set password before launching dashboard
export WSHAWK_WEB_PASSWORD='your-strong-password'
wshawk --web --port 5000
When the password is set, all access to the dashboard requires authentication through the login page at /login.
Password Hashing Implementation
The dashboard implements SHA-256 hashing with salt for password storage:
- Password Reception: Plain text password received via POST to
/login - Salt Generation: A random salt is generated or retrieved from persistent storage
- Hash Computation:
SHA-256(password + salt)is computed - Comparison: Computed hash is compared with stored hash
The actual password is never stored in plaintext or logged.
Authentication Flow Diagram
sequenceDiagram
participant User as "Browser User"
participant Login as "/login Endpoint"
participant Auth as "Authentication Module"
participant Session as "Session Store"
participant DB as "scans.db<br/>(SQLite)"
User->>Login: "POST /login<br/>{password: 'user_password'}"
Login->>Auth: "validate_password()"
Auth->>DB: "SELECT salt FROM auth_config"
DB-->>Auth: "salt_value"
Auth->>Auth: "compute_hash = SHA256(password + salt)"
Auth->>DB: "SELECT password_hash FROM auth_config"
DB-->>Auth: "stored_hash"
Auth->>Auth: "compare(compute_hash, stored_hash)"
alt Authentication Success
Auth-->>Session: "create_session(user_id)"
Session-->>Login: "session_cookie"
Login-->>User: "302 Redirect to /dashboard<br/>Set-Cookie: session_id"
else Authentication Failure
Auth-->>Login: "401 Unauthorized"
Login-->>User: "401 Error: Invalid password"
end
Sources: README.md:121-127, RELEASE_3.0.0.md:18, docs/V3_COMPLETE_GUIDE.md:299-302
API Key Authentication
Configuration
API keys provide a method for programmatic access to the REST API without browser-based login. This is essential for CI/CD integration and automated workflows.
# Set API key for programmatic access
export WSHAWK_API_KEY='your-api-key-here'
# Use with curl
curl -H "X-API-Key: your-api-key-here" \
http://localhost:5000/api/scans
Alternatively, the API key can be passed as a command-line flag when launching the dashboard:
wshawk --web --api-key 'your-api-key-here'
API Key vs Password Authentication
flowchart TB
Request["HTTP Request to Dashboard"]
CheckAuth{"Authentication<br/>Method?"}
CheckCookie{"Session Cookie<br/>Present?"}
CheckAPIKey{"X-API-Key Header<br/>or Query Param?"}
ValidateCookie["Validate Session<br/>Against Session Store"]
ValidateAPIKey["Validate API Key<br/>SHA256(key) == stored_hash"]
Allow["Allow Access<br/>Proceed to Handler"]
Deny["401 Unauthorized<br/>Redirect to /login"]
Request --> CheckAuth
CheckAuth -->|"Browser Access"| CheckCookie
CheckAuth -->|"API Access"| CheckAPIKey
CheckCookie -->|"Valid"| ValidateCookie
CheckCookie -->|"Missing/Invalid"| Deny
CheckAPIKey -->|"Present"| ValidateAPIKey
CheckAPIKey -->|"Missing"| Deny
ValidateCookie -->|"Session Valid"| Allow
ValidateCookie -->|"Session Expired"| Deny
ValidateAPIKey -->|"Key Valid"| Allow
ValidateAPIKey -->|"Key Invalid"| Deny
Sources: README.md:135, docs/V3_COMPLETE_GUIDE.md:313-315
Authentication Storage and Lifecycle
Database Schema
Authentication credentials are stored in the SQLite database scans.db located in ~/.wshawk/. The relevant tables include:
| Table | Columns | Purpose |
|-------|---------|---------|
| auth_config | password_hash, salt, created_at, updated_at | Stores hashed password and salt |
| api_keys | key_hash, name, created_at, last_used | Stores hashed API keys and metadata |
| sessions | session_id, user_id, created_at, expires_at | Manages active web sessions |
Session Management
Browser sessions are managed with the following properties:
- Session Lifetime: 24 hours by default
- Storage: In-memory session store backed by SQLite for persistence
- Cookie Attributes:
HttpOnly,Secure(when using TLS),SameSite=Strict - Session Expiry: Automatic cleanup of expired sessions on dashboard startup
Security Implementation Diagram
graph TB
subgraph "Environment Variables"
EnvPass["WSHAWK_WEB_PASSWORD<br/>(Plaintext)"]
EnvAPIKey["WSHAWK_API_KEY<br/>(Plaintext)"]
end
subgraph "Dashboard Initialization"
Init["wshawk --web Startup"]
HashPass["SHA256 Password<br/>+ Generate Salt"]
HashAPI["SHA256 API Key"]
end
subgraph "Persistent Storage"
DB["~/.wshawk/scans.db"]
AuthTable["auth_config Table<br/>(password_hash, salt)"]
KeyTable["api_keys Table<br/>(key_hash)"]
end
subgraph "Runtime Authentication"
WebReq["Browser Request<br/>/login"]
APIReq["API Request<br/>/api/scans"]
SessionStore["In-Memory Session Store"]
Validator["Authentication Validator"]
end
EnvPass --> Init
EnvAPIKey --> Init
Init --> HashPass
Init --> HashAPI
HashPass --> AuthTable
HashAPI --> KeyTable
AuthTable --> DB
KeyTable --> DB
WebReq --> Validator
APIReq --> Validator
Validator --> DB
Validator --> SessionStore
DB -.->|"Read Hashes"| Validator
SessionStore -.->|"Check Session"| Validator
Sources: RELEASE_3.0.0.md:16-19, docs/V3_COMPLETE_GUIDE.md:294-302
Security Considerations
Password Storage Best Practices
The authentication system implements several security measures:
- No Plaintext Storage: Passwords and API keys are never stored in plaintext
- Salted Hashing: Each password uses a unique salt to prevent rainbow table attacks
- SHA-256 Algorithm: Industry-standard hashing algorithm
- No Logging: Authentication credentials are never written to log files
Deployment Security
For production deployments, additional security layers are recommended:
# Example nginx reverse proxy configuration
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Force HTTPS
if ($scheme != "https") {
return 301 https://$host$request_uri;
}
}
Security Recommendations:
| Risk | Mitigation |
|------|------------|
| Plaintext transmission | Deploy behind TLS-enabled reverse proxy (nginx, Apache) |
| Weak passwords | Enforce strong password policy (min 12 characters, complexity) |
| API key exposure | Rotate API keys regularly, use separate keys per integration |
| Session hijacking | Enable Secure and HttpOnly cookie attributes via HTTPS |
| Brute force attacks | Implement rate limiting on /login endpoint |
Sources: docs/V3_COMPLETE_GUIDE.md:299-302
Configuration Examples
Example 1: Local Development (No Authentication)
# Start dashboard without authentication (localhost only)
wshawk --web --host 127.0.0.1 --port 5000
In this mode, the dashboard is accessible without credentials. Only use this for local testing.
Example 2: Password-Protected Dashboard
# Set strong password
export WSHAWK_WEB_PASSWORD='MyS3cur3P@ssw0rd!'
# Launch dashboard
wshawk --web --host 0.0.0.0 --port 5000
Access the dashboard at http://your-server:5000, then log in with the configured password.
Example 3: API Key for CI/CD Integration
# Generate and set API key
export WSHAWK_API_KEY='wshawk-api-$(openssl rand -hex 32)'
# Launch dashboard
wshawk --web --api-key "$WSHAWK_API_KEY"
# Use in CI/CD pipeline
curl -X POST http://dashboard:5000/api/scans \
-H "X-API-Key: $WSHAWK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "ws://test.example.com", "options": {"smart_payloads": true}}'
Example 4: Docker with Environment File
# .env file
WSHAWK_WEB_PASSWORD=production_password_here
WSHAWK_API_KEY=production_api_key_here
# docker-compose.yml
version: '3.8'
services:
wshawk:
image: rothackers/wshawk:latest
env_file: .env
command: wshawk --web --host 0.0.0.0
ports:
- "5000:5000"
volumes:
- ./wshawk_data:/root/.wshawk
Sources: README.md:117-127
Hierarchical Configuration Integration
The authentication system integrates with the wshawk.yaml hierarchical configuration system. Credentials can be resolved from environment variables using the env: prefix:
# wshawk.yaml
web:
authentication:
password: "env:WSHAWK_WEB_PASSWORD" # Resolved from environment
api_key: "env:WSHAWK_API_KEY"
session:
lifetime_hours: 24
cookie_secure: true # Requires HTTPS
cookie_httponly: true
This approach allows secret management without hardcoding credentials in configuration files. For more information on the configuration system, see Configuration System.
Sources: README.md:137-150
Authentication State Machine
stateDiagram-v2
[*] --> Unauthenticated
Unauthenticated --> Authenticating: "POST /login with password"
Unauthenticated --> Authenticating: "API request with X-API-Key"
Authenticating --> Authenticated: "Credentials Valid"
Authenticating --> Unauthenticated: "Credentials Invalid<br/>(401 Response)"
Authenticated --> SessionActive: "Browser Session Created"
Authenticated --> APIKeyActive: "API Key Validated"
SessionActive --> SessionActive: "Valid Session Cookie<br/>on Each Request"
SessionActive --> Unauthenticated: "Session Expired (24h)"
SessionActive --> Unauthenticated: "User Logout"
APIKeyActive --> APIKeyActive: "Valid X-API-Key Header<br/>on Each Request"
APIKeyActive --> Unauthenticated: "Invalid API Key"
APIKeyActive --> Unauthenticated: "API Key Revoked"
Sources: docs/V3_COMPLETE_GUIDE.md:313-315
Programmatic Access Example
# Example: Python script using API key authentication
import requests
import os
# Read API key from environment
API_KEY = os.environ['WSHAWK_API_KEY']
BASE_URL = 'http://localhost:5000'
# Headers with API key
headers = {
'X-API-Key': API_KEY,
'Content-Type': 'application/json'
}
# Start a new scan
response = requests.post(
f'{BASE_URL}/api/scans',
headers=headers,
json={
'target': 'ws://example.com/api',
'options': {
'smart_payloads': True,
'playwright': False
}
}
)
scan_id = response.json()['scan_id']
print(f"Scan started: {scan_id}")
# Poll scan status
status_response = requests.get(
f'{BASE_URL}/api/scans/{scan_id}',
headers=headers
)
print(f"Status: {status_response.json()['status']}")
For complete REST API documentation, see REST API Reference.
Sources: README.md:135, docs/V3_COMPLETE_GUIDE.md:317-330
Troubleshooting
Common Authentication Issues
| Issue | Symptoms | Solution |
|-------|----------|----------|
| Password not set | Dashboard accessible without login | Set WSHAWK_WEB_PASSWORD environment variable |
| Wrong password | "401 Unauthorized" on login | Verify WSHAWK_WEB_PASSWORD value |
| Session expired | Redirected to login unexpectedly | Sessions expire after 24 hours; re-login required |
| API key rejected | "401 Unauthorized" on API calls | Verify X-API-Key header matches WSHAWK_API_KEY |
| CORS errors | Browser blocks API calls | Deploy behind reverse proxy with proper CORS headers |
Verifying Authentication Configuration
# Check if password is set
echo $WSHAWK_WEB_PASSWORD
# Check if API key is set
echo $WSHAWK_API_KEY
# Test API key authentication
curl -v -H "X-API-Key: $WSHAWK_API_KEY" \
http://localhost:5000/api/scans
# Expected: 200 OK response with scan list
# If 401: API key is invalid or not configured
Sources: docs/V3_COMPLETE_GUIDE.md:413-420