mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-08-05 06:50:10 +00:00
fix:Harden authentication and sensitive file handling
This commit is contained in:
+26
-2
@@ -123,6 +123,9 @@ import {
|
||||
ensureUsedAttachmentDownloadTokenTable as ensureStoredAttachmentTokenTable,
|
||||
consumeAttachmentDownloadToken as consumeStoredAttachmentDownloadToken,
|
||||
} from './storage-attachment-token-repo';
|
||||
import {
|
||||
consumeTotpLoginCounter as consumeStoredTotpLoginCounter,
|
||||
} from './storage-totp-replay-repo';
|
||||
import {
|
||||
getRevisionDate as getStoredRevisionDate,
|
||||
updateRevisionDate as updateStoredRevisionDate,
|
||||
@@ -150,8 +153,8 @@ const STORAGE_SCHEMA_VERSION_KEY = 'schema.version';
|
||||
// Bump this whenever src/services/storage-schema.ts or migrations/0001_init.sql
|
||||
// changes. Existing D1 installs only rerun ensureStorageSchema() when this value
|
||||
// differs from config.schema.version.
|
||||
const STORAGE_SCHEMA_VERSION = '2026-06-23-invite-used-by';
|
||||
const REQUIRED_SCHEMA_TABLES = ['webauthn_credentials', 'webauthn_challenges', 'auth_requests'] as const;
|
||||
const STORAGE_SCHEMA_VERSION = '2026-06-23-totp-login-replay';
|
||||
const REQUIRED_SCHEMA_TABLES = ['webauthn_credentials', 'webauthn_challenges', 'auth_requests', 'totp_login_replays'] as const;
|
||||
|
||||
// D1-backed storage.
|
||||
// Contract:
|
||||
@@ -164,10 +167,13 @@ export class StorageService {
|
||||
private static schemaVerified = false;
|
||||
private static lastRefreshTokenCleanupAt = 0;
|
||||
private static lastAttachmentTokenCleanupAt = 0;
|
||||
private static lastTotpReplayCleanupAt = 0;
|
||||
private static readonly MAX_D1_SQL_VARIABLES = 100;
|
||||
|
||||
private static readonly REFRESH_TOKEN_CLEANUP_INTERVAL_MS = LIMITS.cleanup.refreshTokenCleanupIntervalMs;
|
||||
private static readonly ATTACHMENT_TOKEN_CLEANUP_INTERVAL_MS = LIMITS.cleanup.attachmentTokenCleanupIntervalMs;
|
||||
private static readonly TOTP_REPLAY_CLEANUP_INTERVAL_MS = 10 * 60 * 1000;
|
||||
private static readonly TOTP_REPLAY_MARKER_TTL_MS = 5 * 60 * 1000;
|
||||
private static readonly PERIODIC_CLEANUP_PROBABILITY = LIMITS.cleanup.cleanupProbability;
|
||||
|
||||
constructor(private db: D1Database) {}
|
||||
@@ -833,6 +839,24 @@ export class StorageService {
|
||||
return findStoredTrustedTokenUserId(this.db, this.trustedTwoFactorTokenKey.bind(this), token, deviceIdentifier);
|
||||
}
|
||||
|
||||
async consumeTotpLoginCounter(userId: string, timeCounter: number, consumedAtMs: number = Date.now()): Promise<boolean> {
|
||||
if (!Number.isSafeInteger(timeCounter) || timeCounter < 0) return false;
|
||||
const result = await consumeStoredTotpLoginCounter(
|
||||
this.db,
|
||||
this.shouldRunPeriodicCleanup.bind(this),
|
||||
StorageService.lastTotpReplayCleanupAt,
|
||||
StorageService.TOTP_REPLAY_CLEANUP_INTERVAL_MS,
|
||||
userId,
|
||||
timeCounter,
|
||||
consumedAtMs,
|
||||
StorageService.TOTP_REPLAY_MARKER_TTL_MS
|
||||
);
|
||||
if (result.cleanedUpAt !== null) {
|
||||
StorageService.lastTotpReplayCleanupAt = result.cleanedUpAt;
|
||||
}
|
||||
return result.consumed;
|
||||
}
|
||||
|
||||
// --- Revision dates ---
|
||||
|
||||
async getRevisionDate(userId: string): Promise<string> {
|
||||
|
||||
Reference in New Issue
Block a user