VYPR
Moderate severityNVD Advisory· Published Mar 9, 2024· Updated Feb 13, 2025

Go JOSE vulnerable to Improper Handling of Highly Compressed Data (Data Amplification)

CVE-2024-28180

Description

Package jose aims to provide an implementation of the Javascript Object Signing and Encryption set of standards. An attacker could send a JWE containing compressed data that used large amounts of memory and CPU when decompressed by Decrypt or DecryptMulti. Those functions now return an error if the decompressed data would exceed 250kB or 10x the compressed size (whichever is larger). This vulnerability has been patched in versions 4.0.1, 3.0.3 and 2.6.3.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/go-jose/go-jose/v4Go
< 4.0.14.0.1
github.com/go-jose/go-jose/v3Go
< 3.0.33.0.3
gopkg.in/go-jose/go-jose.v2Go
< 2.6.32.6.3
gopkg.in/square/go-jose.v2Go
<= 2.6.0

Affected products

1

Patches

3
0dd4dd541c66

v2: backport decompression limit fix (#109)

https://github.com/go-jose/go-joseJacob Hoffman-AndrewsMar 7, 2024via ghsa
4 files changed · +141 4
  • CHANGELOG.md+84 0 added
    @@ -0,0 +1,84 @@
    +# v4.0.1
    +
    +## Fixed
    +
    + - An attacker could send a JWE containing compressed data that used large
    +   amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`.
    +   Those functions now return an error if the decompressed data would exceed
    +   250kB or 10x the compressed size (whichever is larger). Thanks to
    +   Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj)
    +   for reporting.
    +
    +# v4.0.0
    +
    +This release makes some breaking changes in order to more thoroughly
    +address the vulnerabilities discussed in [Three New Attacks Against JSON Web
    +Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot
    +token".
    +
    +## Changed
    +
    + - Limit JWT encryption types (exclude password or public key types) (#78)
    + - Enforce minimum length for HMAC keys (#85)
    + - jwt: match any audience in a list, rather than requiring all audiences (#81)
    + - jwt: accept only Compact Serialization (#75)
    + - jws: Add expected algorithms for signatures (#74)
    + - Require specifying expected algorithms for ParseEncrypted,
    +   ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned,
    +   jwt.ParseSignedAndEncrypted (#69, #74)
    +   - Usually there is a small, known set of appropriate algorithms for a program
    +     to use and it's a mistake to allow unexpected algorithms. For instance the
    +     "billion hash attack" relies in part on programs accepting the PBES2
    +     encryption algorithm and doing the necessary work even if they weren't
    +     specifically configured to allow PBES2.
    + - Revert "Strip padding off base64 strings" (#82)
    +  - The specs require base64url encoding without padding.
    + - Minimum supported Go version is now 1.21
    +
    +## Added
    +
    + - ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON.
    +   - These allow parsing a specific serialization, as opposed to ParseSigned and
    +     ParseEncrypted, which try to automatically detect which serialization was
    +     provided. It's common to require a specific serialization for a specific
    +     protocol - for instance JWT requires Compact serialization.
    +
    +[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
    +
    +# v3.0.3
    +
    +## Fixed
    +
    + - Limit decompression output size to prevent a DoS. Backport from v4.0.1.
    +
    +# v3.0.2
    +
    +## Fixed
    +
    + - DecryptMulti: handle decompression error (#19)
    +
    +## Changed
    +
    + - jwe/CompactSerialize: improve performance (#67)
    + - Increase the default number of PBKDF2 iterations to 600k (#48)
    + - Return the proper algorithm for ECDSA keys (#45)
    +
    +## Added
    +
    + - Add Thumbprint support for opaque signers (#38)
    +
    +# v3.0.1
    +
    +## Fixed
    +
    + - Security issue: an attacker specifying a large "p2c" value can cause
    +   JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large
    +   amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the
    +   disclosure and to Tom Tervoort for originally publishing the category of attack.
    +   https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
    +
    +# v2.6.3
    +
    +## Fixed
    +
    + - Limit decompression output size to prevent a DoS. Backport from v4.0.1.
    
  • crypter.go+6 0 modified
    @@ -406,6 +406,9 @@ func (ctx *genericEncrypter) Options() EncrypterOptions {
     // Decrypt and validate the object and return the plaintext. Note that this
     // function does not support multi-recipient, if you desire multi-recipient
     // decryption use DecryptMulti instead.
    +//
    +// Automatically decompresses plaintext, but returns an error if the decompressed
    +// data would be >250kB or >10x the size of the compressed data, whichever is larger.
     func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
     	headers := obj.mergedHeaders(nil)
     
    @@ -470,6 +473,9 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
     // with support for multiple recipients. It returns the index of the recipient
     // for which the decryption was successful, the merged headers for that recipient,
     // and the plaintext.
    +//
    +// Automatically decompresses plaintext, but returns an error if the decompressed
    +// data would be >250kB or >3x the size of the compressed data, whichever is larger.
     func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
     	globalHeaders := obj.mergedHeaders(nil)
     
    
  • encoding.go+17 4 modified
    @@ -21,6 +21,7 @@ import (
     	"compress/flate"
     	"encoding/base64"
     	"encoding/binary"
    +	"fmt"
     	"io"
     	"math/big"
     	"strings"
    @@ -85,7 +86,7 @@ func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
     	}
     }
     
    -// Compress with DEFLATE
    +// deflate compresses the input.
     func deflate(input []byte) ([]byte, error) {
     	output := new(bytes.Buffer)
     
    @@ -97,15 +98,27 @@ func deflate(input []byte) ([]byte, error) {
     	return output.Bytes(), err
     }
     
    -// Decompress with DEFLATE
    +// inflate decompresses the input.
    +//
    +// Errors if the decompressed data would be >250kB or >10x the size of the
    +// compressed data, whichever is larger.
     func inflate(input []byte) ([]byte, error) {
     	output := new(bytes.Buffer)
     	reader := flate.NewReader(bytes.NewBuffer(input))
     
    -	_, err := io.Copy(output, reader)
    -	if err != nil {
    +	maxCompressedSize := 10 * int64(len(input))
    +	if maxCompressedSize < 250000 {
    +		maxCompressedSize = 250000
    +	}
    +
    +	limit := maxCompressedSize + 1
    +	n, err := io.CopyN(output, reader, limit)
    +	if err != nil && err != io.EOF {
     		return nil, err
     	}
    +	if n == limit {
    +		return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize)
    +	}
     
     	err = reader.Close()
     	return output.Bytes(), err
    
  • encoding_test.go+34 0 modified
    @@ -18,6 +18,8 @@ package jose
     
     import (
     	"bytes"
    +	"crypto/rand"
    +	"io"
     	"strings"
     	"testing"
     )
    @@ -57,6 +59,38 @@ func TestInvalidCompression(t *testing.T) {
     	}
     }
     
    +// TestLargeZip tests that we can decompress a large input, so long as its
    +// compression ratio is reasonable.
    +func TestLargeZip(t *testing.T) {
    +	input := new(bytes.Buffer)
    +	_, err := io.CopyN(input, rand.Reader, 251000)
    +	if err != nil {
    +		t.Fatalf("generating input: %s", err)
    +	}
    +	compressed, err := compress(DEFLATE, input.Bytes())
    +	if err != nil {
    +		t.Errorf("compressing: %s", err)
    +	}
    +	t.Logf("compression ratio: %g", float64(len(input.Bytes()))/float64(len(compressed)))
    +	_, err = decompress(DEFLATE, compressed)
    +	if err != nil {
    +		t.Errorf("decompressing large input with low compression ratio: %s", err)
    +	}
    +}
    +
    +func TestZipBomb(t *testing.T) {
    +	input := strings.Repeat("a", 251000)
    +	compressed, err := compress(DEFLATE, []byte(input))
    +	if err != nil {
    +		t.Errorf("compressing: %s", err)
    +	}
    +	t.Logf("compression ratio: %d %g", len(compressed), float64(len(input))/float64(len(compressed)))
    +	out, err := decompress(DEFLATE, compressed)
    +	if err == nil {
    +		t.Errorf("expected error decompressing zip bomb, got none. output size %d", len(out))
    +	}
    +}
    +
     func TestByteBufferTrim(t *testing.T) {
     	buf := newBufferFromInt(1)
     	if !bytes.Equal(buf.data, []byte{1}) {
    
add6a284ea0f

v3: backport decompression limit fix (#107)

https://github.com/go-jose/go-joseJacob Hoffman-AndrewsMar 7, 2024via ghsa
4 files changed · +110 4
  • CHANGELOG.md+53 0 modified
    @@ -1,3 +1,56 @@
    +# v4.0.1
    +
    +## Fixed
    +
    + - An attacker could send a JWE containing compressed data that used large
    +   amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`.
    +   Those functions now return an error if the decompressed data would exceed
    +   250kB or 10x the compressed size (whichever is larger). Thanks to
    +   Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj)
    +   for reporting.
    +
    +# v4.0.0
    +
    +This release makes some breaking changes in order to more thoroughly
    +address the vulnerabilities discussed in [Three New Attacks Against JSON Web
    +Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot
    +token".
    +
    +## Changed
    +
    + - Limit JWT encryption types (exclude password or public key types) (#78)
    + - Enforce minimum length for HMAC keys (#85)
    + - jwt: match any audience in a list, rather than requiring all audiences (#81)
    + - jwt: accept only Compact Serialization (#75)
    + - jws: Add expected algorithms for signatures (#74)
    + - Require specifying expected algorithms for ParseEncrypted,
    +   ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned,
    +   jwt.ParseSignedAndEncrypted (#69, #74)
    +   - Usually there is a small, known set of appropriate algorithms for a program
    +     to use and it's a mistake to allow unexpected algorithms. For instance the
    +     "billion hash attack" relies in part on programs accepting the PBES2
    +     encryption algorithm and doing the necessary work even if they weren't
    +     specifically configured to allow PBES2.
    + - Revert "Strip padding off base64 strings" (#82)
    +  - The specs require base64url encoding without padding.
    + - Minimum supported Go version is now 1.21
    +
    +## Added
    +
    + - ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON.
    +   - These allow parsing a specific serialization, as opposed to ParseSigned and
    +     ParseEncrypted, which try to automatically detect which serialization was
    +     provided. It's common to require a specific serialization for a specific
    +     protocol - for instance JWT requires Compact serialization.
    +
    +[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
    +
    +# v3.0.3
    +
    +## Fixed
    +
    + - Limit decompression output size to prevent a DoS. Backport from v4.0.1.
    +
     # v3.0.2
     
     ## Fixed
    
  • crypter.go+6 0 modified
    @@ -440,6 +440,9 @@ func (ctx *genericEncrypter) Options() EncrypterOptions {
     //
     // Note that ed25519 is only available for signatures, not encryption, so is
     // not an option here.
    +//
    +// Automatically decompresses plaintext, but returns an error if the decompressed
    +// data would be >250kB or >10x the size of the compressed data, whichever is larger.
     func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
     	headers := obj.mergedHeaders(nil)
     
    @@ -511,6 +514,9 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
     //
     // The decryptionKey argument must have one of the types allowed for the
     // decryptionKey argument of Decrypt().
    +//
    +// Automatically decompresses plaintext, but returns an error if the decompressed
    +// data would be >250kB or >3x the size of the compressed data, whichever is larger.
     func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
     	globalHeaders := obj.mergedHeaders(nil)
     
    
  • encoding.go+17 4 modified
    @@ -21,6 +21,7 @@ import (
     	"compress/flate"
     	"encoding/base64"
     	"encoding/binary"
    +	"fmt"
     	"io"
     	"math/big"
     	"strings"
    @@ -85,7 +86,7 @@ func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
     	}
     }
     
    -// Compress with DEFLATE
    +// deflate compresses the input.
     func deflate(input []byte) ([]byte, error) {
     	output := new(bytes.Buffer)
     
    @@ -97,15 +98,27 @@ func deflate(input []byte) ([]byte, error) {
     	return output.Bytes(), err
     }
     
    -// Decompress with DEFLATE
    +// inflate decompresses the input.
    +//
    +// Errors if the decompressed data would be >250kB or >10x the size of the
    +// compressed data, whichever is larger.
     func inflate(input []byte) ([]byte, error) {
     	output := new(bytes.Buffer)
     	reader := flate.NewReader(bytes.NewBuffer(input))
     
    -	_, err := io.Copy(output, reader)
    -	if err != nil {
    +	maxCompressedSize := 10 * int64(len(input))
    +	if maxCompressedSize < 250000 {
    +		maxCompressedSize = 250000
    +	}
    +
    +	limit := maxCompressedSize + 1
    +	n, err := io.CopyN(output, reader, limit)
    +	if err != nil && err != io.EOF {
     		return nil, err
     	}
    +	if n == limit {
    +		return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize)
    +	}
     
     	err = reader.Close()
     	return output.Bytes(), err
    
  • encoding_test.go+34 0 modified
    @@ -18,6 +18,8 @@ package jose
     
     import (
     	"bytes"
    +	"crypto/rand"
    +	"io"
     	"strings"
     	"testing"
     )
    @@ -57,6 +59,38 @@ func TestInvalidCompression(t *testing.T) {
     	}
     }
     
    +// TestLargeZip tests that we can decompress a large input, so long as its
    +// compression ratio is reasonable.
    +func TestLargeZip(t *testing.T) {
    +	input := new(bytes.Buffer)
    +	_, err := io.CopyN(input, rand.Reader, 251000)
    +	if err != nil {
    +		t.Fatalf("generating input: %s", err)
    +	}
    +	compressed, err := compress(DEFLATE, input.Bytes())
    +	if err != nil {
    +		t.Errorf("compressing: %s", err)
    +	}
    +	t.Logf("compression ratio: %g", float64(len(input.Bytes()))/float64(len(compressed)))
    +	_, err = decompress(DEFLATE, compressed)
    +	if err != nil {
    +		t.Errorf("decompressing large input with low compression ratio: %s", err)
    +	}
    +}
    +
    +func TestZipBomb(t *testing.T) {
    +	input := strings.Repeat("a", 251000)
    +	compressed, err := compress(DEFLATE, []byte(input))
    +	if err != nil {
    +		t.Errorf("compressing: %s", err)
    +	}
    +	t.Logf("compression ratio: %d %g", len(compressed), float64(len(input))/float64(len(compressed)))
    +	out, err := decompress(DEFLATE, compressed)
    +	if err == nil {
    +		t.Errorf("expected error decompressing zip bomb, got none. output size %d", len(out))
    +	}
    +}
    +
     func TestByteBufferTrim(t *testing.T) {
     	buf := newBufferFromInt(1)
     	if !bytes.Equal(buf.data, []byte{1}) {
    
f4c051a0653d

jwe: limit maximum output from decompressing (#106)

https://github.com/go-jose/go-joseJacob Hoffman-AndrewsMar 7, 2024via ghsa
4 files changed · +65 4
  • CHANGELOG.md+11 0 modified
    @@ -1,3 +1,14 @@
    +# v4.0.1
    +
    +## Fixed
    +
    + - An attacker could send a JWE containing compressed data that used large
    +   amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`.
    +   Those functions now return an error if the decompressed data would exceed
    +   250kB or 10x the compressed size (whichever is larger). Thanks to
    +   Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj)
    +   for reporting.
    +
     # v4.0.0
     
     This release makes some breaking changes in order to more thoroughly
    
  • crypter.go+6 0 modified
    @@ -440,6 +440,9 @@ func (ctx *genericEncrypter) Options() EncrypterOptions {
     //
     // Note that ed25519 is only available for signatures, not encryption, so is
     // not an option here.
    +//
    +// Automatically decompresses plaintext, but returns an error if the decompressed
    +// data would be >250kB or >10x the size of the compressed data, whichever is larger.
     func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
     	headers := obj.mergedHeaders(nil)
     
    @@ -511,6 +514,9 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
     //
     // The decryptionKey argument must have one of the types allowed for the
     // decryptionKey argument of Decrypt().
    +//
    +// Automatically decompresses plaintext, but returns an error if the decompressed
    +// data would be >250kB or >3x the size of the compressed data, whichever is larger.
     func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
     	globalHeaders := obj.mergedHeaders(nil)
     
    
  • encoding.go+14 4 modified
    @@ -21,6 +21,7 @@ import (
     	"compress/flate"
     	"encoding/base64"
     	"encoding/binary"
    +	"fmt"
     	"io"
     	"math/big"
     	"strings"
    @@ -85,7 +86,7 @@ func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
     	}
     }
     
    -// Compress with DEFLATE
    +// deflate compresses the input.
     func deflate(input []byte) ([]byte, error) {
     	output := new(bytes.Buffer)
     
    @@ -97,15 +98,24 @@ func deflate(input []byte) ([]byte, error) {
     	return output.Bytes(), err
     }
     
    -// Decompress with DEFLATE
    +// inflate decompresses the input.
    +//
    +// Errors if the decompressed data would be >250kB or >10x the size of the
    +// compressed data, whichever is larger.
     func inflate(input []byte) ([]byte, error) {
     	output := new(bytes.Buffer)
     	reader := flate.NewReader(bytes.NewBuffer(input))
     
    -	_, err := io.Copy(output, reader)
    -	if err != nil {
    +	maxCompressedSize := max(250_000, 10*int64(len(input)))
    +
    +	limit := maxCompressedSize + 1
    +	n, err := io.CopyN(output, reader, limit)
    +	if err != nil && err != io.EOF {
     		return nil, err
     	}
    +	if n == limit {
    +		return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize)
    +	}
     
     	err = reader.Close()
     	return output.Bytes(), err
    
  • encoding_test.go+34 0 modified
    @@ -18,6 +18,8 @@ package jose
     
     import (
     	"bytes"
    +	"crypto/rand"
    +	"io"
     	"strings"
     	"testing"
     )
    @@ -57,6 +59,38 @@ func TestInvalidCompression(t *testing.T) {
     	}
     }
     
    +// TestLargeZip tests that we can decompress a large input, so long as its
    +// compression ratio is reasonable.
    +func TestLargeZip(t *testing.T) {
    +	input := new(bytes.Buffer)
    +	_, err := io.CopyN(input, rand.Reader, 251_000)
    +	if err != nil {
    +		t.Fatalf("generating input: %s", err)
    +	}
    +	compressed, err := compress(DEFLATE, input.Bytes())
    +	if err != nil {
    +		t.Errorf("compressing: %s", err)
    +	}
    +	t.Logf("compression ratio: %g", float64(len(input.Bytes()))/float64(len(compressed)))
    +	_, err = decompress(DEFLATE, compressed)
    +	if err != nil {
    +		t.Errorf("decompressing large input with low compression ratio: %s", err)
    +	}
    +}
    +
    +func TestZipBomb(t *testing.T) {
    +	input := strings.Repeat("a", 251_000)
    +	compressed, err := compress(DEFLATE, []byte(input))
    +	if err != nil {
    +		t.Errorf("compressing: %s", err)
    +	}
    +	t.Logf("compression ratio: %d %g", len(compressed), float64(len(input))/float64(len(compressed)))
    +	out, err := decompress(DEFLATE, compressed)
    +	if err == nil {
    +		t.Errorf("expected error decompressing zip bomb, got none. output size %d", len(out))
    +	}
    +}
    +
     func TestByteBufferTrim(t *testing.T) {
     	buf := newBufferFromInt(1)
     	if !bytes.Equal(buf.data, []byte{1}) {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

24

News mentions

0

No linked articles in our index yet.