{"record":{"id":"28569bbd7ab7bbd3","repo":"spring-projects/spring-security","slug":"failed-to-decode-samlresponse-28569b","errorCode":null,"errorMessage":"Failed to decode SAMLResponse","messagePattern":"Failed to decode SAMLResponse","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2Utils.java","lineNumber":189,"sourceCode":"\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// in cases of an incomplete final chunk, ensure the unused bits are zero\n\t\t\t\tswitch (goodChars % 4) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\treturn true;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b1111) == 0;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\treturn (lastGoodCharVal & 0b11) == 0;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvoid checkAcceptable(String ins) {\n\t\t\t\tif (!isAcceptable(ins)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Failed to decode SAMLResponse\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\tstatic class CappedOutputStream extends OutputStream {\n\n\t\tprivate static final long MAX_SIZE = 1024 * 1024;\n\n\t\tprivate final OutputStream delegate;\n\n\t\tprivate int size;\n\n\t\tCappedOutputStream(OutputStream delegate) {\n\t\t\tthis.delegate = delegate;\n\t\t}","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2Utils.java#L171-L207","documentation":"Thrown by Saml2Utils.checkAcceptable after a base64-decoded SAMLResponse fails the acceptability check: the decoded bytes must begin with '<' (an XML document) and contain only acceptable characters. This means the base64 decode succeeded but the result is not a plausible SAML XML payload, usually because the value was corrupted, double-encoded, or not actually a SAMLResponse. It is a defensive check against malformed or malicious input before inflation/parsing.","triggerScenarios":"Passing a SAMLResponse parameter whose base64-decoded bytes do not start with '<', a value that was base64-encoded twice, a truncated or whitespace/newline-corrupted POST body, or sending an artifact/encrypted blob where a plain SAMLResponse is expected.","commonSituations":"IDP and SP disagree on POST vs Redirect binding encoding; a proxy or load balancer re-encodes/re-chunks the SAMLResponse form field; the client sends a deflated-then-encoded value (Redirect binding style) via POST; copy-pasted test values with line breaks.","solutions":["Verify the sender uses HTTP-POST binding: base64 of raw XML, not deflated-then-encoded XML","Log the first decoded byte and confirm it is '<' before delegating; re-encode on the test side if not","Check for intermediaries (proxies, WAFs) altering the SAMLResponse parameter (line wrapping, '+' becoming ' ')","Ensure the form field is posted with proper URL encoding (application/x-www-form-urlencoded) and not truncated"],"exampleFix":"// before (wrong: deflating then encoding for POST binding)\nbyte[] deflated = deflate(samlResponseXml);\nString samlResponse = Base64.getEncoder().encodeToString(deflated);\n// after (POST binding: plain base64 of XML)\nString samlResponse = Base64.getEncoder().encodeToString(samlResponseXml.getBytes(StandardCharsets.UTF_8));","handlingStrategy":"validation","validationCode":"byte[] decoded = Base64.getDecoder().decode(samlResponseParam);\nif (decoded.length == 0 || decoded[0] != '<') {\n    throw new IllegalArgumentException(\"SAMLResponse is not base64-encoded XML\");\n}","typeGuard":null,"tryCatchPattern":"try { /* saml processing */ } catch (IllegalArgumentException ex) {\n    log.warn(\"Malformed SAMLResponse from remote party\", ex);\n    response.sendError(HttpServletResponse.SC_BAD_REQUEST);\n}","preventionTips":["Match binding (POST = plain base64, Redirect = deflate+base64) on both IDP and SP","URL-encode the parameter end-to-end and test through proxies","Sanity-check decoded payloads start with '<' before shipping integrations"],"tags":["saml2","spring-security","base64","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"}