Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,12 @@ In addition to the required settings data (idp, sp), extra settings can be defin
// 'http://www.w3.org/2001/04/xmlenc#sha256'
// 'http://www.w3.org/2001/04/xmldsig-more#sha384'
// 'http://www.w3.org/2001/04/xmlenc#sha512'
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256"
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256",

// If the toolkit receive a message signed with a
// deprecated algoritm (defined at the constant class)
// will raise an error and reject the message
"rejectDeprecatedAlgorithm": true
},

// Contact information template, it is recommended to supply
Expand Down
3 changes: 2 additions & 1 deletion demo-bottle/saml/advanced_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"wantAssertionsEncrypted": false,
"allowSingleLabelDomains": false,
"signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256"
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256",
"rejectDeprecatedAlgorithm": true
},
"contactPerson": {
"technical": {
Expand Down
3 changes: 2 additions & 1 deletion demo-django/saml/advanced_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"wantAssertionsEncrypted": false,
"allowSingleLabelDomains": false,
"signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256"
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256",
"rejectDeprecatedAlgorithm": true
},
"contactPerson": {
"technical": {
Expand Down
3 changes: 2 additions & 1 deletion demo-flask/saml/advanced_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"wantAssertionsEncrypted": false,
"allowSingleLabelDomains": false,
"signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256"
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256",
"rejectDeprecatedAlgorithm": true
},
"contactPerson": {
"technical": {
Expand Down
3 changes: 2 additions & 1 deletion demo_pyramid/demo_pyramid/saml/advanced_settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"wantAssertionsEncrypted": false,
"allowSingleLabelDomains": false,
"signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256"
"digestAlgorithm": "http://www.w3.org/2001/04/xmlenc#sha256",
"rejectDeprecatedAlgorithm": true
},
"contactPerson": {
"technical": {
Expand Down
3 changes: 3 additions & 0 deletions src/onelogin/saml2/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,6 @@ class OneLogin_Saml2_Constants(object):
AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc'
RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5'
RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p'

# Define here the deprecated algorithms
DEPRECATED_ALGORITHMS = [DSA_SHA1, RSA_SHA1, SHA1]
2 changes: 2 additions & 0 deletions src/onelogin/saml2/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ class OneLogin_Saml2_ValidationError(Exception):
WRONG_NUMBER_OF_SIGNATURES = 43
RESPONSE_EXPIRED = 44
AUTHN_CONTEXT_MISMATCH = 45
DEPRECATED_SIGNATURE_METHOD = 46
DEPRECATED_DIGEST_METHOD = 47

def __init__(self, message, code=0, errors=None):
"""
Expand Down
11 changes: 9 additions & 2 deletions src/onelogin/saml2/logout_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ def is_valid(self, request_data, raise_exceptions=False):
if 'lowercase_urlencoding' in request_data.keys():
lowercase_urlencoding = request_data['lowercase_urlencoding']

security = self.__settings.get_security_data()
if self.__settings.is_strict():
res = OneLogin_Saml2_Utils.validate_xml(dom, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
if not isinstance(res, Document):
Expand All @@ -343,8 +344,6 @@ def is_valid(self, request_data, raise_exceptions=False):
OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT
)

security = self.__settings.get_security_data()

current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

# Check NotOnOrAfter
Expand Down Expand Up @@ -395,6 +394,14 @@ def is_valid(self, request_data, raise_exceptions=False):
else:
sign_alg = get_data['SigAlg']

reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False)
if reject_deprecated_alg:
if sign_alg in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS:
raise OneLogin_Saml2_ValidationError(
'Deprecated signature algorithm found: %s' % sign_alg,
OneLogin_Saml2_ValidationError.DEPRECATED_SIGNATURE_METHOD
)

signed_query = 'SAMLRequest=%s' % OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SAMLRequest', lowercase_urlencoding=lowercase_urlencoding)
if 'RelayState' in get_data:
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState', lowercase_urlencoding=lowercase_urlencoding))
Expand Down
11 changes: 9 additions & 2 deletions src/onelogin/saml2/logout_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False):
if 'lowercase_urlencoding' in request_data.keys():
lowercase_urlencoding = request_data['lowercase_urlencoding']

security = self.__settings.get_security_data()
if self.__settings.is_strict():
res = OneLogin_Saml2_Utils.validate_xml(self.document, 'saml-schema-protocol-2.0.xsd', self.__settings.is_debug_active())
if not isinstance(res, Document):
Expand All @@ -98,8 +99,6 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False):
OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT
)

security = self.__settings.get_security_data()

in_response_to = self.get_in_response_to()
# Check if the InResponseTo of the Logout Response matches the ID of the Logout Request (requestId) if provided
if request_id is not None and in_response_to and in_response_to != request_id:
Expand Down Expand Up @@ -145,6 +144,14 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False):
else:
sign_alg = get_data['SigAlg']

reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False)
if reject_deprecated_alg:
if sign_alg in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS:
raise OneLogin_Saml2_ValidationError(
'Deprecated signature algorithm found: %s' % sign_alg,
OneLogin_Saml2_ValidationError.DEPRECATED_SIGNATURE_METHOD
)

signed_query = 'SAMLResponse=%s' % OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'SAMLResponse', lowercase_urlencoding=lowercase_urlencoding)
if 'RelayState' in get_data:
signed_query = '%s&RelayState=%s' % (signed_query, OneLogin_Saml2_Utils.get_encoded_parameter(get_data, 'RelayState', lowercase_urlencoding=lowercase_urlencoding))
Expand Down
25 changes: 24 additions & 1 deletion src/onelogin/saml2/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False):
has_signed_response = '{%s}Response' % OneLogin_Saml2_Constants.NS_SAMLP in signed_elements
has_signed_assertion = '{%s}Assertion' % OneLogin_Saml2_Constants.NS_SAML in signed_elements

security = self.__settings.get_security_data()
if self.__settings.is_strict():
no_valid_xml_msg = 'Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd'
res = OneLogin_Saml2_Utils.validate_xml(
Expand All @@ -130,7 +131,6 @@ def is_valid(self, request_data, request_id=None, raise_exceptions=False):
OneLogin_Saml2_ValidationError.INVALID_XML_FORMAT
)

security = self.__settings.get_security_data()
current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data)

in_response_to = self.get_in_response_to()
Expand Down Expand Up @@ -688,6 +688,9 @@ def process_signed_elements(self):
"""
sign_nodes = self.__query('//ds:Signature')

security = self.__settings.get_security_data()
reject_deprecated_alg = security.get('rejectDeprecatedAlgorithm', False)

signed_elements = []
verified_seis = []
verified_ids = []
Expand Down Expand Up @@ -736,6 +739,26 @@ def process_signed_elements(self):
)
verified_seis.append(sei)

# Check the signature and digest algorithm
if reject_deprecated_alg:
sig_method_node = OneLogin_Saml2_Utils.query(sign_node, './/ds:SignatureMethod')
if sig_method_node:
sig_method = sig_method_node[0].get("Algorithm")
if sig_method in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS:
raise OneLogin_Saml2_ValidationError(
'Deprecated signature algorithm found: %s' % sig_method,
OneLogin_Saml2_ValidationError.DEPRECATED_SIGNATURE_METHOD
)

dig_method_node = OneLogin_Saml2_Utils.query(sign_node, './/ds:DigestMethod')
if dig_method_node:
dig_method = dig_method_node[0].get("Algorithm")
if dig_method in OneLogin_Saml2_Constants.DEPRECATED_ALGORITHMS:
raise OneLogin_Saml2_ValidationError(
'Deprecated digest algorithm found: %s' % dig_method,
OneLogin_Saml2_ValidationError.DEPRECATED_DIGEST_METHOD
)

signed_elements.append(signed_element)

if signed_elements:
Expand Down
3 changes: 3 additions & 0 deletions src/onelogin/saml2/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ def __add_default_values(self):
# Digest Algorithm
self.__security.setdefault('digestAlgorithm', OneLogin_Saml2_Constants.SHA256)

# Reject Deprecated Algorithms
self.__security.setdefault('rejectDeprecatedAlgorithm', False)

# AttributeStatement required by default
self.__security.setdefault('wantAttributeStatement', True)

Expand Down
3 changes: 1 addition & 2 deletions src/onelogin/saml2/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1208,14 +1208,13 @@ def validate_node_sign(signature_node, elem, cert=None, fingerprint=None, finger

@staticmethod
@return_false_on_exception
def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_Saml2_Constants.RSA_SHA256, debug=False):
def validate_binary_sign(signed_query, signature, cert=None, algorithm=OneLogin_Saml2_Constants.RSA_SHA1, debug=False):
"""
Validates signed binary data (Used to validate GET Signature).

:param signed_query: The element we should validate
:type: string


:param signature: The signature that will be validate
:type: string

Expand Down
21 changes: 21 additions & 0 deletions tests/src/OneLogin/saml2_tests/logout_request_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,27 @@ def testIsValidSignUsingX509certMulti(self):
logout_request = OneLogin_Saml2_Logout_Request(settings, request_data['get_data']['SAMLRequest'])
self.assertTrue(logout_request.is_valid(request_data))

def testIsInValidRejectingDeprecatedSignatureAlgorithm(self):
"""
Tests the is_valid method of the OneLogin_Saml2_LogoutRequest
"""
request_data = {
'http_host': 'example.com',
'script_name': 'index.html',
'get_data': {
'SAMLRequest': 'fZJNa+MwEIb/itHdiTz6sC0SQyEsBPoB27KHXoIsj7cGW3IlGfLzV7G7kN1DL2KYmeedmRcdgp7GWT26326JP/FzwRCz6zTaoNbKkSzeKqfDEJTVEwYVjXp9eHpUsKNq9i4640Zyh3xP6BDQx8FZkp1PR3KpqexAl72QmpUCS8SW01IiZz2TVVGD4X1VQYlAsl/oQyKPJAklPIQFzzZEbWNK0YLnlOVA3wqpQCoB7yQ7pWsGq+NKfcQ4q/0+xKXvd8ZNe7Td7AYbw10UxrCbP2aSPbv4Yl/8Qx/R3+SB5bTOoXiDQvFNvjnc7lXrIr75kh+6eYdXPc0jrkMO+/umjXhOtpxP2Q/nJx2/9+uWGbq8X1tV9NqGAW0kzaVvoe1AAJeCSWqYaUVRM2SilKKuqDTpFSlszdcK29RthVm9YriZebYdXpsLdhVAB7VJzif3haYMqqTVcl0JMBR4y+s2zak3sf/4v8l/vlHzBw==',
'RelayState': '_1037fbc88ec82ce8e770b2bed1119747bb812a07e6',
'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
'Signature': 'Ouxo9BV6zmq4yrgamT9EbSKy/UmvSxGS8z26lIMgKOEP4LFR/N23RftdANmo4HafrzSfA0YTXwhKDqbOByS0j+Ql8OdQOes7vGioSjo5qq/Bi+5i6jXwQfphnfcHAQiJL4gYVIifkhhHRWpvYeiysF1Y9J02me0izwazFmoRXr4='
}
}
settings_info = self.loadSettingsJSON('settings8.json')
settings_info['security']['rejectDeprecatedAlgorithm'] = True
settings = OneLogin_Saml2_Settings(settings_info)
logout_request = OneLogin_Saml2_Logout_Request(settings, request_data['get_data']['SAMLRequest'])
self.assertFalse(logout_request.is_valid(request_data))
self.assertEqual('Deprecated signature algorithm found: http://www.w3.org/2000/09/xmldsig#rsa-sha1', logout_request.get_error())

def testGetXML(self):
"""
Tests that we can get the logout request XML directly without
Expand Down
24 changes: 24 additions & 0 deletions tests/src/OneLogin/saml2_tests/logout_response_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,30 @@ def testIsValidSignUsingX509certMulti(self):
logout_response = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
self.assertTrue(logout_response.is_valid(request_data))

def testIsInValidRejectingDeprecatedSignatureAlgorithm(self):
"""
Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
"""
"""
Tests the is_valid method of the OneLogin_Saml2_LogoutResponse
"""
request_data = {
'http_host': 'example.com',
'script_name': 'index.html',
'get_data': {
'SAMLResponse': 'fZHbasJAEIZfJey9ZrNZc1gSodRSBKtQxYveyGQz1kCyu2Q24OM3jS21UHo3p++f4Z+CoGud2th3O/hXJGcNYXDtWkNqapVs6I2yQA0pAx2S8lrtH142Ssy5cr31VtuW3SH/E0CEvW+sYcF6VbLTIktFLMWZgxQR8DSP85wDB4GJGMOqShYVaoBUsOCIPY1kyUahEScacG3Ig/FjiUdyxuOZ4IcoUVGq4vSNBSsk3xjwE3Xx3qkwJD+cz3NtuxBN7WxjPN1F1NLcXdwob77tONiS7bZPm93zenvCqopxgVJmuU50jREsZF4noKWAOuNZJbNznnBky+LTDDVd2S+/dje1m+MVOtfidEER3g8Vt2fsPfiBfmePtsbgCO2A/9tL07TaD1ojEQuXtw0/ouFfD19+AA==',
'RelayState': 'http://stuff.com/endpoints/endpoints/index.php',
'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
'Signature': 'OV9c4R0COSjN69fAKCpV7Uj/yx6/KFxvbluVCzdK3UuortpNMpgHFF2wYNlMSG9GcYGk6p3I8nB7Z+1TQchMWZOlO/StjAqgtZhtpiwPcWryNuq8vm/6hnJ3zMDhHTS7F8KG4qkCXmJ9sQD3Y31UNcuygBwIbNakvhDT5Qo9Nsw='
}
}
settings_info = self.loadSettingsJSON('settings8.json')
settings_info['security']['rejectDeprecatedAlgorithm'] = True
settings = OneLogin_Saml2_Settings(settings_info)
logout_response = OneLogin_Saml2_Logout_Response(settings, request_data['get_data']['SAMLResponse'])
self.assertFalse(logout_response.is_valid(request_data))
self.assertEqual('Deprecated signature algorithm found: http://www.w3.org/2000/09/xmldsig#rsa-sha1', logout_response.get_error())

def testGetXML(self):
"""
Tests that we can get the logout response XML directly without
Expand Down
13 changes: 13 additions & 0 deletions tests/src/OneLogin/saml2_tests/response_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,19 @@ def testIsInValidNoKey(self):
self.assertFalse(response.is_valid(self.get_request_data()))
self.assertEqual('Signature validation failed. SAML Response rejected', response.get_error())

def testIsInValidDeprecatedAlgorithm(self):
"""
Tests the is_valid method of the OneLogin_Saml2_Response
Case Deprecated algorithm used
"""
settings_dict = self.loadSettingsJSON()
settings_dict['security']['rejectDeprecatedAlgorithm'] = True
settings = OneLogin_Saml2_Settings(settings_dict)
xml = self.file_contents(join(self.data_path, 'responses', 'valid_response.xml.base64'))
response = OneLogin_Saml2_Response(settings, xml)
self.assertFalse(response.is_valid(self.get_request_data()))
self.assertEqual('Deprecated signature algorithm found: http://www.w3.org/2000/09/xmldsig#rsa-sha1', response.get_error())

def testIsInValidMultipleAssertions(self):
"""
Tests the is_valid method of the OneLogin_Saml2_Response
Expand Down