@@ -202,80 +202,61 @@ def self.valid_timestamp?(headers, config)
202
202
return false if timestamp_value . strip . empty?
203
203
204
204
parsed_timestamp = parse_timestamp ( timestamp_value . strip )
205
- return false unless parsed_timestamp
205
+ return false unless parsed_timestamp . is_a? ( Integer )
206
206
207
- # parsed_timestamp is a Time object
208
- now = Time . now . utc
207
+ now = Time . now . utc . to_i
209
208
( now - parsed_timestamp ) . abs <= tolerance
210
209
end
211
210
212
211
# Parse timestamp value supporting both ISO 8601 UTC and Unix formats
213
- #
214
- # Attempts to parse the timestamp in the following order:
215
- # 1. ISO 8601 UTC format (e.g., "2025-06-12T10:30:00Z")
216
- # 2. Unix timestamp (e.g., "1609459200")
217
- #
218
- # @param timestamp_value [String] The timestamp string to parse
219
- # @return [Time, nil] Time object if parsing succeeds, nil otherwise
220
- # @note Security: Strict validation prevents various injection attacks
221
- # @api private
222
212
def self . parse_timestamp ( timestamp_value )
223
- # Try ISO 8601 first, then Unix
213
+ # Reject if contains any control characters, whitespace, or null bytes
214
+ if timestamp_value =~ /[\u0000 -\u001F \u007F -\u009F ]/
215
+ log . warn ( "Auth::HMAC validation failed: Timestamp contains invalid characters" )
216
+ return nil
217
+ end
218
+ if timestamp_value != timestamp_value . strip
219
+ log . warn ( "Auth::HMAC validation failed: Timestamp contains leading/trailing whitespace" )
220
+ return nil
221
+ end
224
222
ts = parse_iso8601_timestamp ( timestamp_value )
225
223
return ts if ts
226
224
ts = parse_unix_timestamp ( timestamp_value )
227
225
return ts if ts
228
- nil
226
+
227
+ # If neither format matches, return nil
228
+ log . warn ( "Auth::HMAC validation failed: Timestamp (#{ timestamp_value } ) is not valid ISO 8601 UTC or Unix format" )
229
+ return nil
229
230
end
230
231
231
- # Check if timestamp string looks like ISO 8601 UTC format
232
- #
233
- # @param timestamp_value [String] The timestamp string to check
234
- # @return [Boolean] true if it appears to be ISO 8601 format
235
- # @api private
232
+ # Check if timestamp string looks like ISO 8601 UTC format (must have UTC indicator)
236
233
def self . iso8601_timestamp? ( timestamp_value )
237
- # Accepts Z, +00:00, or +0000, and T or space as separator
238
- !!( timestamp_value =~ /\A \d {4}-\d {2}-\d {2}[T ]\d {2}:\d {2}:\d {2}(?:\. \d +)?(Z|\+ 00:00|\+ 0000)\z / )
234
+ !!( timestamp_value =~ /\A \d {4}-\d {2}-\d {2}[T ]\d {2}:\d {2}:\d {2}(?:\. \d +)?(Z|\+ 00:00|\+ 0000)?\z / )
239
235
end
240
236
241
- # Parse ISO 8601 UTC timestamp string
242
- #
243
- # @param timestamp_value [String] ISO 8601 timestamp string
244
- # @return [Time, nil] Time object if parsing succeeds, nil otherwise
245
- # @note Only accepts UTC timestamps (ending with 'Z' or explicit UTC)
246
- # @api private
237
+ # Parse ISO 8601 UTC timestamp string (must have UTC indicator)
247
238
def self . parse_iso8601_timestamp ( timestamp_value )
248
- # Normalize 'YYYY-MM-DD HH:MM:SS(.fraction)? +0000' to 'YYYY-MM-DDTHH:MM:SS(.fraction)?+00:00'
249
239
if timestamp_value =~ /\A (\d {4}-\d {2}-\d {2}) (\d {2}:\d {2}:\d {2}(?:\. \d +)?)(?: )\+ 0000\z /
250
240
timestamp_value = "#{ $1} T#{ $2} +00:00"
251
241
end
252
242
return nil unless iso8601_timestamp? ( timestamp_value )
253
- t = Time . iso8601 ( timestamp_value ) rescue nil
243
+ t = Time . parse ( timestamp_value ) rescue nil
254
244
return nil unless t
255
- # Only accept UTC (Z, +00:00, or +0000)
256
- return t if t . utc? || t . utc_offset == 0
257
- nil
245
+ ( t . utc? || t . utc_offset == 0 ) ? t . to_i : nil
258
246
end
259
247
260
- # Parse Unix timestamp string
261
- #
262
- # @param timestamp_value [String] Unix timestamp string
263
- # @return [Time, nil] Time object if parsing succeeds, nil otherwise
264
- # @api private
248
+ # Parse Unix timestamp string (must be positive integer, no leading zeros except for "0")
265
249
def self . parse_unix_timestamp ( timestamp_value )
266
250
return nil unless unix_timestamp? ( timestamp_value )
267
251
ts = timestamp_value . to_i
268
252
return nil if ts <= 0
269
- Time . at ( ts ) . utc
253
+ ts
270
254
end
271
255
272
- # Check if timestamp string looks like Unix timestamp format
273
- #
274
- # @param timestamp_value [String] The timestamp string to check
275
- # @return [Boolean] true if it appears to be Unix timestamp format
276
- # @api private
256
+ # Check if timestamp string looks like Unix timestamp format (no leading zeros except "0")
277
257
def self . unix_timestamp? ( timestamp_value )
278
- !!( timestamp_value =~ /\A \d +\z / ) || timestamp_value == "0"
258
+ return true if timestamp_value == "0"
259
+ !!( timestamp_value =~ /\A [1-9]\d *\z / )
279
260
end
280
261
281
262
# Compute HMAC signature based on configuration requirements
0 commit comments