Fix Akamai OCSP purging code (#2531)

Purge both query encoded and un-encoded OCSP GET URLs and fix a typo in the POST body key.
Since there is no public documentation of how the latter Akamai feature works we were unable to
confirm that the previously spelling of `body-mdy` was a typo of `body-md5` but active testing against
the staging environment and the Akamai API has confirmed that it was.

Fixes #2528.
This commit is contained in:
Roland Bracewell Shoemaker 2017-01-30 10:43:18 -08:00 committed by Jacob Hoffman-Andrews
parent 00d11f126b
commit a0da51ee7b
2 changed files with 32 additions and 16 deletions

View File

@ -207,12 +207,28 @@ func reverseBytes(b []byte) []byte {
return b
}
func generatePOSTURL(der []byte, ocspServer string) string {
// Generate POST url, format is the URL that was POST'd to with a query string with
// the parameter 'body-mdy' and the value of the first two uint32s in little endian
// order in hex of the MD5 hash of the OCSP request body.
hash := md5.Sum(der)
return fmt.Sprintf("%s?body-mdy=%x%x", ocspServer, reverseBytes(hash[0:4]), reverseBytes(hash[4:8]))
func generateOCSPCacheKeys(req []byte, ocspServer string) []string {
hash := md5.Sum(req)
encReq := base64.StdEncoding.EncodeToString(req)
return []string{
// Generate POST key, format is the URL that was POST'd to with a query string with
// the parameter 'body-md5' and the value of the first two uint32s in little endian
// order in hex of the MD5 hash of the OCSP request body.
//
// There is no public documentation of this feature that has been published by Akamai
// as far as we are aware.
fmt.Sprintf("%s?body-md5=%x%x", ocspServer, reverseBytes(hash[0:4]), reverseBytes(hash[4:8])),
// RFC 2560 and RFC 5019 state OCSP GET URLs 'MUST properly url-encode the base64
// encoded' request but a large enough portion of tools do not properly do this
// (~10% of GET requests we receive) such that we must purge both the encoded
// and un-encoded URLs.
//
// Due to Akamai proxy/cache behavior which collapses '//' -> '/' we also
// collapse double slashes in the un-encoded URL so that we properly purge
// what is stored in the cache.
fmt.Sprintf("%s%s", ocspServer, strings.Replace(encReq, "//", "/", -1)),
fmt.Sprintf("%s%s", ocspServer, url.QueryEscape(encReq)),
}
}
// sendPurge should only be called as a Goroutine as it will block until the purge
@ -237,11 +253,7 @@ func (updater *OCSPUpdater) sendPurge(der []byte) {
ocspServer += "/"
}
// Generate GET url
urls = append(
urls,
fmt.Sprintf("%s%s", ocspServer, url.QueryEscape(base64.StdEncoding.EncodeToString(req))),
)
urls = append(urls, generatePOSTURL(der, ocspServer))
urls = append(generateOCSPCacheKeys(req, ocspServer))
}
err = updater.ccu.Purge(urls)

View File

@ -857,11 +857,15 @@ func TestReverseBytes(t *testing.T) {
test.AssertDeepEquals(t, reverseBytes(a), []byte{3, 2, 1, 0})
}
func TestGeneratePOSTURL(t *testing.T) {
der := []byte{0}
test.AssertEquals(
func TestGenerateOCSPCacheKeys(t *testing.T) {
der := []byte{105, 239, 255}
test.AssertDeepEquals(
t,
generatePOSTURL(der, "ocsp.invalid/"),
"ocsp.invalid/?body-mdy=ad85b89389a00dfe",
generateOCSPCacheKeys(der, "ocsp.invalid/"),
[]string{
"ocsp.invalid/?body-md5=d6101198a9d9f1f6",
"ocsp.invalid/ae/",
"ocsp.invalid/ae%2F%2F",
},
)
}