This document describes the security architecture, practices, and configuration for the Camera Driver module.
Module signing credentials are NOT stored in version control. To build signed modules:
- Copy
gradle.properties.templatetogradle.properties - Set environment variables:
export KEYSTORE_PASSWORD="your-secure-password" export CERT_PASSWORD="your-secure-password"
- Or edit
gradle.propertieslocally (never commit this file)
- Keystore:
camera-driver.jks(excluded from version control) - Certificate:
camera-driver.der(excluded from version control) - Alias:
camera-driver
IMPORTANT: These files contain private keys and must never be committed to version control.
Current Status: All HTTP endpoints require authentication with comprehensive security features.
/data/camera-driver/snapshot- REQUIRES AUTHENTICATION/data/camera-driver/stream- REQUIRES AUTHENTICATION
Supported Authentication Methods:
-
HTTP Session Authentication (Primary - for Ignition users)
- Users with valid Ignition Gateway sessions automatically authenticated
- No additional configuration required
- Seamless integration with Perspective and Vision clients
- Checks for
authenticated,username, andusersession attributes
-
Basic Authentication (For external tools)
curl -u username:password \ "http://gateway:8088/data/camera-driver/snapshot?device=Camera1&profile=000"- Validates credentials against Ignition gateway authentication
- Account lockout after 5 failed attempts (15-minute duration)
- Failed attempts tracked per username
- All authentication attempts logged for auditing
-
API Key Authentication (For programmatic access)
Query Parameter:
curl "http://gateway:8088/data/camera-driver/snapshot?device=Camera1&profile=000&apiKey=YOUR_KEY"Header (recommended for security):
curl -H "X-API-Key: YOUR_KEY" \ "http://gateway:8088/data/camera-driver/snapshot?device=Camera1&profile=000"
- SHA-256 hashed keys (no plain-text storage)
- Secure random key generation (256-bit entropy)
- Generate keys programmatically:
AuthenticationManager.generateApiKey() - Add keys via:
authManager.addApiKey(key, username)
Security Features (v2.2.0):
- ✅ Proper 401 Unauthorized responses with WWW-Authenticate header
- ✅ Session validation checks for Ignition users
- ✅ Multiple authentication methods for flexibility
- ✅ Failed authentication attempts logged for auditing
- ✅ Account lockout after 5 failed attempts (15-minute duration)
- ✅ SHA-256 hashed API keys with secure storage
- ✅ Security event logging for all authentication failures
- ✅ Failed attempt tracking per username
- ✅ Lockout expiration with automatic cleanup
Managing API Keys:
API keys can be added programmatically:
AuthenticationManager authManager = onvifRoutes.getAuthenticationManager();
// Generate a secure random API key
String apiKey = AuthenticationManager.generateApiKey();
// Add the key for a specific user
authManager.addApiKey(apiKey, "apiuser");
// Remove a key when no longer needed
authManager.removeApiKey(apiKey);
// Clear all account lockouts (administrative override)
authManager.clearAllLockouts();Monitoring Authentication:
// Get failed attempt statistics
Map<String, AtomicInteger> stats = authManager.getFailedAttemptStats();Camera credentials are stored securely using Ignition's SecretConfig:
- Passwords encrypted at rest in Ignition database
- Never logged or exposed in error messages
- Automatic cleanup via try-with-resources
SSL/TLS certificate validation is configurable per device:
Validation Modes:
- STRICT: Full certificate validation (production recommended)
- TRUST_FIRST_USE: Accept and pin self-signed on first connection (NOT IMPLEMENTED — selecting it raises IllegalArgumentException)
- INSECURE: Accept any certificate (development only)
Default: STRICT (changed from INSECURE in 2.34.x — see /modules/.review/FINAL_REVIEW.md §4 C5).
ONVIFClient.createSSLContext() now treats any unknown / unset mode as STRICT and only relaxes validation when the operator has explicitly selected INSECURE. While INSECURE is active:
- The constructor logs a WARN at client creation, naming the device URL.
ONVIFPoller.poll()re-emits a WARN every poll cycle (default interval 5 s) so the unsafe state is surfaced continuously in Gateway logs.
NoopHostnameVerifier and the trust-all X509TrustManager continue to back the INSECURE path; switching the default does not change INSECURE's semantics, only its opt-in posture.
Why configurable?: Many IP cameras ship with self-signed certificates. INSECURE is intended for closed networks during initial bring-up; production deployments should provision proper certificates and use STRICT.
All HTTP endpoints support HTTPS when Ignition Gateway is configured with SSL.
All user inputs are validated before use:
- Pattern:
[a-zA-Z0-9_.()\- ]+ - Maximum length: 64 characters
- Allows letters, digits, spaces,
_,-,., and parentheses to match Ignition's device-name charset; rejects/,\, control characters, and path traversal (..)
- Pattern:
[a-zA-Z0-9_-]+ - Maximum length: 64 characters
- Sanitized before XML insertion
- Validated via
ValidationUtil.isValidIpOrHost: strict IPv4 (each octet 0–255) or a valid DNS hostname - Bogus inputs such as
1111or25525525525are rejected (the previous regex accepted them)
The bundled go2rtc process is launched on gateway localhost to transcode RTSP for browser
playback. Its HTTP API receives credentialed RTSP URLs (rtsp://user:pass@host) when streams
are registered.
- API authentication: as of v3.0.8 the gateway generates a random per-launch password,
writes it into the go2rtc
api.passwordconfig, and sends HTTP Basic auth on every API call. This closes the prior exposure where any local process couldGET /api/streamsand read back camera credentials from an unauthenticated localhost API. - Trust boundary: go2rtc binds to localhost only. The threat model assumes the gateway host itself is trusted; operators should not run untrusted local processes on the gateway, and the go2rtc API port should never be exposed beyond localhost.
- Stream proxy: the module's own stream proxy never logs source URLs, so credentials do not leak into gateway logs.
WebRTC playback introduces one new network listener and one new endpoint; both are constrained:
- Signaling is authenticated: browsers never talk to go2rtc directly. The SDP offer/answer
exchange goes through
POST /data/camera-driver/webrtc, which enforces the same authentication (session / Basic / API key / Perspective session token) and per-IP rate limiting as every other module endpoint. The gateway then relays the exchange to go2rtc's localhost API with the per-launch Basic auth password. - Media listener (port 8555, TCP+UDP): go2rtc listens for ICE/DTLS media connections on 8555. This port carries no plaintext video: media is SRTP, keyed via the DTLS handshake whose fingerprints are pinned in the SDP exchange — which only an authenticated client can perform. An attacker connecting to 8555 without a signaled session cannot negotiate a stream.
- ICE candidates: advertised candidates are auto-detected site-local IPv4 addresses, or an
operator-controlled allowlist file (
data/camera-driver/go2rtc/webrtc-candidates.txt). No STUN/TURN servers are contacted — no traffic leaves the local network for negotiation. - Exposure guidance: do not port-forward 8555 to untrusted networks. For remote viewing, front the gateway with a VPN, as with the rest of the Ignition web interface.
All XML parsing is protected against XML External Entity (XXE) attacks:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);All user inputs are escaped before insertion into XML:
&→&<→<>→>"→"'→'
Cross-Origin Resource Sharing (CORS) headers are restricted to known origins only. The wildcard * origin is not used in production.
- Limit: 600 requests per minute per IP address
- Scope: Applied to snapshot and stream endpoints
- Response: HTTP 429 Too Many Requests when exceeded
- Tracking: Per-IP via X-Forwarded-For and X-Real-IP headers
- Proxy-Aware: Honors reverse proxy headers for accurate IP tracking
- Maximum concurrent snapshots: 50
- Maximum concurrent streams: 20
Exceeding limits returns HTTP 429 (Too Many Requests).
DoS Protection: The per-IP rate limiting prevents abuse and resource exhaustion attacks while allowing legitimate users normal access to camera feeds.
ONVIF WS-UsernameToken specification requires SHA-1 for password digests. This is a protocol-level limitation, not a code defect.
Mitigation:
- Use strong passwords (16+ characters, high entropy)
- Network isolation (VPN, VLAN)
- HTTPS for all ONVIF communication
- Frequent password rotation
Reference: ONVIF Core Specification Version 2.0, Section 5.1.1
If you discover a security vulnerability, please report it responsibly:
-
GitHub Security Advisories (Preferred):
- Visit: https://github.com/nigelgwork/ignition-module-camera-driver/security/advisories
- Click "Report a vulnerability"
- Provide detailed description of the vulnerability
-
GitHub Issues:
- Create an issue at: https://github.com/nigelgwork/ignition-module-camera-driver/issues
- Mark with "Security" label
- Include version number, steps to reproduce, and impact assessment
-
Email: For sensitive disclosures, contact via GitHub profile
Response Time: We aim to respond within 48 hours
Please do NOT publicly disclose vulnerabilities until a patch is available and users have been given reasonable time to update (typically 90 days).
| Date | Version | Auditor | Findings |
|---|---|---|---|
| 2025-11-22 | 1.0.23 | Internal Review | 3 Critical, 5 High |
| 2025-11-22 | 2.0.0 | Internal Review | 2 Critical resolved, 1 Critical remaining (authentication) |
| 2025-11-22 | 2.1.0 | Internal Review | All critical issues resolved + comprehensive testing |
| 2025-11-22 | 2.2.0 | Internal Review | Production-ready authentication with account lockout + API key management |
- Camera surveillance requires proper access controls ✅ (v2.0.0+)
- Audit logging recommended for camera access
- Healthcare facilities require authentication ✅ (v2.0.0+)
- Encryption in transit recommended (HTTPS)
- Access logging recommended
- Payment environments require encryption ✅
- Access control implemented ✅ (v2.0.0+)
- Regular security updates required
- Network Isolation: Place cameras on dedicated VLAN
- Firewall Rules: Restrict camera access to Ignition Gateway only
- HTTPS: Enable SSL/TLS on Ignition Gateway
- Strong Passwords: Use 16+ character passwords for cameras
- Regular Updates: Keep Ignition and modules updated
- Monitoring: Enable access logging and alerting
- SSL/TLS Mode: Use STRICT mode in production
- Authentication: Never disable authentication on endpoints
- CORS: Configure allowed origins explicitly
- Rate Limits: Adjust based on environment needs
- Password Rotation: Rotate camera passwords quarterly
- Certificate Updates: Renew certificates before expiration
- Dependency Updates: Monitor for security advisories
- Audit Logs: Review access logs regularly
All dependencies are scanned for known vulnerabilities:
./gradlew dependencyCheckAnalyzeorg.apache.httpcomponents:httpclient:4.5.14- ✅ No critical CVEsorg.apache.httpcomponents:httpcore:4.4.16- ✅ No critical CVEscom.google.code.gson:gson:2.13.2- ✅ No known vulnerabilitiesIgnition SDK 8.3.0- Managed by Ignition platform
- SECURITY: Production-ready authentication with comprehensive security features
- SECURITY: Account lockout after 5 failed attempts (15-minute duration)
- SECURITY: SHA-256 hashed API keys with secure random generation
- SECURITY: Failed authentication tracking per username
- SECURITY: Security event logging for all authentication failures
- SECURITY: Lockout expiration with automatic cleanup
- SECURITY: Secure credential validation (no plain-text storage)
- SECURITY: API key management via AuthenticationManager
- ENHANCEMENT: Separated authentication logic into AuthenticationManager class
- ENHANCEMENT: Support for X-API-Key header (in addition to query parameter)
- Placeholder authentication from v2.1.0 FULLY IMPLEMENTED
- SECURITY: HTTP endpoint authentication implemented (session, Basic Auth, API key)
- SECURITY: Per-IP rate limiting implemented (600 req/min)
- SECURITY: 168 comprehensive automated tests including security tests
- SECURITY: XSS, SQL injection, JNDI injection, and path traversal protection verified
- SECURITY: XXE and Billion Laughs attack prevention tested
- KNOWN LIMITATION: Basic Auth and API key were placeholders (FIXED in v2.2.0)
- Critical authentication gap from v2.0.0 RESOLVED
- SECURITY: Removed hardcoded credentials from version control
- SECURITY: Made SSL/TLS validation configurable (STRICT mode available)
- SECURITY: Added ValidationUtil for centralized input validation
- SECURITY: Improved CORS policy with origin validation
- KNOWN LIMITATION: HTTP endpoints still use OPEN_ROUTE (FIXED in v2.1.0)
- Created comprehensive security documentation
- Credentials hardcoded (CRITICAL vulnerability - FIXED in v2.0.0)
- No authentication on endpoints (CRITICAL vulnerability - FIXED in v2.1.0)
- SSL validation always disabled (HIGH vulnerability - FIXED in v2.0.0)