-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
The token_is_current? method in DeviseTokenAuth::Concerns::User module contains a security vulnerability that allows previous tokens to remain valid indefinitely, effectively bypassing the batch request buffer throttle setting.
Current Implementation
In the current implementation, token_is_current? checks if the token matches either the current token OR the previous token without any time window restriction for the previous token:
def token_is_current?(token, client)
# ...
return true if (
# ensure that expiry and token are set
expiry && token &&
# ensure that the token has not yet expired
DateTime.strptime(expiry.to_s, '%s') > Time.zone.now &&
# ensure that the token is valid
(
# check if the latest token matches
does_token_match?(token_hash, token) ||
# check if the previous token matches
does_token_match?(previous_token_hash, token)
)
)
endWhile there is a separate token_can_be_reused? method that properly enforces the batch window throttle:
def token_can_be_reused?(token, client)
# ...
result = (
updated_at && last_token_hash &&
Time.zone.now - updated_at.to_time < DeviseTokenAuth.batch_request_buffer_throttle &&
does_token_match?(last_token_hash, token)
)
# ...
endSecurity Problem
This implementation creates a situation where:
The previous_token remains valid indefinitely without any time window restriction
The batch_request_buffer_throttle setting is effectively bypassed for previous tokens
Token rotation security is undermined since old tokens remain valid forever
How to Reproduce
# create first token
first_token = User.first.create_new_auth_headers('test')
# create new token
User.first.create_new_auth_headers('test')
# Observe that the credentials from first_token are valid