@@ -149,17 +149,27 @@ public function isValid($requestId = null)
149149
150150 if ($ security ['wantNameIdEncrypted ' ]) {
151151 $ encryptedIdNodes = $ this ->_queryAssertion ('/saml:Subject/saml:EncryptedID/xenc:EncryptedData ' );
152- if ($ encryptedIdNodes ->length == 0 ) {
152+ if ($ encryptedIdNodes ->length != 1 ) {
153153 throw new Exception ("The NameID of the Response is not encrypted and the SP requires it " );
154154 }
155155 }
156156
157+ // Validate Conditions element exists
158+ if (!$ this ->checkOneCondition ()) {
159+ throw new Exception ("The Assertion must include a Conditions element " );
160+ }
161+
157162 // Validate Asserion timestamps
158163 $ validTimestamps = $ this ->validateTimestamps ();
159164 if (!$ validTimestamps ) {
160165 throw new Exception ('Timing issues (please check your clock settings) ' );
161166 }
162167
168+ // Validate AuthnStatement element exists and is unique
169+ if (!$ this ->checkOneAuthnStatement ()) {
170+ throw new Exception ("The Assertion must include an AuthnStatement element " );
171+ }
172+
163173 // EncryptedAttributes are not supported
164174 $ encryptedAttributeNodes = $ this ->_queryAssertion ('/saml:AttributeStatement/saml:EncryptedAttribute ' );
165175 if ($ encryptedAttributeNodes ->length > 0 ) {
@@ -169,7 +179,9 @@ public function isValid($requestId = null)
169179 // Check destination
170180 if ($ this ->document ->documentElement ->hasAttribute ('Destination ' )) {
171181 $ destination = trim ($ this ->document ->documentElement ->getAttribute ('Destination ' ));
172- if (!empty ($ destination )) {
182+ if (empty ($ destination )) {
183+ throw new Exception ("The response has an empty Destination value " );
184+ } else {
173185 if (strpos ($ destination , $ currentURL ) !== 0 ) {
174186 $ currentURLNoRouted = OneLogin_Saml2_Utils::getSelfURLNoQuery ();
175187
@@ -316,6 +328,34 @@ public function checkStatus()
316328 }
317329 }
318330
331+ /**
332+ * Checks that the samlp:Response/saml:Assertion/saml:Conditions element exists and is unique.
333+ *
334+ * @return boolean true if the Conditions element exists and is unique
335+ */
336+ public function checkOneCondition () {
337+ $ entries = $ this ->_queryAssertion ("/saml:Conditions " );
338+ if ($ entries ->length == 1 ) {
339+ return true ;
340+ } else {
341+ return false ;
342+ }
343+ }
344+
345+ /**
346+ * Checks that the samlp:Response/saml:Assertion/saml:AuthnStatement element exists and is unique.
347+ *
348+ * @return boolean true if the AuthnStatement element exists and is unique
349+ */
350+ public function checkOneAuthnStatement () {
351+ $ entries = $ this ->_queryAssertion ("/saml:AuthnStatement " );
352+ if ($ entries ->length == 1 ) {
353+ return true ;
354+ } else {
355+ return false ;
356+ }
357+ }
358+
319359 /**
320360 * Gets the audiences.
321361 *
@@ -348,11 +388,15 @@ public function getIssuers()
348388 $ responseIssuer = $ this ->_query ('/samlp:Response/saml:Issuer ' );
349389 if ($ responseIssuer ->length == 1 ) {
350390 $ issuers [] = $ responseIssuer ->item (0 )->textContent ;
391+ } else {
392+ throw new Exception ("Issuer of the Response not found or multiple. " );
351393 }
352394
353395 $ assertionIssuer = $ this ->_queryAssertion ('/saml:Issuer ' );
354396 if ($ assertionIssuer ->length == 1 ) {
355397 $ issuers [] = $ assertionIssuer ->item (0 )->textContent ;
398+ } else {
399+ throw new Exception ("Issuer of the Assertion not found or multiple. " );
356400 }
357401
358402 return array_unique ($ issuers );
@@ -391,9 +435,20 @@ public function getNameIdData()
391435 throw new Exception ("Not NameID found in the assertion of the Response " );
392436 }
393437 } else {
438+ if (empty ($ nameId ->nodeValue )) {
439+ throw new Exception ("An empty NameID value found " );
440+ }
394441 $ nameIdData ['Value ' ] = $ nameId ->nodeValue ;
442+
395443 foreach (array ('Format ' , 'SPNameQualifier ' , 'NameQualifier ' ) as $ attr ) {
396444 if ($ nameId ->hasAttribute ($ attr )) {
445+ if ($ attr == 'SPNameQualifier ' ) {
446+ $ spData = $ this ->_settings ->getSPData ();
447+ $ spEntityId = $ spData ['entityId ' ];
448+ if ($ spEntityId != $ nameId ->getAttribute ($ attr )) {
449+ throw new Exception ("The SPNameQualifier value mistmatch the SP entityID value. " );
450+ }
451+ }
397452 $ nameIdData [$ attr ] = $ nameId ->getAttribute ($ attr );
398453 }
399454 }
@@ -476,11 +531,18 @@ public function getAttributes()
476531 */
477532
478533 $ entries = $ this ->_queryAssertion ('/saml:AttributeStatement/saml:Attribute ' );
534+ $ processedAttrNames = array ();
479535
480536 /** @var $entry DOMNode */
481537 foreach ($ entries as $ entry ) {
482538 $ attributeName = $ entry ->attributes ->getNamedItem ('Name ' )->nodeValue ;
483539
540+ if (in_array ($ attributeName , $ processedAttrNames )) {
541+ throw new Exception ("Found an Attribute element with duplicated Name " );
542+ } else {
543+ $ processedAttrNames [] = $ attributeName ;
544+ }
545+
484546 $ attributeValues = array ();
485547 foreach ($ entry ->childNodes as $ childNode ) {
486548 $ tagName = ($ childNode ->prefix ? $ childNode ->prefix .': ' : '' ) . 'AttributeValue ' ;
@@ -503,7 +565,15 @@ public function validateNumAssertions()
503565 {
504566 $ encryptedAssertionNodes = $ this ->document ->getElementsByTagName ('EncryptedAssertion ' );
505567 $ assertionNodes = $ this ->document ->getElementsByTagName ('Assertion ' );
506- return ($ assertionNodes ->length + $ encryptedAssertionNodes ->length == 1 );
568+
569+ $ valid = $ assertionNodes ->length + $ encryptedAssertionNodes ->length == 1 ;
570+
571+ if ($ this ->encrypted ) {
572+ $ assertionNodes = $ this ->decryptedDocument ->getElementsByTagName ('Assertion ' );
573+ $ valid = $ valid && $ assertionNodes ->length == 1 ;
574+ }
575+
576+ return $ valid ;
507577 }
508578
509579 /**
@@ -543,7 +613,7 @@ public function processSignedElements()
543613 $ verifiedIds [] = $ idValue ;
544614
545615 $ ref = $ signNode ->getElementsByTagName ('Reference ' );
546- if ($ ref ->length > 0 ) {
616+ if ($ ref ->length == 1 ) {
547617 $ ref = $ ref ->item (0 );
548618 $ sei = $ ref ->getAttribute ('URI ' );
549619 if (!empty ($ sei )) {
@@ -558,6 +628,8 @@ public function processSignedElements()
558628 }
559629 $ verifiedSeis [] = $ sei ;
560630 }
631+ } else {
632+ throw new Exception ('Unexpected number of Reference nodes found for signature. SAML Response rejected. ' );
561633 }
562634 $ signedElements [] = $ signedElement ;
563635 }
0 commit comments