Bind forward-auth fa_token to its destination host

An observed fa_token (via Referer leaks, access logs, JS monitors)
could previously be redeemed against a different reverse-proxied app
within the 60s TTL. The token now stores the destination host at
creation and the verifier rejects mismatches without burning the cache
entry, so legitimate destinations can still redeem.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
Dan Milne
2026-04-20 19:04:53 +10:00
parent 7f0d3d3900
commit 2d5650e620
3 changed files with 168 additions and 28 deletions

View File

@@ -163,16 +163,25 @@ module Api
def check_forward_auth_token
token = params[:fa_token]
return nil unless token.present?
return nil if token.blank?
session_id = Rails.cache.read("forward_auth_token:#{token}")
return nil unless session_id
cached = Rails.cache.read("forward_auth_token:#{token}")
return nil unless cached.is_a?(Hash)
session = Session.find_by(id: session_id)
# The token is bound to the host that created it. If the request is
# arriving at a different host, refuse — and do NOT burn the cache
# entry, so that the legitimate destination can still redeem within
# the 60s TTL.
request_host = (request.headers["X-Forwarded-Host"] || request.headers["Host"])
.to_s.sub(/:\d+\z/, "").downcase
return nil if request_host.blank?
return nil unless cached[:host] == request_host
session = Session.find_by(id: cached[:session_id])
return nil unless session && !session.expired?
Rails.cache.delete("forward_auth_token:#{token}")
session_id
cached[:session_id]
end
def extract_session_id