-
-
Notifications
You must be signed in to change notification settings - Fork 593
[WIP] v2.x - Nokogiri Refactor Part 6 - Conversion of SignedDocument and final removal of REXML 🎉 #759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v2.x
Are you sure you want to change the base?
[WIP] v2.x - Nokogiri Refactor Part 6 - Conversion of SignedDocument and final removal of REXML 🎉 #759
Conversation
06a205c
to
ae09439
Compare
…efactor' into signed-document-info-refactor
@ahacker1-securesaml, @p- can you help me review this PR? Thanks |
If we are refactoring, I just suggest we remove the Response/SignedDocument class, and make it into a function instead. This OOP style is extremely hard to audit. |
@pitbulk Yes I will help 👍 But I think I can't start before next week. |
Hi, yes I agree I am intending to make the code more functional. This PR was intended firstly to get all tests green on Nokogiri while changing relatively little. I am thinking something like separating
I need to think a bit more about exactly where the I may not have time to look at this further for a few weeks however. |
ok, ResponseValidator -> change to Assertion Validator, we don't care about the Response, since IdPs don't even sign it. Also don't mix this with the signature verification steps. Response (Parser) -> Since the individual assertions are signed, this should return a list of Signed Assertions, and not nesscarily the whole (unsigned) SAML Authentication response. And maybe we should do something like SignedAssertion class? Would remove the SignedDocument class. Let's replace this with something called SignedXML, which cryptographically verifies some bytes of XML at instantianation. Response parser can then accept SignedXML only. Hence, whatever is used for the Response class is always signed. |
@ahacker1-securesaml I agree with all those points, that sounds along the right lines. I will need more time to get my head around it, the code is really spaghetti today 🍝 and the real trick is getting all the tests to pass (fortunately the test suite is quite robust!!) |
# Get the ID of the signed element | ||
# @return [String] The ID of the signed element | ||
def subject_id | ||
# TODO: The error here is problematic, perhaps it can be checked elsewhere |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not recommend exposing an the ID of signed Element. It can't be used for security decisions, since the ID is not guaranteed to be unique & is attacker controlled.
# Get the Reference node | ||
# @return [Nokogiri::XML::Element] The Reference node | ||
def reference_node | ||
signed_info_node.at_xpath('./ds:Reference', { 'ds' => RubySaml::XML::DSIG }) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Load this from the canonicalized_signed_info, since only canonicalized_signed_info was authenticated
return nil unless cert | ||
|
||
fingerprint_alg = RubySaml::XML.hash_algorithm(algorithm).new | ||
fingerprint_alg.hexdigest(cert.to_der).gsub(/[^a-zA-Z0-9]/, '').downcase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Authenticated = cert.to_der, should only use cert.to_der in future, and not cert
The main challenge is to: First, verify the structure, number of signatures, number of elements, etc
And once the signature is validated and we know we can trust the Assertion (because it had a Signature, or its Response has a Signature), use the right XML to keep validating rest of elements and make that the methods that retrieve info (attributes, nameId, etc use the right XML as well).
The main problem is that most of the tests use payloads with invalid signatures and in the current implementation, this is not a problem due the validation order. As making signature validation optional is something that we better don't even add in the code, I will try to start updating the |
@pitbulk making the signatures validatable in the tests would be a huge help to give us flexibility in refactoring. |
First step should be verifying signature IMO. After verifying signature, we can get some signed bytes of XML. Then Assertion re-parses the signed bytes of XML, and verifies the assertion data. That gives us much more flexibility, if we want to test some assertion on some unsigned data, we simply mock the payload as signed. |
@ahacker1-securesaml, If we identify that something is wrong in the XML (xsd validation, number of elements that we want to accept, etc) we save us of checking signatures on XMLs that we gonna consider already invalids. |
I can see the performance benefits there. However I feel strongly that we would be mixing the SAML Assertion processing logic and the Signature logic. Ideally we want to keep them separate as much as possible. And separating logic would help with security |
@ahacker1-securesaml, would you be able to add some context to the following?
Signing the |
Three ways SAML can be signed: Assertions alone: default for most Identity Providers In the responses alone case, I have seem some SAML libraries only verify signatures on SAML Assertions. So it would break their code. For the Assertions & Responses case: an attacker can trivially remove the signature on the response, and the SAML payload would still be valid. For assertions alone case: an attacker can just change whatever they want on the response i.e. statuscode ... So IMO, there's no point in verifying the properties of a SAML responses i.e. checking status code .... It would mean mixing unsigned data (Response) and the signed data (Assertion). That makes it really hard to analyze in terms of security, and has directly lead to vulnerabilities in libraries such as https://github.com/node-saml/node-saml My idea is to only verify whatever is in the (signed) Assertion, be it the AudienceRestriction. These are actually important for security AND we separate the signature verification logic into it's own module. I don't believe it should be part of verifying the assertion logic. |
We still support signed responses, we separate business logic from signature verification steps extracted_signature = extract_signature(untrusted_doc)
signed_bytes = get_signed_bytes(extracted_signature)
# if signed_bytes happens to be a Response, then we get the Assertion from ./saml:Assertion
assertion = ParseAssertion(signed_bytes)
AssertionValidator.validate(assertion) # business logic
return assertion we just don't verify the business logic of a Response that the SAML spec supposedly requires. For example, verifying the StatusCode, or even the Version. |
It would definitely help us to get a library of all the vendor IDP SAML variations in the wild... maybe some other SAML lib already has one? As it stands the current RubySaml test suite is pretty good, it should protect us from a pretty wide range of possible regressions. |
In terms of security, I agree that if the Response is not signed, and the StatusCode is not "protected" anyone can modify it, but in terms of business, if I receive a StatusCode != Success, I don't need to do anything else, I will simply reject this SAMLResponse and don't grant access to the app, the same applies to malformed XMLs received or XMLs wellformed, but that we want to avoid and consider invalids (multiple Assertions, more than 2 Signatures). |
@johnnyshields This PR works fine with omniauth-saml. Anything specific I can help test? |
Just a heads up here--@pitbulk I'm not going to have the bandwidth for several months to take this PR any further--have to focus on running my company. So the last mile will have to be carried by someone else. |
Understood, thanks for your hard work! |
@pitbulk I think we should merge this branch into v2.x--or even master--regardless. I realize there are more refactors needed, but perhaps we can track those in a follow-up ticket? |
@johnnyshields, I was able to reorder the validations and adjust tests and payloads: |
@pitbulk I will release this branch to production tomorrow. I think after it runs stablely for a week we should look at releasing it as v2.x. It's not "perfect" but it's a heck of a lot better than the current v1.x main release. |
@johnnyshields @pitbulk anything I can do to help with v2.x? Happy to lend a hand here or elsewhere. |
This PR resolves the "Parser Differential" exploit vector mentioned below, by migrating to 100% Nokogiri.
https://github.blog/security/sign-in-as-anyone-bypassing-saml-sso-authentication-with-parser-differentials/
TODO: