diff --git a/internal/transformations/base64decode.go b/internal/transformations/base64decode.go index c4be9897c..42eb0b5f3 100644 --- a/internal/transformations/base64decode.go +++ b/internal/transformations/base64decode.go @@ -5,16 +5,30 @@ package transformations import ( "encoding/base64" + "strings" stringsutil "github.com/corazawaf/coraza/v3/internal/strings" ) // base64decode decodes a Base64-encoded string. func base64decode(data string) (string, bool, error) { - dec, err := base64.StdEncoding.DecodeString(data) + // RawStdEncoding.DecodeString accepts and requires an unpadded string as input + // https://stackoverflow.com/questions/31971614/base64-encode-decode-without-padding-on-golang-appengine + dataNoPadding := strings.TrimRight(data, "=") + dec, err := base64.RawStdEncoding.DecodeString(dataNoPadding) if err != nil { - // Forgiving implementation, which ignores invalid characters - return data, false, nil + // If the error is of type CorruptInputError, we can get the position of the illegal character + // and perform a partial decoding up to that point + if corrErr, ok := err.(base64.CorruptInputError); ok { + illegalCharPos := int(corrErr) + // Forgiving call (no error check) to DecodeString. Decoding is performed truncating + // the input string to the first error index. If a new decoding error occurs, + // it will not be about an illegal character but a malformed encoding of the trailing + // character because of the truncation. The dec will still contain a best effort decoded string + dec, _ = base64.RawStdEncoding.DecodeString(dataNoPadding[:illegalCharPos]) + } else { + return data, false, nil + } } return stringsutil.WrapUnsafe(dec), true, nil } diff --git a/internal/transformations/base64decode_test.go b/internal/transformations/base64decode_test.go index d022b1659..cd5ef9213 100644 --- a/internal/transformations/base64decode_test.go +++ b/internal/transformations/base64decode_test.go @@ -10,17 +10,86 @@ import ( "testing" ) -var b64DecodeTests = []string{ - "VGVzdENhc2U=", - "P.HNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==", - "VGVzdABDYXNl", +var b64DecodeTests = []struct { + name string + input string + expected string +}{ + { + name: "Valid", + input: "VGVzdENhc2U=", + expected: "TestCase", + }, + { + name: "Valid with \u0000", + input: "VGVzdABDYXNl", + expected: "Test\x00Case", + }, + { + name: "Valid without padding", + input: "VGVzdENhc2U", + expected: "TestCase", + }, + { + name: "Valid without longer padding", + input: "PA==", + expected: "<", + }, + { + name: "valid ", + input: "PFRFU1Q+", + expected: "", + }, + { + name: "Malformed base64 encoding", + input: "PHNjcmlwd", + expected: "