|
| 1 | +From 0dd4dd541c665fb292d664f77604ba694726f298 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Jacob Hoffman-Andrews <github@hoffman-andrews.com> |
| 3 | +Date: Thu, 7 Mar 2024 14:25:21 -0800 |
| 4 | +Subject: [PATCH] v2: backport decompression limit fix (#109) |
| 5 | + |
| 6 | +Backport from #107. |
| 7 | +--- |
| 8 | + CHANGELOG.md | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ |
| 9 | + crypter.go | 6 ++++ |
| 10 | + encoding.go | 21 +++++++++--- |
| 11 | + encoding_test.go | 34 ++++++++++++++++++++ |
| 12 | + 4 files changed, 141 insertions(+), 4 deletions(-) |
| 13 | + create mode 100644 CHANGELOG.md |
| 14 | + |
| 15 | +diff --git a/CHANGELOG.md b/CHANGELOG.md |
| 16 | +new file mode 100644 |
| 17 | +index 0000000..8e6e913 |
| 18 | +--- /dev/null |
| 19 | ++++ b/CHANGELOG.md |
| 20 | +@@ -0,0 +1,84 @@ |
| 21 | ++# v4.0.1 |
| 22 | ++ |
| 23 | ++## Fixed |
| 24 | ++ |
| 25 | ++ - An attacker could send a JWE containing compressed data that used large |
| 26 | ++ amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`. |
| 27 | ++ Those functions now return an error if the decompressed data would exceed |
| 28 | ++ 250kB or 10x the compressed size (whichever is larger). Thanks to |
| 29 | ++ Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj) |
| 30 | ++ for reporting. |
| 31 | ++ |
| 32 | ++# v4.0.0 |
| 33 | ++ |
| 34 | ++This release makes some breaking changes in order to more thoroughly |
| 35 | ++address the vulnerabilities discussed in [Three New Attacks Against JSON Web |
| 36 | ++Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot |
| 37 | ++token". |
| 38 | ++ |
| 39 | ++## Changed |
| 40 | ++ |
| 41 | ++ - Limit JWT encryption types (exclude password or public key types) (#78) |
| 42 | ++ - Enforce minimum length for HMAC keys (#85) |
| 43 | ++ - jwt: match any audience in a list, rather than requiring all audiences (#81) |
| 44 | ++ - jwt: accept only Compact Serialization (#75) |
| 45 | ++ - jws: Add expected algorithms for signatures (#74) |
| 46 | ++ - Require specifying expected algorithms for ParseEncrypted, |
| 47 | ++ ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned, |
| 48 | ++ jwt.ParseSignedAndEncrypted (#69, #74) |
| 49 | ++ - Usually there is a small, known set of appropriate algorithms for a program |
| 50 | ++ to use and it's a mistake to allow unexpected algorithms. For instance the |
| 51 | ++ "billion hash attack" relies in part on programs accepting the PBES2 |
| 52 | ++ encryption algorithm and doing the necessary work even if they weren't |
| 53 | ++ specifically configured to allow PBES2. |
| 54 | ++ - Revert "Strip padding off base64 strings" (#82) |
| 55 | ++ - The specs require base64url encoding without padding. |
| 56 | ++ - Minimum supported Go version is now 1.21 |
| 57 | ++ |
| 58 | ++## Added |
| 59 | ++ |
| 60 | ++ - ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON. |
| 61 | ++ - These allow parsing a specific serialization, as opposed to ParseSigned and |
| 62 | ++ ParseEncrypted, which try to automatically detect which serialization was |
| 63 | ++ provided. It's common to require a specific serialization for a specific |
| 64 | ++ protocol - for instance JWT requires Compact serialization. |
| 65 | ++ |
| 66 | ++[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf |
| 67 | ++ |
| 68 | ++# v3.0.3 |
| 69 | ++ |
| 70 | ++## Fixed |
| 71 | ++ |
| 72 | ++ - Limit decompression output size to prevent a DoS. Backport from v4.0.1. |
| 73 | ++ |
| 74 | ++# v3.0.2 |
| 75 | ++ |
| 76 | ++## Fixed |
| 77 | ++ |
| 78 | ++ - DecryptMulti: handle decompression error (#19) |
| 79 | ++ |
| 80 | ++## Changed |
| 81 | ++ |
| 82 | ++ - jwe/CompactSerialize: improve performance (#67) |
| 83 | ++ - Increase the default number of PBKDF2 iterations to 600k (#48) |
| 84 | ++ - Return the proper algorithm for ECDSA keys (#45) |
| 85 | ++ |
| 86 | ++## Added |
| 87 | ++ |
| 88 | ++ - Add Thumbprint support for opaque signers (#38) |
| 89 | ++ |
| 90 | ++# v3.0.1 |
| 91 | ++ |
| 92 | ++## Fixed |
| 93 | ++ |
| 94 | ++ - Security issue: an attacker specifying a large "p2c" value can cause |
| 95 | ++ JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large |
| 96 | ++ amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the |
| 97 | ++ disclosure and to Tom Tervoort for originally publishing the category of attack. |
| 98 | ++ https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf |
| 99 | ++ |
| 100 | ++# v2.6.3 |
| 101 | ++ |
| 102 | ++## Fixed |
| 103 | ++ |
| 104 | ++ - Limit decompression output size to prevent a DoS. Backport from v4.0.1. |
| 105 | +diff --git a/vendor/gopkg.in/square/go-jose.v2/crypter.go b/vendor/gopkg.in/square/go-jose.v2/crypter.go |
| 106 | +index 73aab0f..0ae2e5e 100644 |
| 107 | +--- a/vendor/gopkg.in/square/go-jose.v2/crypter.go |
| 108 | ++++ b/vendor/gopkg.in/square/go-jose.v2/crypter.go |
| 109 | +@@ -406,6 +406,9 @@ func (ctx *genericEncrypter) Options() EncrypterOptions { |
| 110 | + // Decrypt and validate the object and return the plaintext. Note that this |
| 111 | + // function does not support multi-recipient, if you desire multi-recipient |
| 112 | + // decryption use DecryptMulti instead. |
| 113 | ++// |
| 114 | ++// Automatically decompresses plaintext, but returns an error if the decompressed |
| 115 | ++// data would be >250kB or >10x the size of the compressed data, whichever is larger. |
| 116 | + func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) { |
| 117 | + headers := obj.mergedHeaders(nil) |
| 118 | + |
| 119 | +@@ -470,6 +473,9 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) |
| 120 | + // with support for multiple recipients. It returns the index of the recipient |
| 121 | + // for which the decryption was successful, the merged headers for that recipient, |
| 122 | + // and the plaintext. |
| 123 | ++// |
| 124 | ++// Automatically decompresses plaintext, but returns an error if the decompressed |
| 125 | ++// data would be >250kB or >3x the size of the compressed data, whichever is larger. |
| 126 | + func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) { |
| 127 | + globalHeaders := obj.mergedHeaders(nil) |
| 128 | + |
| 129 | +diff --git a/vendor/gopkg.in/square/go-jose.v2/encoding.go b/vendor/gopkg.in/square/go-jose.v2/encoding.go |
| 130 | +index 40b688b..636f6c8 100644 |
| 131 | +--- a/vendor/gopkg.in/square/go-jose.v2/encoding.go |
| 132 | ++++ b/vendor/gopkg.in/square/go-jose.v2/encoding.go |
| 133 | +@@ -21,6 +21,7 @@ import ( |
| 134 | + "compress/flate" |
| 135 | + "encoding/base64" |
| 136 | + "encoding/binary" |
| 137 | ++ "fmt" |
| 138 | + "io" |
| 139 | + "math/big" |
| 140 | + "strings" |
| 141 | +@@ -85,7 +86,7 @@ func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) { |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | +-// Compress with DEFLATE |
| 146 | ++// deflate compresses the input. |
| 147 | + func deflate(input []byte) ([]byte, error) { |
| 148 | + output := new(bytes.Buffer) |
| 149 | + |
| 150 | +@@ -97,15 +98,27 @@ func deflate(input []byte) ([]byte, error) { |
| 151 | + return output.Bytes(), err |
| 152 | + } |
| 153 | + |
| 154 | +-// Decompress with DEFLATE |
| 155 | ++// inflate decompresses the input. |
| 156 | ++// |
| 157 | ++// Errors if the decompressed data would be >250kB or >10x the size of the |
| 158 | ++// compressed data, whichever is larger. |
| 159 | + func inflate(input []byte) ([]byte, error) { |
| 160 | + output := new(bytes.Buffer) |
| 161 | + reader := flate.NewReader(bytes.NewBuffer(input)) |
| 162 | + |
| 163 | +- _, err := io.Copy(output, reader) |
| 164 | +- if err != nil { |
| 165 | ++ maxCompressedSize := 10 * int64(len(input)) |
| 166 | ++ if maxCompressedSize < 250000 { |
| 167 | ++ maxCompressedSize = 250000 |
| 168 | ++ } |
| 169 | ++ |
| 170 | ++ limit := maxCompressedSize + 1 |
| 171 | ++ n, err := io.CopyN(output, reader, limit) |
| 172 | ++ if err != nil && err != io.EOF { |
| 173 | + return nil, err |
| 174 | + } |
| 175 | ++ if n == limit { |
| 176 | ++ return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize) |
| 177 | ++ } |
| 178 | + |
| 179 | + err = reader.Close() |
| 180 | + return output.Bytes(), err |
0 commit comments