go: proposal: net/http.Client should allow omitting URLs from error strings
Context
When using a net/http.Client, especially with a context.Context that has a deadline, it’s not uncommon for the http.Client.Do() method to return an error that contains the full URL used when actioning the request. Unfortunately that URL may contain sensitive query parameters, or other things that make it unsafe for directly logging or forwarding elsewhere without first sanitizing the URL or removing it entirely.
There was a Gopher who recently ran into a case where they were almost writing sensitive URLs out to log files, but they managed to catch that in a pre-production environment. They could, at each http.Client.Do() call site, write logic to reformat the error value to not include the URL, but there are a few problems with that approach.
The first is that you’d run the risk of forgetting to add that protection somewhere, or a code change / addition in the future could omit it. In addition if you’re mutating the error value to remove the URL, you’ve made it so that callers cannot inspect that detail of the error if they wanted to use it to decide to handle the error in a specific way.
Proposal
Considering the above context, I’d like to propose that we extend the net/http.Client to optionally omit the URL from any and all errors that get generated when it tries to action a request. This would allow programmers to set the behavior on the client itself, mitigating the first risk of forgetting to filter the URL from the error at a specific call site. This would be accomplished by adding a bool field to the net/http.Client struct: OnErrorOmitURL. The zero value (false) for this field would result in the http.Client behaving like it does today, meaning this would not be a breaking change.
Because the errors returned from the http.Client.Do() method are of type net/url.Error, we will also need to extend that type to include a bool struct field, OmitURL, which results in the Error() method not including the URL in the error string it returns. This will address the second risk above as the URL will still be present in string form within the url.Error.URL field, but it won’t be implicitly included when the error value is printed or logged. Like the field on the net/http.Client, the zero value (false) will result in url.Error working like it does today, meaning this would also not be a breaking change.
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 16
- Comments: 15 (14 by maintainers)
You can do something like this to redact the URLs without requiring any changes to net/http (playground link - won’t run on the playground because of #44822):
That works even when you don’t have control over the
Clientstruct (which is pretty common in my experience) and can be used wherever is appropriate. You could use it directly after a http request, or after a wrapper function is done, or all the way down at where the error is actually being logged.This seems related to
net/url.(*URL).Redacted. And, for that matter, net/http already tries to strip the password in a returned URL (https://go.googlesource.com/go/+/refs/heads/master/src/net/http/client.go#1003).This seems like a reasonable idea to me if you want to write a proposal. Thanks.
If we’re willing to permit
net/http.Client.MutateErrto return a non-*url.Errorerror, then a redaction function could return an error that wraps the original*url.Error.This permits inspecting the original URL (by recovering the
url.Errorwitherrors.As), while redacting it from the error text.