diff options
context:
space:
mode:
Diffstat (limited to 'extensions/auth-file.lua')
-rw-r--r--extensions/auth-file.lua36
1 file changed, 31 insertions, 5 deletions
diff --git a/extensions/auth-file.lua b/extensions/auth-file.lua
index 1f435c6..71c8bc6 100644
--- a/extensions/auth-file.lua
+++ b/extensions/auth-file.lua
@@ -322,8 +322,8 @@ function validate_value(expected_field, cookie)
return nil
end
- -- Lua hashes strings, so these comparisons are time invariant.
- if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
+ -- Compare the HMAC without short-circuiting on the first mismatch.
+ if not constant_equals(chmac, tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt))) then
return nil
end
@@ -335,7 +335,13 @@ function validate_value(expected_field, cookie)
return nil
end
- return url_decode(value)
+ local decoded = url_decode(value)
+ -- Reject values carrying control characters so a signed cookie or
+ -- redirect target cannot smuggle CR or LF into a response header.
+ if decoded:find("%c") then
+ return nil
+ end
+ return decoded
end
function secure_value(field, value, expiration)
@@ -352,8 +358,28 @@ function secure_value(field, value, expiration)
return authstr
end
+-- Strip control characters that could split an HTTP response header.
+function strip_ctl(s)
+ return (string.gsub(s or "", "%c", ""))
+end
+
+-- Compare two strings without stopping at the first mismatch, so the time
+-- taken does not reveal how many leading bytes already matched.
+function constant_equals(a, b)
+ if a == nil or b == nil or #a ~= #b then
+ return false
+ end
+ local diff = 0
+ for i = 1, #a do
+ if a:byte(i) ~= b:byte(i) then
+ diff = diff + 1
+ end
+ end
+ return diff == 0
+end
+
function set_cookie(cookie, value)
- html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
+ html("Set-Cookie: " .. cookie .. "=" .. strip_ctl(value) .. "; HttpOnly; SameSite=Lax")
if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
html("; secure")
end
@@ -363,7 +389,7 @@ end
function redirect_to(url)
html("Status: 302 Redirect\n")
html("Cache-Control: no-cache, no-store\n")
- html("Location: " .. url .. "\n")
+ html("Location: " .. strip_ctl(url) .. "\n")
end
function not_found()