admin: constant-time credential comparison

This commit is contained in:
Sergei Poljanski 2026-07-02 17:31:19 +04:00
commit fb62176358
Signed by: asxpi
GPG key ID: 4F8851660FA4121B

View file

@ -1,4 +1,5 @@
require 'rack/auth/basic'
require 'rack/utils'
# Rack middleware that gates everything under /admin/* behind HTTP Basic.
# Credentials come from ENV at boot; missing vars fail closed (401 always).
@ -14,7 +15,7 @@ class AdminAuth
return @app.call(env) unless path == '/admin' || path.start_with?('/admin/')
auth = Rack::Auth::Basic::Request.new(env)
if @user && @pass && auth.provided? && auth.basic? && auth.credentials == [@user, @pass]
if @user && @pass && auth.provided? && auth.basic? && credentials_match?(auth.credentials)
@app.call(env)
else
[401,
@ -22,4 +23,13 @@ class AdminAuth
["Unauthorized\n"]]
end
end
private
def credentials_match?(creds)
user, pass = creds
# Single & so both comparisons always run (no short-circuit timing signal).
Rack::Utils.secure_compare(@user, user.to_s) &
Rack::Utils.secure_compare(@pass, pass.to_s)
end
end