{"record":{"id":"eb712e10eecf6adc","repo":"spring-projects/spring-security","slug":"unable-to-inflate-string-eb712e","errorCode":null,"errorMessage":"Unable to inflate string","messagePattern":"Unable to inflate string","errorType":"exception","errorClass":"Saml2Exception","httpStatus":null,"severity":"error","filePath":"saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2Utils.java","lineNumber":74,"sourceCode":"\t\t\tdeflater.write(s.getBytes(StandardCharsets.UTF_8));\n\t\t\tdeflater.finish();\n\t\t\treturn b.toByteArray();\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to deflate string\", ex);\n\t\t}\n\t}\n\n\tstatic String samlInflate(byte[] b) {\n\t\ttry {\n\t\t\tByteArrayOutputStream out = new ByteArrayOutputStream();\n\t\t\tInflaterOutputStream iout = new InflaterOutputStream(new CappedOutputStream(out), new Inflater(true));\n\t\t\tiout.write(b);\n\t\t\tiout.finish();\n\t\t\treturn new String(out.toByteArray(), StandardCharsets.UTF_8);\n\t\t}\n\t\tcatch (IOException ex) {\n\t\t\tthrow new Saml2Exception(\"Unable to inflate string\", ex);\n\t\t}\n\t}\n\n\tstatic EncodingConfigurer withDecoded(String decoded) {\n\t\treturn new EncodingConfigurer(decoded);\n\t}\n\n\tstatic DecodingConfigurer withEncoded(String encoded) {\n\t\treturn new DecodingConfigurer(encoded);\n\t}\n\n\tstatic final class EncodingConfigurer {\n\n\t\tprivate final String decoded;\n\n\t\tprivate boolean deflate;\n\n\t\tprivate EncodingConfigurer(String decoded) {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/Saml2Utils.java#L56-L92","documentation":"Saml2Utils.samlInflate RAW-DEFLATE decompresses a Base64-decoded SAML message (typically a GET-bound SAMLResponse) and re-raises any IOException as a Saml2Exception. It throws when the input bytes are not a valid raw-DEFLATE stream, or when the decompressed output exceeds the 1 MiB CappedOutputStream limit. Since inflate is expected only for HTTP-Redirect (GET) responses, this usually means the payload was malformed or not actually deflated.","triggerScenarios":"Decoding an encoded SAMLResponse whose bytes are not valid raw-DEFLATE data (e.g. it was not compressed, was zlib-wrapped instead of raw-deflated, was truncated, or is garbage); or a decompressed payload larger than 1 MiB (which surfaces as the CappedOutputStream IOException wrapped here).","commonSituations":"IdP sends a POST-style (non-deflated) message but the request arrived via GET and inflate(true) was applied; a proxy/HTML form truncated the query-string payload; attacker-supplied tampered SAMLResponse; a decompression-bomb attempt hitting the 1 MiB cap.","solutions":["Confirm the message encoding matches the binding: GET/Redirect implies deflate(true); POST implies inflate(false) — check shouldConvertGetRequests and the request method","Base64-decode the SAMLResponse yourself and try raw Inflater(true) on the bytes to see the exact zlib failure (unknown compression method, corrupt data)","Check for truncation: query strings over URL length limits get cut off by proxies/browsers; inspect the raw parameter value length","If the message exceeds 1 MiB decompressed, this is treated as a decompression attack; verify the IdP is not sending oversized messages","Wrap the decode call and treat Saml2AuthenticationException invalid_response as an authentication failure, not a crash"],"exampleFix":"// before (converter misconfigured)\nconverter.setShouldConvertGetRequests(false); // GET SAMLResponse now decoded without inflate handling mismatch\n// after\nconverter.setShouldConvertGetRequests(true); // GET => inflate(true), POST => no inflate, matching the IdP binding","handlingStrategy":"try-catch","validationCode":"// decode-only sanity check before handing to the library\nbyte[] raw = java.util.Base64.getMimeDecoder().decode(encoded);\njava.util.zip.Inflater probe = new java.util.zip.Inflater(true);\nprobe.setInput(raw);\nbyte[] buf = new byte[64];\nboolean ok = probe.inflate(buf) > 0 || probe.getRemaining() > 0;\nprobe.end();\nif (!ok) { throw new IllegalArgumentException(\"payload is not raw-DEFLATE\"); }","typeGuard":"boolean looksInflatable(byte[] b) { return b != null && b.length > 2 && (b[0] & 0x0F) <= 7; } // first deflate byte hints block type","tryCatchPattern":"try { decoded = decoding.decode(); } catch (Saml2Exception ex) { throw new Saml2AuthenticationException(Saml2Error.invalidResponse(\"not a valid deflated SAML message\"), ex); }","preventionTips":["Match inflate(true) only to HTTP-Redirect/GET bindings; POST messages are not deflated","Check shouldConvertGetRequests matches how your IdP sends responses","Beware URL length limits (~8KB) that truncate deflated GET payloads","Treat inflate failures as rejected authentication attempts, possibly attack traffic"],"tags":["saml","compression","inflate","malformed-input"],"backgroundTag":"invalid-argument-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}