diff --git a/logging/aws.go b/logging/aws.go index 1ab8ffbc..6e48f34e 100644 --- a/logging/aws.go +++ b/logging/aws.go @@ -26,18 +26,58 @@ var UniqueIDRegex = regexp.MustCompile(`(A3T[A-Z0-9]` + var SensitiveKeyRegex = regexp.MustCompile(`[A-Za-z0-9/+=]{16,}`) +const ( + unmaskedFirst = 4 + unmaskedLast = 4 +) + func MaskAWSAccessKey(field string) string { field = UniqueIDRegex.ReplaceAllStringFunc(field, func(s string) string { - return partialMaskString(s, 4, 4) //nolint:gomnd + return partialMaskString(s, unmaskedFirst, unmaskedLast) }) return field } func MaskAWSSensitiveValues(field string) string { field = MaskAWSAccessKey(field) - - field = SensitiveKeyRegex.ReplaceAllStringFunc(field, func(s string) string { - return partialMaskString(s, 4, 4) //nolint:gomnd - }) + field = MaskAWSSecretKeys(field) return field } + +// MaskAWSSecretKeys masks likely AWS secret access keys in the input. +// See https://aws.amazon.com/blogs/security/a-safer-way-to-distribute-aws-credentials-to-ec2/: +// "Find me 40-character, base-64 strings that don’t have any base 64 characters immediately before or after". +func MaskAWSSecretKeys(in string) string { + const ( + secretKeyLen = 40 + ) + len := len(in) + out := make([]byte, len) + base64Characters := 0 + + for i := 0; i < len; i++ { + b := in[i] + out[i] = b + + if (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9') || b == '/' || b == '+' || b == '=' { + // base64 character. + base64Characters++ + } else { + if base64Characters == secretKeyLen { + for j := (i - secretKeyLen) + unmaskedFirst; j < i-unmaskedLast; j++ { + out[j] = '*' + } + } + + base64Characters = 0 + } + } + + if base64Characters == secretKeyLen { + for j := (len - secretKeyLen) + unmaskedFirst; j < len-unmaskedLast; j++ { + out[j] = '*' + } + } + + return string(out) +} diff --git a/logging/aws_test.go b/logging/aws_test.go index e760a6e7..1c300c75 100644 --- a/logging/aws_test.go +++ b/logging/aws_test.go @@ -17,23 +17,55 @@ func TestMaskAWSSensitiveValues(t *testing.T) { tests := map[string]testCase{ "mask_simple": { - input: "4skd4lTSLVBMG/asedterGLKSNMSAlsxiLGfjt=ssD", - expected: "4skd**********************************=ssD", + input: "MfP3tIG15gibzIx7CSbhSNkgD5sSV4k2tWXgN8U8", + expected: "MfP3********************************N8U8", }, "mask_complex_json": { input: ` { - "AWSSecretKey": "4skd4lTSLVBMG/asedterGLKSNMSAlsxiLGfjt=ssD", + "AWSSecretKey": "LEfH8nZmFN4BGIJnku6lkChHydRN5B/YlWCIjOte", "BucketName": "test-bucket", "AWSKeyId": "AIDACKCEVSQ6C2EXAMPLE", } `, expected: ` { - "AWSSecretKey": "4skd**********************************=ssD", + "AWSSecretKey": "LEfH********************************jOte", "BucketName": "test-bucket", "AWSKeyId": "AIDA*************MPLE", } +`, + }, + "mask_multiple_json": { + input: ` +{ + "AWSSecretKey": "LEfH8nZmFN4BGIJnku6lkChHydRN5B/YlWCIjOte", + "BucketName": "test-bucket-1", + "AWSKeyId": "AIDACKCEVSQ6C2EXAMPLE", +}, +{ + "Key": "ABCDEFGH!JKLMNOPQRSTUVWXYZ012345678901234567890123456789", +}, +{ + "AWSSecretKey": "MfP3tIG15gibzIx7CSbhSNkgD5sSV4k2tWXgN8U8", + "BucketName": "test-bucket-2", + "AWSKeyId": "AKIA5PX2H2S3LHEXAMPLE", +} +`, + expected: ` +{ + "AWSSecretKey": "LEfH********************************jOte", + "BucketName": "test-bucket-1", + "AWSKeyId": "AIDA*************MPLE", +}, +{ + "Key": "ABCDEFGH!JKLMNOPQRSTUVWXYZ012345678901234567890123456789", +}, +{ + "AWSSecretKey": "MfP3********************************N8U8", + "BucketName": "test-bucket-2", + "AWSKeyId": "AKIA*************MPLE", +} `, }, "no_mask": { @@ -42,12 +74,12 @@ func TestMaskAWSSensitiveValues(t *testing.T) { }, "mask_xml": { input: ` -4skd4lTSLVBMG/asedterGLKSNMSAlsxiLGfjt=ssD +8/AiP0ofCD/YOAqXWrungQt/Y4BkTj1UOjZ0MqBs test-bucket AIDACKCEVSQ6C2EXAMPLE `, expected: ` -4skd**********************************=ssD +8/Ai********************************MqBs test-bucket AIDA*************MPLE `,