Skip to content

Commit 61eacb4

Browse files
committed
Security improvements. Use of tagid to prevent XPath injection. Disable DTD on _parse_etree (fromstring defusedxml) method.
1 parent d579a81 commit 61eacb4

File tree

3 files changed

+25
-12
lines changed

3 files changed

+25
-12
lines changed

src/onelogin/saml2/response.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -737,38 +737,44 @@ def __query_assertion(self, xpath_expr):
737737
signature_expr = '/ds:Signature/ds:SignedInfo/ds:Reference'
738738
signed_assertion_query = '/samlp:Response' + assertion_expr + signature_expr
739739
assertion_reference_nodes = self.__query(signed_assertion_query)
740+
tagid = None
740741

741742
if not assertion_reference_nodes:
742743
# Check if the message is signed
743744
signed_message_query = '/samlp:Response' + signature_expr
744745
message_reference_nodes = self.__query(signed_message_query)
745746
if message_reference_nodes:
746747
message_id = message_reference_nodes[0].get('URI')
747-
final_query = "/samlp:Response[@ID='%s']/" % message_id[1:]
748+
final_query = "/samlp:Response[@ID=$tagid]/"
749+
tagid = message_id[1:]
748750
else:
749751
final_query = "/samlp:Response"
750752
final_query += assertion_expr
751753
else:
752754
assertion_id = assertion_reference_nodes[0].get('URI')
753-
final_query = '/samlp:Response' + assertion_expr + "[@ID='%s']" % assertion_id[1:]
755+
final_query = '/samlp:Response' + assertion_expr + "[@ID=$tagid]"
756+
tagid = assertion_id[1:]
754757
final_query += xpath_expr
755-
return self.__query(final_query)
758+
return self.__query(final_query, tagid)
756759

757-
def __query(self, query):
760+
def __query(self, query, tagid=None):
758761
"""
759762
Extracts nodes that match the query from the Response
760763
761764
:param query: Xpath Expresion
762765
:type query: String
763766
767+
:param tagid: Tag ID
768+
:type query: String
769+
764770
:returns: The queried nodes
765771
:rtype: list
766772
"""
767773
if self.encrypted:
768774
document = self.decrypted_document
769775
else:
770776
document = self.document
771-
return OneLogin_Saml2_XML.query(document, query)
777+
return OneLogin_Saml2_XML.query(document, query, None, tagid)
772778

773779
def __decrypt_assertion(self, xml):
774780
"""
@@ -817,7 +823,7 @@ def __decrypt_assertion(self, xml):
817823
if not uri.startswith('#'):
818824
break
819825
uri = uri.split('#')[1]
820-
encrypted_key = OneLogin_Saml2_XML.query(encrypted_assertion_nodes[0], './xenc:EncryptedKey[@Id="' + uri + '"]')
826+
encrypted_key = OneLogin_Saml2_XML.query(encrypted_assertion_nodes[0], './xenc:EncryptedKey[@Id=$tagid]', None, uri)
821827
if encrypted_key:
822828
keyinfo.append(encrypted_key[0])
823829

src/onelogin/saml2/utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from copy import deepcopy
1414
import calendar
1515
from datetime import datetime
16-
from defusedxml.lxml import fromstring
1716
from hashlib import sha1, sha256, sha384, sha512
1817
from isodate import parse_duration as duration_parser
1918
import re
@@ -684,7 +683,7 @@ def decrypt_element(encrypted_data, key, debug=False, inplace=False):
684683
"""
685684

686685
if isinstance(encrypted_data, Element):
687-
encrypted_data = fromstring(str(encrypted_data.toxml()))
686+
encrypted_data = OneLogin_Saml2_XML.to_etree(str(encrypted_data.toxml()))
688687
if not inplace and isinstance(encrypted_data, OneLogin_Saml2_XML._element_class):
689688
encrypted_data = deepcopy(encrypted_data)
690689
elif isinstance(encrypted_data, OneLogin_Saml2_XML._text_class):

src/onelogin/saml2/xml_utils.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def to_etree(xml):
6262
if isinstance(xml, OneLogin_Saml2_XML._element_class):
6363
return xml
6464
if isinstance(xml, OneLogin_Saml2_XML._text_class):
65-
return OneLogin_Saml2_XML._parse_etree(xml)
65+
return OneLogin_Saml2_XML._parse_etree(xml, forbid_dtd=True)
6666

6767
raise ValueError('unsupported type %r' % type(xml))
6868

@@ -101,7 +101,7 @@ def validate_xml(xml, schema, debug=False):
101101
return xml
102102

103103
@staticmethod
104-
def query(dom, query, context=None):
104+
def query(dom, query, context=None, tagid=None):
105105
"""
106106
Extracts nodes that match the query from the Element
107107
@@ -114,13 +114,21 @@ def query(dom, query, context=None):
114114
:param context: Context Node
115115
:type: DOMElement
116116
117+
:param tagid: Tag ID
118+
:type query: String
119+
117120
:returns: The queried nodes
118121
:rtype: list
119122
"""
120123
if context is None:
121-
return dom.xpath(query, namespaces=OneLogin_Saml2_Constants.NSMAP)
124+
source = dom
125+
else:
126+
source = context
127+
128+
if tagid is None:
129+
return source.xpath(query, namespaces=OneLogin_Saml2_Constants.NSMAP)
122130
else:
123-
return context.xpath(query, namespaces=OneLogin_Saml2_Constants.NSMAP)
131+
return source.xpath(query, tagid=tagid, namespaces=OneLogin_Saml2_Constants.NSMAP)
124132

125133
@staticmethod
126134
def cleanup_namespaces(tree_or_element, top_nsmap=None, keep_ns_prefixes=None):

0 commit comments

Comments
 (0)