diff options
Diffstat (limited to 'src/zenhttp/security/passwordsecurity.cpp')
| -rw-r--r-- | src/zenhttp/security/passwordsecurity.cpp | 18 |
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 |