Skip to content

Commit

Permalink
Added bcrypt support
Browse files Browse the repository at this point in the history
Signed-off-by: Athish Pranav D <[email protected]>
  • Loading branch information
Athishpranav2003 committed Oct 29, 2024
1 parent e486663 commit f0e6849
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 10 deletions.
11 changes: 11 additions & 0 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ REPOSITORY_LOCATIONS_SPEC = dict(
release_date = "2022-06-13",
cpe = "cpe:2.3:a:google:boringssl:*",
),
com_github_bcrypt = dict(
project_name = "bcrypt",
project_desc = " C++ wrapper around bcrypt password hashing",
project_url = "https://github.com/hilch/Bcrypt.cpp",
version = "V2.0_NODEBCRYPT",
sha256 = "150abb95058a459e58f2bf995fd85af1d616374cc920d1ebb84666a8b2b3c50c",
urls = ["https://github.com/hilch/Bcrypt.cpp/archive/refs/tags/{version}.tar.gz"],
use_category = ["controlplane", "dataplane_core"],
release_date = "2021-12-24",
cpe = "N/A",
),
aspect_bazel_lib = dict(
project_name = "Aspect Bazel helpers",
project_desc = "Base Starlark libraries and basic Bazel rules which are useful for constructing rulesets and BUILD files",
Expand Down
2 changes: 1 addition & 1 deletion source/extensions/filters/http/basic_auth/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ envoy_cc_library(
name = "basic_auth_lib",
srcs = ["basic_auth_filter.cc"],
hdrs = ["basic_auth_filter.h"],
external_deps = ["ssl"],
external_deps = ["ssl", "bcrypt"],
deps = [
"//envoy/server:filter_config_interface",
"//source/common/common:base64_lib",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "source/common/http/header_utility.h"
#include "source/common/http/headers.h"
#include "source/common/http/utility.h"
#include <bcrypt/BCrypt.hpp>

namespace Envoy {
namespace Extensions {
Expand Down Expand Up @@ -101,7 +102,12 @@ bool BasicAuthFilter::validateUser(const UserMap& users, absl::string_view usern
return false;
}

return computeSHA1(password) == user->second.hash;
if (absl::StartsWith(user->second.hash, "{SHA}")) {
return user->second.hash == absl::StrCat("{SHA}", computeSHA1(password));
}

std::string password_string{password.data(), password.length()};
return BCrypt::validatePassword(password_string, user->second.hash);
}

Http::FilterHeadersStatus BasicAuthFilter::onDenied(absl::string_view body,
Expand Down
19 changes: 13 additions & 6 deletions source/extensions/filters/http/basic_auth/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,21 @@ UserMap readHtpasswd(const std::string& htpasswd) {
throw EnvoyException("basic auth: duplicate users");
}

if (!absl::StartsWith(hash, "{SHA}")) {
throw EnvoyException("basic auth: unsupported htpasswd format: please use {SHA}");
if (absl::StartsWith(hash, "{SHA}")) {
// The base64 encoded SHA1 hash is 28 bytes long and prefix is 5
if (hash.length() != 5+28) {
throw EnvoyException("basic auth: invalid htpasswd format, invalid SHA hash length");
}
}

hash = hash.substr(5);
// The base64 encoded SHA1 hash is 28 bytes long
if (hash.length() != 28) {
throw EnvoyException("basic auth: invalid htpasswd format, invalid SHA hash length");
else if (absl::StartsWith(hash, "$2b$") || absl::StartsWith(hash, "$2y$") || absl::StartsWith(hash, "$2x$") || absl::StartsWith(hash, "$2a$")) {
if (hash.length() != 60) {
throw EnvoyException("basic auth: invalid htpasswd format, invalid bcrypt hash length");
}
}

else {
throw EnvoyException("basic auth: unsupported htpasswd format: invalid hash type");
}

users.insert({name, {name, hash}});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ name: envoy.filters.http.basic_auth
inline_string: |-
user1:{SHA}tESsBmE/yNY3lb6a0L6vVQEZNqw=
user2:{SHA}EJ9LPFDXsN9ynSmbxvjp75Bmlx8=
user3:$2y$05$br5Mb9Dvx3tjWSd9imKvfOBTP9EycYIRTzpDkrMMuZS7y8G1P7p8O
forward_username_header: x-username
)EOF";

Expand Down Expand Up @@ -89,7 +90,7 @@ INSTANTIATE_TEST_SUITE_P(
testing::ValuesIn(HttpProtocolIntegrationTest::getProtocolTestParamsWithoutHTTP3()),
HttpProtocolIntegrationTest::protocolTestParamsToString);

// Request with valid credential
// Request with valid credential with SHA1 hash
TEST_P(BasicAuthIntegrationTestAllProtocols, ValidCredential) {
initializeFilter();
codec_client_ = makeHttpConnection(lookupPort("http"));
Expand All @@ -114,6 +115,31 @@ TEST_P(BasicAuthIntegrationTestAllProtocols, ValidCredential) {
EXPECT_EQ("200", response->headers().getStatusValue());
}

// Request with valid credential with bcrypt hash
TEST_P(BasicAuthIntegrationTestAllProtocols, ValidCredential) {
initializeFilter();
codec_client_ = makeHttpConnection(lookupPort("http"));

auto response = codec_client_->makeHeaderOnlyRequest(Http::TestRequestHeaderMapImpl{
{":method", "GET"},
{":path", "/"},
{":scheme", "http"},
{":authority", "host"},
{"Authorization", "Basic dXNlcjM6dGVzdDM="}, // user3, test3
});

waitForNextUpstreamRequest();

const auto username_entry = upstream_request_->headers().get(Http::LowerCaseString("x-username"));
EXPECT_FALSE(username_entry.empty());
EXPECT_EQ(username_entry[0]->value().getStringView(), "user3");

upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, true);
ASSERT_TRUE(response->waitForEndStream());
ASSERT_TRUE(response->complete());
EXPECT_EQ("200", response->headers().getStatusValue());
}

// Request without credential
TEST_P(BasicAuthIntegrationTestAllProtocols, NoCredential) {
initializeFilter();
Expand Down Expand Up @@ -167,7 +193,7 @@ TEST_P(BasicAuthIntegrationTestAllProtocols, NoneExistedUser) {
{":path", "/"},
{":scheme", "http"},
{":authority", "host"},
{"Authorization", "Basic dXNlcjM6dGVzdDI="}, // user3, test2
{"Authorization", "Basic dXNlcjQ6dGVzdDI="}, // user4, test2
});

ASSERT_TRUE(response->waitForEndStream());
Expand Down

0 comments on commit f0e6849

Please sign in to comment.