{"record":{"id":"b8906108dcdd5de8","repo":"spring-projects/spring-security","slug":"failed-to-decode-samlresponse-b89061","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/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/Saml2Utils.java#L171-L207","documentation":"Before base64-decoding a SAML response parameter, Saml2Utils validates the base64 string against a whitelist of acceptable characters (an EncodingConfigurer's checkAcceptable). If the string contains characters not permitted in base64, it throws IllegalArgumentException 'Failed to decode SAMLResponse' rather than producing corrupt bytes.","triggerScenarios":"Passing a SAMLResponse (or SAMLRequest/SAMLLogoutRequest) parameter value to the decoder that contains non-base64 characters — e.g. raw XML, already-decoded text, HTML-escaped entities, or a value with whitespace/newlines outside the accepted set.","commonSituations":"The POST parameter was already URL-decoded or base64-decoded upstream (gateway, logging filter) and the mangled value is re-processed; the wrong form field is read (e.g. RelayState instead of SAMLResponse); the IdP sends content the SP's acceptable-character policy rejects; copy-pasted sample payloads in tests include line breaks or quotes.","solutions":["Ensure exactly one URL-decode of the SAMLResponse parameter before base64 decoding and no manual pre-decoding by intermediate filters.","Inspect the offending parameter value (log a safe prefix) to find illegal characters such as spaces, <, >, or quotes.","Read the correct request parameter name (SAMLResponse, not RelayState or SAMLRequest) in the controller/filter.","If legitimate IdP payloads include characters your validator rejects (e.g. newlines), align isAcceptable() with RFC 4648 base64 plus the line-length rules you actually receive, or update Spring Security to a version with relaxed validation.","Catch IllegalArgumentException around decode and respond with HTTP 400 for malformed SAML responses."],"exampleFix":"// before\nString raw = URLDecoder.decode(request.getParameter(\"SAMLResponse\"), StandardCharsets.UTF_8);\nSaml2Utils.withDecoded(raw).decode(); // IllegalArgumentException\n// after\nString raw = request.getParameter(\"SAMLResponse\"); // container already URL-decodes form params\nSaml2Utils.withDecoded(raw).decode();","handlingStrategy":"validation","validationCode":"static final Pattern B64 = Pattern.compile(\"^[A-Za-z0-9+/=\\\\r\\\\n]+$\");\nboolean looksLikeBase64(String s) { return s != null && B64.matcher(s).matches(); }\nif (!looksLikeBase64(request.getParameter(\"SAMLResponse\"))) { response.sendError(400); return; }","typeGuard":null,"tryCatchPattern":"try {\n    byte[] decoded = Saml2Utils.withDecoded(param).decode();\n} catch (IllegalArgumentException ex) {\n    response.sendError(HttpServletResponse.SC_BAD_REQUEST, \"malformed SAML response\");\n}","preventionTips":["Read the parameter raw — servlet containers already URL-decode form params","Verify you are reading SAMLResponse and not RelayState/SAMLRequest","Audit intermediate filters/proxies for re-encoding of POST bodies or query params"],"tags":["saml2","spring-security","base64","validation"],"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-14T16:17:12.679Z"}