{"record":{"id":"d4bdd97ebf011fd6","repo":"spring-projects/spring-security","slug":"failed-to-decode-samlresponse-d4bdd9","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/metadata/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/metadata/Saml2Utils.java#L171-L207","documentation":"Saml2Utils' internal decoding pipeline validates that the decoded bytes look like an acceptable SAMLResponse before producing it. checkAcceptable throws this IllegalArgumentException when the decoded payload fails the acceptability checks (e.g. doesn't start with expected XML prefix / fails size or content validation), guarding against malformed or malicious input.","triggerScenarios":"Calling the Saml2Utils decode/samlDecode pipeline with a base64 payload whose decoded content is empty, truncated, or does not match the expected SAML XML structure, causing the internal IllegalArgumentException.","commonSituations":"IdP posts a non-XML or garbage SAMLResponse; double- or missing-base64-encoding of the parameter; truncated POST body due to proxy limits; an attacker or misconfigured IdP sending crafted payloads; tests pasting wrong sample payloads.","solutions":["Confirm the SAMLResponse POST parameter is exactly once base64-decoded into well-formed XML starting with the expected XML declaration/saml prefix","Log and inspect the decoded bytes to see what content actually arrived","Check the IdP is configured to use the HTTP-POST binding for responses","Inspect proxies/load balancers for body truncation or re-encoding","If the payload is legitimate and still rejected, compare against the library's isAcceptable constraints (prefix/size) and file an issue"],"exampleFix":"// before: blindly decoding\nString xml = Saml2Utils.samlDecode(encodedResponse);\n// after: pre-validate the parameter\nif (encodedResponse == null || encodedResponse.isBlank()) {\n    throw new Saml2Exception(\"Missing SAMLResponse parameter\");\n}\nString xml = Saml2Utils.samlDecode(encodedResponse);","handlingStrategy":"validation","validationCode":"boolean looksLikeSamlResponse(String b64) {\n    if (b64 == null || b64.isBlank()) return false;\n    try {\n        String xml = new String(Base64.getMimeDecoder().decode(b64), StandardCharsets.UTF_8);\n        return xml.contains(\"<saml2p:Response\") || xml.contains(\"<samlp:Response\");\n    } catch (IllegalArgumentException ex) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n    byte[] decoded = Saml2Utils.samlDecode(param);\n} catch (IllegalArgumentException | Saml2Exception ex) {\n    auditLog.malformedSamlResponse(request.getRemoteAddr());\n    throw new Saml2Exception(\"Malformed SAMLResponse from IdP\", ex);\n}","preventionTips":["Validate the base64 payload decodes to XML containing a samlp:Response element before processing","Check the IdP binding configuration (POST binding for responses)","Watch for proxies truncating large POST bodies"],"tags":["saml","decoding","validation","base64"],"backgroundTag":"schema-validation-failed","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}