Fix 16 HIGH/MEDIUM security bugs across API
HIGH fixes: - TOTP disable now requires valid code verification - TOTP secret removed from plaintext file storage - Container ID validated before update/check-update/logs operations - DNS server parameter restricted to configured servers (SSRF prevention) - Backup export no longer includes encryption key - Backup restore of sensitive files requires TOTP re-authentication MEDIUM fixes: - Session cookie Secure flag added - Caddy reload errors no longer leaked to client - saveConfig uses atomic locked updates via configStateManager - Log file path traversal prevented via symlink resolution - Credential cache entries now expire after 5 minutes - _httpFetch enforces 10MB response size limit - External URL path injection into Caddyfile blocked - Custom volume host paths validated against allowed roots - Error logs endpoint no longer returns stack traces - Logo delete path traversal prevented via path.basename() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,7 @@ module.exports = function(ctx) {
|
||||
{ key: 'config', path: ctx.CONFIG_FILE, required: false },
|
||||
{ key: 'dnsCredentials', path: ctx.dns.credentialsFile, required: false },
|
||||
{ key: 'credentials', path: process.env.CREDENTIALS_FILE || path.join(__dirname, '..', '..', 'credentials.json'), required: false },
|
||||
{ key: 'encryptionKey', path: ENCRYPTION_KEY_FILE, required: false },
|
||||
// NOTE: encryptionKey deliberately excluded — bundling it with encrypted data defeats the encryption
|
||||
{ key: 'totpConfig', path: ctx.TOTP_CONFIG_FILE, required: false },
|
||||
{ key: 'tailscaleConfig', path: ctx.TAILSCALE_CONFIG_FILE, required: false },
|
||||
{ key: 'notifications', path: ctx.NOTIFICATIONS_FILE, required: false }
|
||||
@@ -70,7 +70,7 @@ module.exports = function(ctx) {
|
||||
width: 256, margin: 2,
|
||||
color: { dark: '#000000', light: '#ffffff' }
|
||||
});
|
||||
backup.totp = { qrCode: qrDataUrl, manualKey: secret, issuer: 'DashCaddy' };
|
||||
backup.totp = { qrCode: qrDataUrl, issuer: 'DashCaddy' };
|
||||
}
|
||||
} catch (e) {
|
||||
ctx.log.warn('backup', 'Could not include TOTP QR in backup', { error: e.message });
|
||||
@@ -161,27 +161,44 @@ module.exports = function(ctx) {
|
||||
|
||||
// Restore configuration from backup
|
||||
router.post('/backup/restore', ctx.asyncHandler(async (req, res) => {
|
||||
const { backup, options = {} } = req.body;
|
||||
const { backup, options = {}, totpCode } = req.body;
|
||||
|
||||
if (!backup || !backup.version || !backup.files) {
|
||||
return ctx.errorResponse(res, 400, 'Invalid backup file format');
|
||||
}
|
||||
|
||||
// Require TOTP verification for restores that include security-sensitive files
|
||||
const sensitiveKeys = ['credentials', 'totpConfig', 'encryptionKey'];
|
||||
const restoresSensitive = sensitiveKeys.some(key =>
|
||||
backup.files[key] && backup.files[key].type !== 'missing' && !(options.skip || []).includes(key)
|
||||
);
|
||||
if (restoresSensitive && ctx.totpConfig.enabled && ctx.totpConfig.isSetUp) {
|
||||
if (!totpCode || !/^\d{6}$/.test(totpCode)) {
|
||||
return ctx.errorResponse(res, 400, 'TOTP code required for restoring security-sensitive files');
|
||||
}
|
||||
const { authenticator } = require('otplib');
|
||||
const secret = await ctx.credentialManager.retrieve('totp.secret');
|
||||
if (secret) {
|
||||
authenticator.options = { window: 1 };
|
||||
if (!authenticator.verify({ token: totpCode, secret })) {
|
||||
return ctx.errorResponse(res, 401, '[DC-111] Invalid TOTP code');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const results = {
|
||||
restored: [],
|
||||
skipped: [],
|
||||
errors: []
|
||||
};
|
||||
|
||||
// File mapping
|
||||
const ENCRYPTION_KEY_FILE = process.env.ENCRYPTION_KEY_FILE || path.join(path.dirname(ctx.SERVICES_FILE), '.encryption-key');
|
||||
// File mapping (encryptionKey excluded — must never be overwritten from backup)
|
||||
const fileMapping = {
|
||||
services: ctx.SERVICES_FILE,
|
||||
caddyfile: ctx.caddy.filePath,
|
||||
config: ctx.CONFIG_FILE,
|
||||
dnsCredentials: ctx.dns.credentialsFile,
|
||||
credentials: process.env.CREDENTIALS_FILE || path.join(__dirname, '..', '..', 'credentials.json'),
|
||||
encryptionKey: ENCRYPTION_KEY_FILE,
|
||||
totpConfig: ctx.TOTP_CONFIG_FILE,
|
||||
tailscaleConfig: ctx.TAILSCALE_CONFIG_FILE,
|
||||
notifications: ctx.NOTIFICATIONS_FILE
|
||||
|
||||
Reference in New Issue
Block a user