From 156320979703d5bd905cb1d43fc4d5155f737036 Mon Sep 17 00:00:00 2001 From: jack Date: Mon, 12 Jan 2026 10:46:34 -1000 Subject: [PATCH] =?UTF-8?q?fix(security):=20reduce=20timestamp=20validatio?= =?UTF-8?q?n=20window=20to=20=C2=B15=20minutes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BCH-01-011: Reduces replay attack window from ±1 hour to ±5 minutes. The previous 1-hour window allowed extended replay attacks. A 5-minute window is industry standard for replay protection while accommodating reasonable clock drift. - Updated InputValidator.validateTimestamp to use 300-second window - Updated tests to verify new boundary conditions Co-Authored-By: Claude Opus 4.5 --- bitchat/Utils/InputValidator.swift | 8 +++-- bitchatTests/InputValidatorTests.swift | 46 ++++++++++++++++++-------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/bitchat/Utils/InputValidator.swift b/bitchat/Utils/InputValidator.swift index 468531f2..c2b2fb53 100644 --- a/bitchat/Utils/InputValidator.swift +++ b/bitchat/Utils/InputValidator.swift @@ -52,11 +52,13 @@ struct InputValidator { // MessageType/NoisePayloadType enums; keeping validator free of stale lists. /// Validates timestamp is reasonable (not too far in past or future) + /// BCH-01-011: Reduced from ±1 hour to ±5 minutes to limit replay attack window static func validateTimestamp(_ timestamp: Date) -> Bool { let now = Date() - let oneHourAgo = now.addingTimeInterval(-3600) - let oneHourFromNow = now.addingTimeInterval(3600) - return timestamp >= oneHourAgo && timestamp <= oneHourFromNow + // 5 minutes = 300 seconds (industry standard for replay protection) + let fiveMinutesAgo = now.addingTimeInterval(-300) + let fiveMinutesFromNow = now.addingTimeInterval(300) + return timestamp >= fiveMinutesAgo && timestamp <= fiveMinutesFromNow } } diff --git a/bitchatTests/InputValidatorTests.swift b/bitchatTests/InputValidatorTests.swift index 021d454a..0611fdc0 100644 --- a/bitchatTests/InputValidatorTests.swift +++ b/bitchatTests/InputValidatorTests.swift @@ -131,6 +131,7 @@ struct InputValidatorTests { } // MARK: - Timestamp Validation Tests + // BCH-01-011: Window reduced from ±1 hour to ±5 minutes @Test func currentTimestampIsValid() throws { let now = Date() @@ -138,31 +139,48 @@ struct InputValidatorTests { #expect(result == true) } - @Test func timestampWithinOneHourIsValid() throws { + @Test func timestampWithinFiveMinutesIsValid() throws { + // 2 minutes ago should be valid (within 5-minute window) + let twoMinutesAgo = Date().addingTimeInterval(-2 * 60) + let result = InputValidator.validateTimestamp(twoMinutesAgo) + #expect(result == true) + } + + @Test func timestampThirtyMinutesAgoIsInvalid() throws { + // BCH-01-011: 30 minutes is now outside the 5-minute window let thirtyMinutesAgo = Date().addingTimeInterval(-30 * 60) let result = InputValidator.validateTimestamp(thirtyMinutesAgo) - #expect(result == true) - } - - @Test func timestampTwoHoursAgoIsInvalid() throws { - let twoHoursAgo = Date().addingTimeInterval(-2 * 3600) - let result = InputValidator.validateTimestamp(twoHoursAgo) #expect(result == false) } - @Test func timestampTwoHoursInFutureIsInvalid() throws { - let twoHoursFromNow = Date().addingTimeInterval(2 * 3600) - let result = InputValidator.validateTimestamp(twoHoursFromNow) + @Test func timestampTenMinutesAgoIsInvalid() throws { + // 10 minutes is outside the 5-minute window + let tenMinutesAgo = Date().addingTimeInterval(-10 * 60) + let result = InputValidator.validateTimestamp(tenMinutesAgo) #expect(result == false) } - @Test func timestampAtOneHourBoundaryIsValid() throws { - // Just slightly within the one-hour window - let almostOneHourAgo = Date().addingTimeInterval(-3599) - let result = InputValidator.validateTimestamp(almostOneHourAgo) + @Test func timestampTenMinutesInFutureIsInvalid() throws { + // 10 minutes in future is outside the 5-minute window + let tenMinutesFromNow = Date().addingTimeInterval(10 * 60) + let result = InputValidator.validateTimestamp(tenMinutesFromNow) + #expect(result == false) + } + + @Test func timestampAtFiveMinuteBoundaryIsValid() throws { + // Just slightly within the five-minute window (299 seconds) + let almostFiveMinutesAgo = Date().addingTimeInterval(-299) + let result = InputValidator.validateTimestamp(almostFiveMinutesAgo) #expect(result == true) } + @Test func timestampJustOutsideFiveMinuteWindowIsInvalid() throws { + // Just outside the five-minute window (301 seconds) + let justOverFiveMinutesAgo = Date().addingTimeInterval(-301) + let result = InputValidator.validateTimestamp(justOverFiveMinutesAgo) + #expect(result == false) + } + // MARK: - Edge Cases @Test func singleCharacterStringIsAccepted() throws {