Skip to content

Commit 30e89e0

Browse files
authored
impl: censor API key header in traces (#14755)
1 parent 1289fa6 commit 30e89e0

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

google/cloud/internal/tracing_rest_client.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3333

3434
namespace {
3535

36+
/**
37+
* The number of characters to print in an [API key].
38+
*
39+
* API keys are 39 characters in length. The value is a secret, so we do not
40+
* want to include the entire key in our telemetry.
41+
*
42+
* Providing some number of characters allows applications to confirm the
43+
* correct API key is in use, given that the full API key is known.
44+
*
45+
* [API key]: https://cloud.google.com/docs/authentication/api-keys-use
46+
*/
47+
auto constexpr kApiKeyHintLength = 12;
48+
3649
/**
3750
* Extracts information from @p value, and adds it to a span.
3851
*
@@ -68,6 +81,11 @@ StatusOr<std::unique_ptr<RestResponse>> EndResponseSpan(
6881
span->SetAttribute(name, kv.second.front().substr(0, 32));
6982
continue;
7083
}
84+
if (absl::EqualsIgnoreCase(kv.first, "x-goog-api-key")) {
85+
span->SetAttribute(
86+
name, kv.second.front().substr(0, kApiKeyHintLength) + "...");
87+
continue;
88+
}
7189
span->SetAttribute(name, kv.second.front());
7290
}
7391
if (!request_result || !(*request_result)) {

google/cloud/internal/tracing_rest_client_test.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,48 @@ TEST(TracingRestClient, WithRestContextDetails) {
278278
EventNamed("gl-cpp.curl.ssl.handshake")))));
279279
}
280280

281+
TEST(TracingRestClient, CensorsAuthFields) {
282+
auto span_catcher = InstallSpanCatcher();
283+
284+
auto impl = std::make_unique<MockRestClient>();
285+
EXPECT_CALL(*impl, Delete).WillOnce([](RestContext&, RestRequest const&) {
286+
auto response = std::make_unique<MockRestResponse>();
287+
EXPECT_CALL(*response, StatusCode)
288+
.WillRepeatedly(Return(HttpStatusCode::kOk));
289+
EXPECT_CALL(*response, Headers).WillRepeatedly(Return(MockHeaders()));
290+
EXPECT_CALL(std::move(*response), ExtractPayload).WillOnce([] {
291+
return MakeMockHttpPayloadSuccess(MockContents());
292+
});
293+
return std::unique_ptr<RestResponse>(std::move(response));
294+
});
295+
296+
auto constexpr kUrl = "https://storage.googleapis.com/storage/v1/b/my-bucket";
297+
RestRequest request(kUrl);
298+
299+
auto client = MakeTracingRestClient(std::move(impl));
300+
rest_internal::RestContext context;
301+
context.AddHeader("authorization", "bearer: ABCDEFGHIJKLMNOPQRSTUVWXYZ");
302+
context.AddHeader("x-goog-api-key", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
303+
304+
auto r = client->Delete(context, request);
305+
ASSERT_STATUS_OK(r);
306+
auto response = *std::move(r);
307+
ASSERT_THAT(response, NotNull());
308+
EXPECT_THAT(response->StatusCode(), Eq(HttpStatusCode::kOk));
309+
EXPECT_THAT(response->Headers(), ElementsAreArray(MockHeaders()));
310+
auto contents = ReadAll(std::move(*response).ExtractPayload());
311+
EXPECT_THAT(contents, IsOkAndHolds(MockContents()));
312+
313+
auto spans = span_catcher->GetSpans();
314+
EXPECT_THAT(
315+
spans,
316+
Contains(SpanHasAttributes(
317+
OTelAttribute<std::string>("http.request.header.authorization",
318+
"bearer: ABCDEFGHIJKLMNOPQRSTUVWX"),
319+
OTelAttribute<std::string>("http.request.header.x-goog-api-key",
320+
"ABCDEFGHIJKL..."))));
321+
}
322+
281323
TEST(TracingRestClient, CachedConnection) {
282324
auto span_catcher = InstallSpanCatcher();
283325

0 commit comments

Comments
 (0)