aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/security/passwordsecurity.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenhttp/security/passwordsecurity.cpp')
-rw-r--r--src/zenhttp/security/passwordsecurity.cpp18
1 files changed, 17 insertions, 1 deletions
diff --git a/src/zenhttp/security/passwordsecurity.cpp b/src/zenhttp/security/passwordsecurity.cpp
index 0e3a743c3..70315fa03 100644
--- a/src/zenhttp/security/passwordsecurity.cpp
+++ b/src/zenhttp/security/passwordsecurity.cpp
@@ -67,7 +67,10 @@ PasswordSecurity::IsAllowed(std::string_view InPassword, std::string_view BaseUr
{
return true;
}
- if (Password() == InPassword)
+ // Constant-time compare: a plain == short-circuits on the first byte mismatch
+ // (and on length mismatch before any bytes are inspected), exposing the
+ // configured password to a remote timing oracle across the network.
+ if (ConstantTimeEquals(Password(), InPassword))
{
return true;
}
@@ -148,6 +151,19 @@ TEST_CASE("passwordsecurity.allowsomelocaluris")
CHECK(AllLocal.IsAllowed("123456"sv, "/supersecret", "/uri", /*IsMachineLocalRequest*/ false));
}
+TEST_CASE("passwordsecurity.constanttimeequals")
+{
+ // Documents the contract of the helper used to compare passwords. We
+ // cannot observe timing from here, but we can at least verify equality
+ // and inequality of the three interesting shapes: same length equal,
+ // same length unequal, and length mismatch.
+ CHECK(ConstantTimeEquals("abcdef"sv, "abcdef"sv));
+ CHECK_FALSE(ConstantTimeEquals("abcdef"sv, "abcdeg"sv));
+ CHECK_FALSE(ConstantTimeEquals("abcdef"sv, "abcdefg"sv));
+ CHECK_FALSE(ConstantTimeEquals(""sv, "x"sv));
+ CHECK(ConstantTimeEquals(""sv, ""sv));
+}
+
TEST_CASE("passwordsecurity.conflictingunprotecteduris")
{
try