diff --git a/deflate.go b/deflate.go index c788f5b..b46cdf9 100644 --- a/deflate.go +++ b/deflate.go @@ -3,15 +3,27 @@ package jose import ( "bytes" "compress/flate" - "io/ioutil" + "errors" + "io" ) +var ErrSizeExceeded = errors.New("Deflate stream size exceeded limit.") + func init() { - RegisterJwc(new(Deflate)) + // 250Kb limited decompression buffer + RegisterJwc(NewDeflate(250 * 1024)) } // Deflate compression algorithm implementation -type Deflate struct {} +type Deflate struct { + maxBufferSizeBytes int64 +} + +func NewDeflate(maxBufferSizeBytes int64) JwcAlgorithm { + return &Deflate{ + maxBufferSizeBytes: maxBufferSizeBytes, + } +} func (alg *Deflate) Name() string { return DEF @@ -19,21 +31,43 @@ func (alg *Deflate) Name() string { func (alg *Deflate) Compress(plainText []byte) []byte { var buf bytes.Buffer - deflate,_ := flate.NewWriter(&buf, 8) //level=DEFLATED - + deflate, _ := flate.NewWriter(&buf, 8) //level=DEFLATED + deflate.Write(plainText) deflate.Close() - + return buf.Bytes() } -func (alg *Deflate) Decompress(compressedText []byte) []byte { - - enflated,_ := ioutil.ReadAll( - flate.NewReader( - bytes.NewReader(compressedText))) - - return enflated +func (alg *Deflate) Decompress(compressedText []byte) ([]byte, error) { + enflated, err := io.ReadAll( + newMaxBytesReader(alg.maxBufferSizeBytes, + flate.NewReader( + bytes.NewReader(compressedText)))) + + return enflated, err } +// Max bytes reader +type maxBytesReader struct { + reader io.Reader + limit int64 +} + +func newMaxBytesReader(limit int64, r io.Reader) io.Reader { + return &maxBytesReader{reader: r, limit: limit} +} +func (mbr *maxBytesReader) Read(p []byte) (n int, err error) { + if mbr.limit <= 0 { + return 0, ErrSizeExceeded + } + + if int64(len(p)) > mbr.limit { + p = p[0:mbr.limit] + } + + n, err = mbr.reader.Read(p) + mbr.limit -= int64(n) + return +} diff --git a/jose.go b/jose.go index 3549a91..0d14ae9 100644 --- a/jose.go +++ b/jose.go @@ -140,7 +140,7 @@ type JwsAlgorithm interface { // JwcAlgorithm is a contract for implementing compression algorithm type JwcAlgorithm interface { Compress(plainText []byte) []byte - Decompress(compressedText []byte) []byte + Decompress(compressedText []byte) ([]byte, error) Name() string } @@ -427,7 +427,9 @@ func decrypt(parts [][]byte, key interface{}) (plainText []byte, headers map[str return nil, nil, errors.New(fmt.Sprintf("jwt.decrypt(): Unknown compression algorithm '%v'", zip)) } - plainBytes = zipAlg.Decompress(plainBytes) + if plainBytes, err = zipAlg.Decompress(plainBytes); err != nil { + return nil, nil, err + } } return plainBytes, jwtHeader, nil diff --git a/sec_test/security_vulnerabilities_test.go b/sec_test/security_vulnerabilities_test.go index d9dda4f..e122611 100644 --- a/sec_test/security_vulnerabilities_test.go +++ b/sec_test/security_vulnerabilities_test.go @@ -2,12 +2,18 @@ package sec_test import ( "crypto/ecdsa" + "crypto/rsa" + "encoding/json" "fmt" - "github.com/dvsekhvalnov/jose2go" + "strings" + "testing" + "time" + + jose "github.com/dvsekhvalnov/jose2go" "github.com/dvsekhvalnov/jose2go/arrays" "github.com/dvsekhvalnov/jose2go/keys/ecc" + Rsa "github.com/dvsekhvalnov/jose2go/keys/rsa" . "gopkg.in/check.v1" - "testing" ) func Test(t *testing.T) { TestingT(t) } @@ -93,8 +99,89 @@ func (s *SecurityTestSuite) Test_AAD_IntegerOverflow(c *C) { c.Assert(test, IsNil) } +func (s *SecurityTestSuite) Test_DeflateBomb(c *C) { + strU := strings.Repeat("U", 400000000) + strUU := strings.Repeat("U", 100000000) + + payloadMap := map[string]string{ + "U": strU, + "UU": strUU, + } + + payloadBytes, _ := json.Marshal(payloadMap) + + fmt.Println("Uncompressed payload length", len(payloadBytes)) + test, _ := jose.Encrypt(string(payloadBytes), jose.RSA_OAEP, jose.A256GCM, PubKey(), jose.Zip(jose.DEF)) + fmt.Println("Encoded & Compressed token length", len(test)) + + start := time.Now() + payload, headers, err := jose.Decode(test, PrivKey()) + timeElapsed := time.Since(start) + fmt.Printf("The `decode` took %s\n", timeElapsed) + + c.Assert(payload, Equals, "") + c.Assert(headers, IsNil) + c.Assert(err, Equals, jose.ErrSizeExceeded) +} + func Ecc256() *ecdsa.PrivateKey { return ecc.NewPrivate([]byte{193, 227, 73, 203, 97, 236, 112, 36, 140, 232, 1, 3, 76, 56, 52, 225, 184, 142, 190, 17, 97, 203, 37, 175, 56, 116, 31, 120, 95, 207, 196, 196}, []byte{123, 201, 103, 8, 239, 128, 149, 43, 83, 248, 210, 85, 95, 231, 43, 132, 30, 208, 69, 136, 98, 139, 29, 55, 138, 89, 73, 57, 80, 14, 201, 201}, []byte{84, 73, 131, 102, 144, 215, 92, 175, 41, 240, 221, 2, 157, 219, 49, 179, 221, 184, 171, 169, 210, 213, 21, 197, 1, 36, 101, 232, 23, 212, 169, 220}) } + +func PubKey() *rsa.PublicKey { + key, _ := Rsa.ReadPublic([]byte(pubKey)) + return key +} + +func PrivKey() *rsa.PrivateKey { + key, _ := Rsa.ReadPrivate([]byte(privKey)) + return key +} + +var pubKey = `-----BEGIN CERTIFICATE----- +MIICnTCCAYUCBEReYeAwDQYJKoZIhvcNAQEFBQAwEzERMA8GA1UEAxMIand0LTIw +NDgwHhcNMTQwMTI0MTMwOTE2WhcNMzQwMjIzMjAwMDAwWjATMREwDwYDVQQDEwhq +d3QtMjA0ODCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKhWb9KXmv45 ++TKOKhFJkrboZbpbKPJ9Yp12xKLXf8060KfStEStIX+7dCuAYylYWoqiGpuLVVUL +5JmHgXmK9TJpzv9Dfe3TAc/+35r8r9IYB2gXUOZkebty05R6PLY0RO/hs2ZhrOoz +HMo+x216Gwz0CWaajcuiY5Yg1V8VvJ1iQ3rcRgZapk49RNX69kQrGS63gzj0gyHn +Rtbqc/Ua2kobCA83nnznCom3AGinnlSN65AFPP5jmri0l79+4ZZNIerErSW96mUF +8jlJFZI1yJIbzbv73tL+y4i0+BvzsWBs6TkHAp4pinaI8zT+hrVQ2jD4fkJEiRN9 +lAqLPUd8CNkCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAnqBw3UHOSSHtU7yMi1+H +E+9119tMh7X/fCpcpOnjYmhW8uy9SiPBZBl1z6vQYkMPcURnDMGHdA31kPKICZ6G +LWGkBLY3BfIQi064e8vWHW7zX6+2Wi1zFWdJlmgQzBhbr8pYh9xjZe6FjPwbSEuS +0uE8dWSWHJLdWsA4xNX9k3pr601R2vPVFCDKs3K1a8P/Xi59kYmKMjaX6vYT879y +gWt43yhtGTF48y85+eqLdFRFANTbBFSzdRlPQUYa5d9PZGxeBTcg7UBkK/G+d6D5 +sd78T2ymwlLYrNi+cSDYD6S4hwZaLeEK6h7p/OoG02RBNuT4VqFRu5DJ6Po+C6Jh +qQ== +-----END CERTIFICATE-----` + +var privKey = `-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAqFZv0pea/jn5Mo4qEUmStuhlulso8n1inXbEotd/zTrQp9K0 +RK0hf7t0K4BjKVhaiqIam4tVVQvkmYeBeYr1MmnO/0N97dMBz/7fmvyv0hgHaBdQ +5mR5u3LTlHo8tjRE7+GzZmGs6jMcyj7HbXobDPQJZpqNy6JjliDVXxW8nWJDetxG +BlqmTj1E1fr2RCsZLreDOPSDIedG1upz9RraShsIDzeefOcKibcAaKeeVI3rkAU8 +/mOauLSXv37hlk0h6sStJb3qZQXyOUkVkjXIkhvNu/ve0v7LiLT4G/OxYGzpOQcC +nimKdojzNP6GtVDaMPh+QkSJE32UCos9R3wI2QIDAQABAoIBAQCUmHBvSkqUHaK/ +IMU7q2FqOi0KWswDefEiJKQhRu9Wv5NOgW2FrfqDIXrDp7pg1dBezgeExHLX9v6d +FAOTwbj9/m6t3+r6k6fm7gp+ao3dfD6VgPd12L2oXQ0t5NVQ1UUBJ4/QUWps9h90 +3AP4vK/COG1P+CAw4DDeZi9TlwF/Pr7e492GXcLBAUJODA6538ED2nYw8xQcbzbA +wr+w07UjRNimObtOfA0HCIpsx/6LkIqe6iGChisQNgt4yDd/fZ4GWOUIU1hqgK1P +6avVl7Q5Mk0PTi9t8ui1X4EEq6Uils45J5WkobuAnFkea/uKfs8Tn9bNrEoVWgdb +fBHq/8bNAoGBANKmjpE9e+L0RtxP+u4FN5YDoKE+i96VR7ru8H6yBKMcnD2uf5mV +RueEoL0FKHxlGBBo0dJWr1AIwpcPbTs3Dgx1/EQMZLg57QBZ7QcYETPiMwMvEM3k +Zf3G4YFYwUwIQXMYPt1ckr+RncRcq0GiKPDsvzzyNS+BBSmR5onAXd7bAoGBAMyT +6ggyqmiR/UwBn87em+GjbfX6YqxHHaQBdWwnnRX0JlGTNCxt6zLTgCIYxF4AA7eR +gfGTStwUJfAScjJirOe6Cpm1XDgxEQrT6oxAl17MR/ms/Z88WrT73G+4phVvDpVr +JcK+CCESnRI8xGLOLMkCc+5NpLajqWCOf1H2J8NbAoGAKTWmTGmf092AA1euOmRQ +5IsfIIxQ5qGDn+FgsRh4acSOGE8L7WrTrTU4EOJyciuA0qz+50xIDbs4/j5pWx1B +JVTrnhBin9vNLrVo9mtR6jmFS0ko226kOUpwEVLgtdQjobWLjtiuaMW+/Iw4gKWN +ptxZ6T1lBD8UWHaPiEFW2+MCgYAmfSWoyS96YQ0QwbV5TDRzrTXA84yg8PhIpOWc +pY9OVBLpghJs0XlQpK4UvCglr0cDwGJ8OsP4x+mjUzUc+aeiKURZSt/Ayqp0KQ6V +uIlCEpjwBnXpAYfnSQNeGZVVrwFFZ1VBYFNTNZdLmRcxp6yRXN7G1ODKY9w4CFc3 +6mHsxQKBgQCxEA+KAmmXxL++x/XOElOscz3vFHC4HbpHpOb4nywpE9vunnHE2WY4 +EEW9aZbF22jx0ESU2XJ1JlqffvfIEvHNb5tmBWn4HZEpPUHdaFNhb9WjkMuFaLzh +cydwnEftq+3G0X3KSxp4p7R7afcnpNNqfneYODgoXxTQ4Q7ZyKo72A== +-----END RSA PRIVATE KEY-----`