Document Version: 1.0
Last Updated: January 2026
Scope: Authentication, authorization, event bus security, API secret management
The Nightscout security model has evolved across API versions, progressing from simple API_SECRET authentication to a sophisticated role-based JWT system. This audit examines the current security architecture, identifies vulnerabilities, and recommends improvements.
| Component | Current State | Risk Level | Priority |
|---|---|---|---|
| API_SECRET handling | Adequate | Medium | Medium |
| JWT implementation | Good | Low | Low |
| Permission model (Shiro) | Good | Low | Low |
| Auth brute-force protection | Implemented (IP delay list) | Low | Low |
| General API rate limiting | Not Implemented | Medium | Medium |
| Event bus security | Minimal | Medium | Medium |
| Input validation | Inconsistent | High | High |
Location: lib/authorization/index.js, lib/server/env.js
Implementation:
function authorizeAdminSecret (secret) {
return env.enclave.isApiKey(secret);
}Flow:
- Client sends
api-secretheader orsecretquery parameter - Server compares against stored API_SECRET hash
- If match, client receives full admin permissions (
*)
Security Considerations:
- API_SECRET is hashed using SHA-1 (adequate but dated)
- Transmitted in headers - OK over HTTPS, risky over HTTP
- Single secret grants full admin access (no granularity)
Recommendations:
- Migrate to SHA-256 or bcrypt for secret comparison
- Add API_SECRET rotation mechanism
- Consider deprecating API_SECRET for role-based tokens
Location: lib/authorization/storage.js
Access tokens are pre-generated identifiers tied to subjects (users/devices).
Token Generation:
// Tokens are derived from API_SECRET + subject name
function generateAccessToken(subjectName) {
const hash = crypto.createHash('sha1');
hash.update(apiSecret + subjectName);
return subjectName.replace(' ', '-').toLowerCase() + '-' + hash.digest('hex').substring(0, 16);
}Security Considerations:
- Deterministic token generation (predictable if API_SECRET compromised)
- Tokens never expire until manually revoked
- Stored in MongoDB
auth_subjectscollection
Recommendations:
- Add token expiration
- Implement cryptographically random token generation
- Add token revocation audit logging
Location: lib/authorization/index.js, lib/server/enclave.js
Implementation:
const verified = env.enclave.verifyJWT(data.token);
token = verified.accessToken;JWT Structure:
{
"accessToken": "subject-access-token",
"iat": 1234567890,
"exp": 1234571490
}Security Considerations:
- JWTs signed with API_SECRET (HMAC-SHA256)
- Default expiration: 1 hour
- No refresh token mechanism
Recommendations:
- Implement refresh tokens for long-lived sessions
- Add JWT revocation list (for logout)
- Consider asymmetric signing (RS256) for distributed systems
Location: lib/authorization/, uses shiro-trie package
The authorization system uses Apache Shiro-style permissions with a trie data structure for efficient permission checking.
Permission Format:
domain:action:instance
Examples:
api:entries:read - Read entries
api:treatments:create - Create treatments
api:*:* - All API operations
* - Full admin access
Permission Hierarchy:
Subject (user/device)
↓
Roles (readable, denied, admin, etc.)
↓
Permissions (api:entries:read, etc.)
↓
Shiro Trie (wildcard matching)
Location: lib/authorization/storage.js
| Role | Permissions | Description |
|---|---|---|
admin |
* |
Full access |
readable |
api:*:read, notifications:*:ack |
Read-only access |
denied |
(none) | No permissions |
careportal |
api:treatments:create |
Can add treatments |
devicestatus-upload |
api:devicestatus:create |
Loop/pump status upload |
activity-create |
api:activity:create |
Activity logging |
Configuration: AUTH_DEFAULT_ROLES environment variable
| Setting | Effect |
|---|---|
readable |
Unauthenticated users can read data |
denied |
Unauthenticated users have no access |
| (custom) | Comma-separated role names |
Security Risk: Many installations set readable as default, exposing patient data publicly.
Recommendations:
- Default to
deniedin new installations - Add prominent warning when
readableis enabled - Implement IP whitelisting for read access
Location: lib/bus.js
The event bus is a Node.js Stream used for internal pub/sub communication.
var stream = new Stream;
stream.emit('notification', notify);
ctx.bus.on('data-update', handler);Security Characteristics:
- No authentication: Any code with
ctxreference can emit/listen - No authorization: No permission checks on events
- No encryption: Events contain plaintext data
- No rate limiting: Unlimited event emission
| Event | Data Sensitivity | Risk |
|---|---|---|
tick |
Low (heartbeat) | Low |
data-update |
High (glucose data) | Medium |
notification |
High (patient alerts) | Medium |
admin-notify |
Medium (auth failures) | Low |
teardown |
Low (shutdown) | Low |
- Plugin Isolation: Plugins can subscribe to any event
- Event Injection: Compromised plugin can emit fake events
- Data Leakage: Sensitive data passed through events without sanitization
- No Audit Trail: Events not logged for security analysis
Recommendations:
- Implement event namespace isolation for plugins
- Add event schema validation
- Create audit log for sensitive events
- Consider replacing with typed EventEmitter
Current State: Inconsistent across API versions
| API Version | Validation | Notes |
|---|---|---|
| v1 | Minimal | Basic type checking |
| v2 | Moderate | Some Joi schemas |
| v3 | Better | OpenAPI validation |
Identified Gaps:
- No consistent validation middleware
- Some endpoints accept arbitrary JSON
- MongoDB injection possible in some queries
Example Vulnerable Pattern:
// Potential NoSQL injection
collection.find({ type: req.query.type });Recommendations:
- Implement centralized validation middleware (Zod/Joi)
- Add request sanitization layer
- Enable MongoDB strict mode
Location: lib/authorization/delaylist.js
Implementation:
const DELAY_ON_FAIL = settings.authFailDelay || 5000; // Configurable via env
const FAIL_AGE = 60000; // Clear after 1 minute
ipDelayList.addFailedRequest(ip); // Add cumulative delay
ipDelayList.shouldDelayRequest(ip); // Check if request should be delayed
ipDelayList.requestSucceeded(ip); // Clear delay on successBehavior:
- Tracks failed authentication attempts by IP address
- Adds progressive delays (default 5000ms per failure, cumulative)
- Configurable via
authFailDelaysetting (useful for faster tests) - Auto-clears entries after 60 seconds of inactivity
- Immediately clears IP on successful authentication
Strengths:
- Effective brute-force protection for authentication endpoints
- Progressive delay makes automated attacks impractical
- Configurable for different environments
Current State: No rate limiting for general API endpoints
Gaps:
- Unauthenticated endpoints have no request limits
- Authenticated users can make unlimited requests
- No protection against API abuse or scraping
Recommendations:
- Add express-rate-limit middleware for general API protection
- Implement per-endpoint rate limits for expensive operations
- Add request size limits
- Consider Redis-based distributed rate limiting for multi-instance deployments
Location: lib/server/app.js
Current State: CORS enabled for all origins by default.
Recommendations:
- Allow configuring allowed origins
- Restrict credentials mode
- Add CORS preflight caching
Location: lib/server/app.js (uses helmet package)
Current Headers (via Helmet 4.x):
- Content-Security-Policy
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
Recommendations:
- Review and tighten CSP rules
- Add Permissions-Policy header
- Enable Report-Only mode for CSP testing
Location: lib/api3/storageSocket.js, lib/api3/alarmSocket.js
Implementation:
socket.on('subscribe', function onSubscribe (message, returnCallback) {
if (message && message.accessToken) {
return ctx.authorization.resolveAccessToken(message.accessToken, ...);
}
});Security Characteristics:
- Requires
accessTokenfor subscription - Token validated against authorization system
- Per-collection permission checks for
/storage
| Namespace | Required Permission |
|---|---|
/storage |
api:{collection}:read |
/alarm |
(any valid token) |
- Connection without auth: Clients can connect without authentication
- No message signing: Messages can be tampered
- Broadcast scope: Alarms broadcast to all subscribed clients
Recommendations:
- Require authentication on connection
- Add message integrity verification
- Implement fine-grained alarm subscriptions
| Category | Examples | Current Protection |
|---|---|---|
| PHI (Protected Health Information) | Glucose readings, treatments | Encryption at rest (MongoDB) |
| Authentication secrets | API_SECRET, tokens | Environment variables |
| Session data | JWTs | Signed, not encrypted |
| User preferences | Time zone, units | Stored in profile collection |
At Rest:
- MongoDB encryption depends on deployment
- No application-level encryption
In Transit:
- HTTPS recommended but not enforced
- Socket.IO uses same transport as HTTP
Recommendations:
- Require HTTPS in production
- Add application-level encryption for sensitive fields
- Implement key rotation mechanism
- No automatic data expiration
autoPrunefeature in API v3 (configurable days)- No GDPR-specific data deletion
Recommendations:
- Implement configurable data retention policies
- Add data export functionality
- Create data deletion audit trail
| ID | Description | Severity | Status |
|---|---|---|---|
| NS-SEC-001 | API_SECRET transmitted in query params | Medium | Open |
| NS-SEC-002 | No brute force protection on API | Medium | Partial |
| NS-SEC-003 | XSS possible in announcement messages | Low | Open |
| NS-SEC-004 | Deprecated request library |
Low | Open |
Threat Actors:
- Unauthenticated attackers (internet)
- Authenticated low-privilege users
- Compromised plugins/bridges
- Malicious caregivers
Attack Vectors:
- Brute force API_SECRET
- Token theft via XSS
- Data injection via unsanitized input
- Denial of service via resource exhaustion
Nightscout handles Protected Health Information (PHI):
- Requires HTTPS in production
- Needs access audit logging
- Must support user access controls
For EU users:
- Data export (partial support via API)
- Data deletion (not automated)
- Consent management (not implemented)
- Add input validation middleware - Prevent injection attacks
- Implement API rate limiting - Prevent DoS attacks
- Enforce HTTPS in production - Protect data in transit
- Replace deprecated
requestlibrary - Security maintenance - Add comprehensive audit logging - Compliance requirement
- Implement token rotation - Reduce exposure window
- Migrate API_SECRET hashing to SHA-256 - Stronger security
- Add event bus isolation - Plugin security
- Implement GDPR data deletion - Compliance
- Consider asymmetric JWT signing - Distributed deployment
- Add multi-factor authentication - Enhanced security
- Implement security scanning in CI - Automated vulnerability detection