Limit the length of logged HTTP response (#1778)

* Limit the length of logged HTTP response

Fixes #1777

* Fix crash, add tests
* Fix utf-8 truncation
* move more logic into helper method
* Add unit test for truncateBody

https://github.com/letsencrypt/boulder/pull/1778
This commit is contained in:
Kane York 2016-04-29 11:25:51 -07:00 committed by Roland Bracewell Shoemaker
parent dc15f6a55e
commit 801626fb15
2 changed files with 66 additions and 1 deletions

View File

@ -373,8 +373,9 @@ func (va *ValidationAuthorityImpl) validateHTTP01(ctx context.Context, identifie
}
if expectedKeyAuth != payload {
truncBody := truncateBody(payload)
errString := fmt.Sprintf("The key authorization file from the server did not match this challenge [%v] != [%v]",
expectedKeyAuth, string(body))
expectedKeyAuth, truncBody)
va.log.Info(fmt.Sprintf("%s for %s", errString, identifier))
return validationRecords, &probs.ProblemDetails{
Type: probs.UnauthorizedProblem,
@ -385,6 +386,19 @@ func (va *ValidationAuthorityImpl) validateHTTP01(ctx context.Context, identifie
return validationRecords, nil
}
// truncateBody will cut off a string at 45 UTF-8 characters.
func truncateBody(str string) string {
count := 0
const max = 45
for index, _ := range str {
count++
if count > max {
return fmt.Sprintf("%s…", str[:index])
}
}
return str
}
func (va *ValidationAuthorityImpl) validateTLSSNI01(ctx context.Context, identifier core.AcmeIdentifier, challenge core.Challenge) ([]core.ValidationRecord, *probs.ProblemDetails) {
if identifier.Type != "dns" {
va.log.Info(fmt.Sprintf("Identifier type for TLS-SNI was not DNS: %s", identifier))

View File

@ -541,6 +541,36 @@ func TestValidateHTTP(t *testing.T) {
test.AssertEquals(t, core.StatusValid, mockRA.lastAuthz.Challenges[0].Status)
}
func TestValidateHTTPResponseDocument(t *testing.T) {
chall := core.HTTPChallenge01(accountKey)
setChallengeToken(&chall, core.NewToken())
hs := httpSrv(t, `a.StartOfLine.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.PastTruncationPoint.aaaaaaaaaaaaaaaaaaaa`)
port, err := getPort(hs)
test.AssertNotError(t, err, "failed to get test server port")
stats, _ := statsd.NewNoopClient()
va := NewValidationAuthorityImpl(&cmd.PortConfig{HTTPPort: port}, nil, nil, stats, clock.Default())
va.DNSResolver = &bdns.MockDNSResolver{}
mockRA := &MockRegistrationAuthority{}
va.RA = mockRA
defer hs.Close()
var authz = core.Authorization{
ID: core.NewToken(),
RegistrationID: 1,
Identifier: ident,
Challenges: []core.Challenge{chall},
}
va.validate(ctx, authz, 0)
test.AssertEquals(t, core.StatusInvalid, mockRA.lastAuthz.Challenges[0].Status)
test.Assert(t, len(log.GetAllMatching("StartOfLine")) > 1, "Beginning of response body not logged")
test.Assert(t, len(log.GetAllMatching("…")) > 1, "Ellipsis not logged")
test.AssertEquals(t, len(log.GetAllMatching("PastTruncationPoint")), 0) // End of response body was logged
}
// challengeType == "tls-sni-00" or "dns-00", since they're the same
func createChallenge(challengeType string) core.Challenge {
chall := core.Challenge{
@ -1008,6 +1038,27 @@ func TestCAAFailure(t *testing.T) {
test.AssertEquals(t, core.StatusInvalid, mockRA.lastAuthz.Challenges[0].Status)
}
func TestTruncateBody(t *testing.T) {
testCases := []struct {
Case string
Expected string
}{
{"", ""},
{"a", "a"},
{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…"},
{"ááááááááááááááááááááááááááááááááááááááááááááá", "ááááááááááááááááááááááááááááááááááááááááááááá"},
{"áááááááááááááááááááááááááááááááááááááááááááááá", "ááááááááááááááááááááááááááááááááááááááááááááá…"},
}
for _, v := range testCases {
result := truncateBody(v.Case)
if result != v.Expected {
t.Errorf("got %q, expected %q", result, v.Expected)
}
}
}
type MockRegistrationAuthority struct {
lastAuthz *core.Authorization
}