// Copyright Epic Games, Inc. All Rights Reserved. #include "zenhttp/security/passwordsecurityfilter.h" #include #include #include namespace zen { using namespace std::literals; PasswordHttpFilter::Configuration PasswordHttpFilter::ReadConfiguration(CbObjectView Config) { Configuration Result; if (CbObjectView PasswordType = Config["basic"sv].AsObjectView(); PasswordType) { Result.AuthenticationTypeString = "Basic "; std::string_view Username = PasswordType["username"sv].AsString(); std::string_view Password = PasswordType["password"sv].AsString(); std::string UsernamePassword = fmt::format("{}:{}", Username, Password); Result.PasswordConfig.Password.resize(Base64::GetEncodedDataSize(uint32_t(UsernamePassword.length()))); Base64::Encode(reinterpret_cast(UsernamePassword.data()), uint32_t(UsernamePassword.size()), const_cast(Result.PasswordConfig.Password.data())); } Result.PasswordConfig.ProtectMachineLocalRequests = Config["protect-machine-local-requests"sv].AsBool(); Result.PasswordConfig.UnprotectedUris = compactbinary_helpers::ReadArray("unprotected-uris"sv, Config); return Result; } IHttpRequestFilter::Result PasswordHttpFilter::FilterRequest(HttpServerRequest& Request) { std::string_view Password; std::string_view AuthorizationHeader = Request.GetAuthorizationHeader(); // Look for the configured scheme prefix (e.g. "Basic ") case-insensitively. // Only extract a candidate credential when the scheme prefix matches and the // remainder passes two defense-in-depth checks: // * trim any trailing LWS (SP / HTAB) from the credential, so a // well-behaved but "Authorization: Basic " style header still // works instead of producing a silent 403; // * reject the header outright if the credential contains any control // byte (< 0x20) or DEL (0x7F) — valid Base64 never does, and passing // such bytes downstream is a log-injection / smuggling vector. if (AuthorizationHeader.length() > m_AuthenticationTypeString.length() && StrCaseCompare(AuthorizationHeader.data(), m_AuthenticationTypeString.c_str(), m_AuthenticationTypeString.length()) == 0) { std::string_view Candidate = AuthorizationHeader.substr(m_AuthenticationTypeString.length()); while (!Candidate.empty() && (Candidate.back() == ' ' || Candidate.back() == '\t')) { Candidate.remove_suffix(1); } bool HasControlByte = false; for (char C : Candidate) { const uint8_t B = static_cast(C); if (B < 0x20 || B == 0x7F) { HasControlByte = true; break; } } if (!HasControlByte) { Password = Candidate; } } bool IsAllowed = m_PasswordSecurity.IsAllowed(Password, Request.Service().BaseUri(), Request.RelativeUri(), Request.IsLocalMachineRequest()); if (IsAllowed) { return Result::Accepted; } return Result::Forbidden; } } // namespace zen