XMLSignature Wrapping attacks (XSW) in SAML based applications like open saml
Secure validation of SAML assertions
SAML document validation consists of the following steps:
1.Parsing the XML document, which includes structure validation based on supplied schema;
2.Digital signature validation, which verified authenticity and integrity of the assertion embedded in
SAML document.
The first step, schema validation, might prevent XML manipulation attacks such as wrapping (it will not if
schema contains “any” extensions, see below). The second step, signature validation, prevents forgery.
Each of these steps has to be successful for the whole validation to complete
.Recommendation:
Always perform schema validation on the XML document prior to using it for any security related purposes.
Validate saml response sent by identity provider for invalid assertions
validate digital signatures
I am just giving the solution to first step validating assertions.
There can be 8 possible XSW attacks starting from XSW1 to XSW8
public boolean validateAssertions(String inputXml) {
boolean isValid = true;
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(inputXml)));
NamespaceContext namespaceContext = new SAMLNamespaceResolver();
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(namespaceContext);
XPathExpression assertionXPath = xpath.compile("/samlp:Response/saml:Assertion");
NodeList assertionXPathResult = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
if (assertionXPathResult.getLength() > 1){
isValid = false;
LOGGER.debug("More than 1 Assertion found");
return;
}else if(assertionXPathResult.getLength()==0){
LOGGER.debug("No assertion found");
isValid = false;
return;
}else{
assertionXPath = xpath.compile("/samlp:Response/saml:Assertion/ds:Signature/saml:Assertion");
NodeList xsw6NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw6 node list length:"+xsw6NodeList.getLength());
if(xsw6NodeList.getLength()>0){
LOGGER.debug("XSW6 security Attack occured ");
isValid = false;
return;
}
assertionXPath = xpath.compile("/samlp:Response/saml:Assertion/ds:Signature/Object/saml:Assertion");
NodeList xsw8NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw8 node list length :"+xsw8NodeList.getLength());
if(xsw8NodeList.getLength()>0){
LOGGER.debug("XSW8 security Attack occured ");
isValid = false;
return;
}
assertionXPath = xpath.compile("/samlp:Response/Extensions/saml:Assertion");
NodeList xsw7NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw7 node list length :"+xsw7NodeList.getLength());
if(xsw7NodeList.getLength()>0){
LOGGER.debug(" XSW8 security Attack occured ");
isValid = false;
return;
}
assertionXPath = xpath.compile("/samlp:Response/saml:Assertion/saml:Assertion");
NodeList xsw4NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw4 node list length :"+xsw4NodeList.getLength());
if(xsw4NodeList.getLength()>0){
LOGGER.debug(" XSW4 security Attack occured ");
isValid = false;
return;
}
}
} catch (ParserConfigurationException e) {
LOGGER.error(" parser configuration error occured ",e);
}
catch(XPathExpressionException e){
LOGGER.error(" IOException occured",e);
}
catch(IOException e){
LOGGER.error(" IOException occured",e);
}
catch(SAXException e){
LOGGER.error(" SAXException occured",e);
}
return isValid;
}
Secure validation of SAML assertions
SAML document validation consists of the following steps:
1.Parsing the XML document, which includes structure validation based on supplied schema;
2.Digital signature validation, which verified authenticity and integrity of the assertion embedded in
SAML document.
The first step, schema validation, might prevent XML manipulation attacks such as wrapping (it will not if
schema contains “any” extensions, see below). The second step, signature validation, prevents forgery.
Each of these steps has to be successful for the whole validation to complete
.Recommendation:
Always perform schema validation on the XML document prior to using it for any security related purposes.
Validate saml response sent by identity provider for invalid assertions
validate digital signatures
I am just giving the solution to first step validating assertions.
There can be 8 possible XSW attacks starting from XSW1 to XSW8
public boolean validateAssertions(String inputXml) {
boolean isValid = true;
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(inputXml)));
NamespaceContext namespaceContext = new SAMLNamespaceResolver();
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(namespaceContext);
XPathExpression assertionXPath = xpath.compile("/samlp:Response/saml:Assertion");
NodeList assertionXPathResult = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
if (assertionXPathResult.getLength() > 1){
isValid = false;
LOGGER.debug("More than 1 Assertion found");
return;
}else if(assertionXPathResult.getLength()==0){
LOGGER.debug("No assertion found");
isValid = false;
return;
}else{
assertionXPath = xpath.compile("/samlp:Response/saml:Assertion/ds:Signature/saml:Assertion");
NodeList xsw6NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw6 node list length:"+xsw6NodeList.getLength());
if(xsw6NodeList.getLength()>0){
LOGGER.debug("XSW6 security Attack occured ");
isValid = false;
return;
}
assertionXPath = xpath.compile("/samlp:Response/saml:Assertion/ds:Signature/Object/saml:Assertion");
NodeList xsw8NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw8 node list length :"+xsw8NodeList.getLength());
if(xsw8NodeList.getLength()>0){
LOGGER.debug("XSW8 security Attack occured ");
isValid = false;
return;
}
assertionXPath = xpath.compile("/samlp:Response/Extensions/saml:Assertion");
NodeList xsw7NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw7 node list length :"+xsw7NodeList.getLength());
if(xsw7NodeList.getLength()>0){
LOGGER.debug(" XSW8 security Attack occured ");
isValid = false;
return;
}
assertionXPath = xpath.compile("/samlp:Response/saml:Assertion/saml:Assertion");
NodeList xsw4NodeList = (NodeList) assertionXPath.evaluate(doc, XPathConstants.NODESET);
LOGGER.debug(" xsw4 node list length :"+xsw4NodeList.getLength());
if(xsw4NodeList.getLength()>0){
LOGGER.debug(" XSW4 security Attack occured ");
isValid = false;
return;
}
}
} catch (ParserConfigurationException e) {
LOGGER.error(" parser configuration error occured ",e);
}
catch(XPathExpressionException e){
LOGGER.error(" IOException occured",e);
}
catch(IOException e){
LOGGER.error(" IOException occured",e);
}
catch(SAXException e){
LOGGER.error(" SAXException occured",e);
}
return isValid;
}
Now check the assertions in calling method:
if( !validateAssertions(rspWrt2.toString())){
throw new InvalidAssertionsException("invalid assetions");
}else{
//validate signatures
}