Skip to content

Commit

Permalink
Redact query string values
Browse files Browse the repository at this point in the history
  • Loading branch information
jeanbisutti committed Jan 27, 2025
1 parent aa86281 commit aba2207
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import io.opentelemetry.instrumentation.api.semconv.network.internal.InternalServerAttributesExtractor;
import io.opentelemetry.semconv.HttpAttributes;
import io.opentelemetry.semconv.UrlAttributes;
import java.util.Arrays;
import java.util.List;
import java.util.function.ToIntFunction;
import javax.annotation.Nullable;

Expand All @@ -32,6 +34,9 @@ public final class HttpClientAttributesExtractor<REQUEST, RESPONSE>
REQUEST, RESPONSE, HttpClientAttributesGetter<REQUEST, RESPONSE>>
implements SpanKeyProvider {

private static final List<String> PARAMS_TO_REDACT =
Arrays.asList("AWSAccessKeyId", "Signature", "sig", "X-Goog-Signature");

/**
* Creates the HTTP client attributes extractor with default configuration.
*
Expand All @@ -54,6 +59,7 @@ public static <REQUEST, RESPONSE> HttpClientAttributesExtractorBuilder<REQUEST,
private final InternalNetworkAttributesExtractor<REQUEST, RESPONSE> internalNetworkExtractor;
private final InternalServerAttributesExtractor<REQUEST> internalServerExtractor;
private final ToIntFunction<Context> resendCountIncrementer;
private final boolean redactSensitiveParameters;

HttpClientAttributesExtractor(HttpClientAttributesExtractorBuilder<REQUEST, RESPONSE> builder) {
super(
Expand All @@ -65,6 +71,9 @@ public static <REQUEST, RESPONSE> HttpClientAttributesExtractorBuilder<REQUEST,
internalNetworkExtractor = builder.buildNetworkExtractor();
internalServerExtractor = builder.buildServerExtractor();
resendCountIncrementer = builder.resendCountIncrementer;
redactSensitiveParameters =
Boolean.getBoolean(
"otel.instrumentation.http.client.experimental.redact-sensitive-parameters");
}

@Override
Expand Down Expand Up @@ -104,7 +113,7 @@ public SpanKey internalGetSpanKey() {
}

@Nullable
private static String stripSensitiveData(@Nullable String url) {
private String stripSensitiveData(@Nullable String url) {
if (url == null || url.isEmpty()) {
return url;
}
Expand Down Expand Up @@ -141,8 +150,69 @@ private static String stripSensitiveData(@Nullable String url) {
}

if (atIndex == -1 || atIndex == len - 1) {
return url;
return redactSensitiveParameters ? redactUrlParameters(url) : url;
}

String afterUserPwdRedaction = url.substring(atIndex);
return url.substring(0, schemeEndIndex + 3)
+ "REDACTED:REDACTED"
+ (redactSensitiveParameters
? redactUrlParameters(afterUserPwdRedaction)
: afterUserPwdRedaction);
}

public static String redactUrlParameters(String urlpart) {

int questionMarkIndex = urlpart.indexOf('?');

if (questionMarkIndex == -1) {
return urlpart;
}

if (!containsParamToRedact(urlpart)) {
return urlpart;
}

StringBuilder redactedParameters = new StringBuilder();
boolean paramToRedact = false;
boolean paramNameDetected = false;
boolean reference = false;

StringBuilder currentParamName = new StringBuilder();

for (int i = questionMarkIndex + 1; i < urlpart.length(); i++) {
char currentChar = urlpart.charAt(i);
if (currentChar == '=') {
paramNameDetected = true;
redactedParameters.append(currentParamName);
redactedParameters.append('=');
if (PARAMS_TO_REDACT.contains(currentParamName.toString())) {
redactedParameters.append("REDACTED");
paramToRedact = true;
}
} else if (currentChar == '&') {
redactedParameters.append('&');
paramNameDetected = false;
paramToRedact = false;
currentParamName.setLength(0);
} else if (currentChar == '#') {
reference = true;
redactedParameters.append('#');
} else if (!paramNameDetected) {
currentParamName.append(currentChar);
} else if (!paramToRedact || reference) {
redactedParameters.append(currentChar);
}
}
return urlpart.substring(0, questionMarkIndex) + "?" + redactedParameters;
}

private static boolean containsParamToRedact(String urlpart) {
for (String param : PARAMS_TO_REDACT) {
if (urlpart.contains(param)) {
return true;
}
}
return url.substring(0, schemeEndIndex + 3) + "REDACTED:REDACTED" + url.substring(atIndex);
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.entry;
import static org.junit.jupiter.params.provider.Arguments.arguments;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
Expand All @@ -36,9 +37,13 @@
import java.util.List;
import java.util.Map;
import java.util.function.ToIntFunction;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.jupiter.params.provider.ValueSource;

Expand Down Expand Up @@ -200,6 +205,78 @@ void normal() {
entry(NETWORK_PEER_PORT, 456L));
}

@ParameterizedTest
@ArgumentsSource(StripUrlArgumentSource.class)
void stripBasicAuthTest(String url, String expectedResult) {
Map<String, String> request = new HashMap<>();
request.put("urlFull", url);

System.setProperty(
"otel.instrumentation.http.client.experimental.redact-sensitive-parameters", "true");
AttributesExtractor<Map<String, String>, Map<String, String>> extractor =
HttpClientAttributesExtractor.create(new TestHttpClientAttributesGetter());

AttributesBuilder attributes = Attributes.builder();
extractor.onStart(attributes, Context.root(), request);

assertThat(attributes.build()).containsOnly(entry(URL_FULL, expectedResult));
}

static final class StripUrlArgumentSource implements ArgumentsProvider {

@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
arguments("https://user1:[email protected]", "https://REDACTED:[email protected]"),
arguments(
"https://user1:[email protected]/path/",
"https://REDACTED:[email protected]/path/"),
arguments(
"https://user1:[email protected]#test.html",
"https://REDACTED:[email protected]#test.html"),
arguments(
"https://user1:[email protected]?foo=b@r",
"https://REDACTED:[email protected]?foo=b@r"),
arguments(
"https://user1:[email protected]/p@th?foo=b@r",
"https://REDACTED:[email protected]/p@th?foo=b@r"),
arguments("https://github.com/p@th?foo=b@r", "https://github.com/p@th?foo=b@r"),
arguments("https://github.com#[email protected]", "https://github.com#[email protected]"),
arguments("user1:[email protected]", "user1:[email protected]"),
arguments("https://github.com@", "https://github.com@"),
arguments(
"https://service.com?paramA=valA&paramB=valB",
"https://service.com?paramA=valA&paramB=valB"),
arguments(
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7",
"https://service.com?AWSAccessKeyId=REDACTED"),
arguments(
"https://service.com?Signature=39Up9jzHkxhuIhFE9594DJxe7w6cIRCg0V6ICGS0%3A377",
"https://service.com?Signature=REDACTED"),
arguments(
"https://service.com?sig=39Up9jzHkxhuIhFE9594DJxe7w6cIRCg0V6ICGS0",
"https://service.com?sig=REDACTED"),
arguments(
"https://service.com?X-Goog-Signature=39Up9jzHkxhuIhFE9594DJxe7w6cIRCg0V6ICGS0",
"https://service.com?X-Goog-Signature=REDACTED"),
arguments(
"https://service.com?paramA=valA&AWSAccessKeyId=AKIAIOSFODNN7&paramB=valB",
"https://service.com?paramA=valA&AWSAccessKeyId=REDACTED&paramB=valB"),
arguments(
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7&paramA=valA",
"https://service.com?AWSAccessKeyId=REDACTED&paramA=valA"),
arguments(
"https://service.com?paramA=valA&AWSAccessKeyId=AKIAIOSFODNN7",
"https://service.com?paramA=valA&AWSAccessKeyId=REDACTED"),
arguments(
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7&AWSAccessKeyId=ZGIAIOSFODNN7",
"https://service.com?AWSAccessKeyId=REDACTED&AWSAccessKeyId=REDACTED"),
arguments(
"https://service.com?AWSAccessKeyId=AKIAIOSFODNN7#ref",
"https://service.com?AWSAccessKeyId=REDACTED#ref"));
}
}

@ParameterizedTest
@ArgumentsSource(ValidRequestMethodsProvider.class)
void shouldExtractKnownMethods(String requestMethod) {
Expand Down

0 comments on commit aba2207

Please sign in to comment.