Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Assertion signature is missing in SAML Response when using Google Workspace #852

Closed
michielswaanen opened this issue Mar 1, 2023 · 5 comments

Comments

@michielswaanen
Copy link

michielswaanen commented Mar 1, 2023

Hey team,

It seems that I'm unable to post a question in the GitHub Discussion section, so I'm posting it here. Feel free to move it.

I'm currently trying to implement several identity providers with passport-saml (version: 4.0.2) and node-saml (version: 4.0.3).
Currently, I've successfully implemented Azure AD, OneLogin and Okta. However Google Workspaces doesn't seem to work.

I've been debugging it for several days now, but I need help figuring out what's wrong.

Config

This is currently my config:

Identity Provider:
afbeelding

Software Provider:

const googleOptions = {
    callbackUrl: 'https://xxxx.eu.ngrok.io/authentication/login/sso/reply',
    audience: null,
    issuer: 'https://xxxx.eu.ngrok.io/login/abc',
    entryPoint: 'https://accounts.google.com/o/saml2/idp?idpid=xxxx', 
    cert: fs.readFileSync('./google_workspace_cert.pem', 'utf-8'), 
    forceAuthn: true, 
    wantAssertionsSigned: true, 
    wantAuthnResponseSigned: true,
};

Error

I get the following error:

[Nest] 10344  - 01/03/2023, 20:44:39   ERROR [ExceptionsHandler] Invalid signature
Error: Invalid signature
    at SAML.validatePostResponseAsync (C:\Users\michi\Desktop\saml_code_red\saml\node_modules\@node-saml\node-saml\src\saml.ts:730:9)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Upon further inspection, my SAML Response doesn't contain an assertion signature.

<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
                 Destination="https://XXXX.eu.ngrok.io/authentication/login/sso/reply"
                 ID="_8bd082501f25e5a5e5fd75f47a31c19c"
                 InResponseTo="_fef9a89f8f14ce162982bf7cdbe5b795adab1b7f"
                 IssueInstant="2023-03-01T19:53:55.689Z"
                 Version="2.0"
                 >
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://accounts.google.com/o/saml2?idpid=XXXX</saml2:Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <ds:Reference URI="#_8bd082501f25e5a5e5fd75f47a31c19c">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <ds:DigestValue>XXXX</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>XXXX</ds:SignatureValue>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509SubjectName>ST=California,C=US,OU=Google For Work,CN=Google,L=Mountain View,O=Google Inc.</ds:X509SubjectName>
                <ds:X509Certificate>XXXX</ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>
    <saml2p:Status>
        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
    </saml2p:Status>
    <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
                     ID="_7c75c9695c0e97ff4539767efb2fd011"
                     IssueInstant="2023-03-01T19:53:55.689Z"
                     Version="2.0"
                     >
        <saml2:Issuer>https://accounts.google.com/o/saml2?idpid=XXXX</saml2:Issuer>
        <saml2:Subject>
            <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">X@X.X</saml2:NameID>
            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml2:SubjectConfirmationData InResponseTo="_fef9a89f8f14ce162982bf7cdbe5b795adab1b7f"
                                               NotOnOrAfter="2023-03-01T19:58:55.689Z"
                                               Recipient="https://XXXX.eu.ngrok.io/authentication/login/sso/reply"
                                               />
            </saml2:SubjectConfirmation>
        </saml2:Subject>
        <saml2:Conditions NotBefore="2023-03-01T19:48:55.689Z"
                          NotOnOrAfter="2023-03-01T19:58:55.689Z"
                          >
            <saml2:AudienceRestriction>
                <saml2:Audience>https://XXXX.eu.ngrok.io/login/abc</saml2:Audience>
            </saml2:AudienceRestriction>
        </saml2:Conditions>
        <saml2:AttributeStatement>
            <saml2:Attribute Name="email">
                <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xs:anyType"
                                      >X@X.X</saml2:AttributeValue>
            </saml2:Attribute>
            <saml2:Attribute Name="firstName">
                <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xs:anyType"
                                      >X</saml2:AttributeValue>
            </saml2:Attribute>
            <saml2:Attribute Name="lastName">
                <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
                                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                      xsi:type="xs:anyType"
                                      >X</saml2:AttributeValue>
            </saml2:Attribute>
        </saml2:AttributeStatement>
        <saml2:AuthnStatement AuthnInstant="2023-02-27T14:57:06.000Z"
                              SessionIndex="_7c75c9695c0e97ff4539767efb2fd011"
                              >
            <saml2:AuthnContext>
                <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef>
            </saml2:AuthnContext>
        </saml2:AuthnStatement>
    </saml2:Assertion>
</saml2p:Response>

Changes

I added some console.log statements to better understand what's going wrong.

https://github.com/node-saml/node-saml/blob/f86252bcbaa4435159d121d2e342ec89f94ad183/src/xml.ts#L77-L95

The xpathSigQuery variable printed out the following:

xpathSigQuery: .//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#' and descendant::*[local-name(.)='Reference' and @URI='#_7c75c9695c0e97ff4539767efb2fd011']]

The most interesting part is the @URI='#_7c75c9695c0e97ff4539767efb2fd011' part which doesn't match the ID property of <Response> tag, but the one from the <Assertion> tag. I did the same test with the other identity providers; they used the ID of the <Response> tag, not the <Assertion>.

This results in no signature being found. This is just an observation. The weird part, in my opinion, is that the doesn't contain a value.

I'm currently stuck, any thought, new insight or push in the right direction is appreciated!

@srd90
Copy link

srd90 commented Mar 2, 2023

You have configured this:

    wantAssertionsSigned: true, 
    wantAuthnResponseSigned: true,

(which is also default cofiguration for @node-saml/passport-saml / @node-saml/node-saml)

This bug report is duplicate of e.g. these (read background information from those and from linked PRs and from release notes):

  1. [BUG] When validating the SAML Response, it is assumed that the SAML Response ID attribute is the same as the SAML Assertion ID attribute node-saml#211
  2. [BUG] Invalid document signature after upgrading from 2.2.0 to 4.0.1 #816
  3. [BUG] After an upgrade from passport-saml to @node-saml/passport-saml, assertion validation stopped working with HTTP-POST binding with an error: SAML.validatePostResponseAsync, Invalid document signature #839

Long story short. Your configuration expects that IdP signs response (wantAuthnReponseSigned = true) and nested assertion (wantAssertionSigned = true).
Your IdP is configured to sign only top level (sign response). Reconfigure your IdP to sign also assertion (sign assertion). If you are not able to alter IdP configuration change passport-saml side configuration (in this particular case change wantAssertionsSigned = false.

SAML specification provides answer to possible questions about security implications (one should always go through SAML / OAuth / ... specifications prior to implement/use those protocols so I assume you already have those SAML specs available).

fwiw, consider also enabling audience validation (due reasons described e.g. here #137 and in SAML specs for extra layer of security). It is now:

  audience: null

changit it (based on provided example repsonse) to https://XXXX.eu.ngrok.io/login/abc (which seems to be same as issuer and by default audience would be validated to have same value as issuer if no value is provided or if audience validation is not explicitly disabled).

Side note: @markstos @cjbarth could you check whats wrong with passport-saml's Discussions because issue description says:

It seems that I'm unable to post a question in the GitHub Discussion section, so I'm posting it here.

@michielswaanen
Copy link
Author

Hey @srd90

Thanks for getting back to me.
I will make the audience similar to the issuer parameter.

Your IdP is configured to sign only top level (sign response). Reconfigure your IdP to sign also assertion (sign assertion). If you are not able to alter IdP configuration change passport-saml side configuration (in this particular case change wantAssertionsSigned = false.

I can't seem to find a setting in Google Workspaces that would enable signed assertions. Isn't it unsafe to set wantAssertionsSigned to false?

@srd90
Copy link

srd90 commented Mar 2, 2023

Isn't it unsafe to set wantAssertionsSigned to false?

I urge you to seek definitive answer from SAML specs and how XML signatures work etc. Along the way you pick quite a lot information which is usable in other contexts also.

Having said that Shibboleth IdP documentation says e.g. this at "SAML 2.0 Browser SSO profile" notes:

The default value of signResponses for this profile is "true", in keeping with modern best practice. As long as one of the response or assertion are signed, use of the profile is "safe" in terms of authentication integrity, but there are vulnerabilities in XML Encryption that make signing responses advisable when the most common encryption algorithms are used.

source: https://shibboleth.atlassian.net/wiki/spaces/IDP4/pages/1265631694/SAML2SSOConfiguration#Notes (referenced 02 Mar 2023)

Furthermore Response level signature covers content of whole Response content (including Assertion).
If XML signature validation process doesn't have any flaws (like recent GHSA-m974-647v-whv7 ) then it should be sufficient (If only assertion would be signed then few elements like Response/Status could be altered by attacker). I do not know your uses cases. If you detach Assertion and pass it to some other program block (other than passport-saml / node-saml) then you should have it signed so that some other program block can verify its validity.

btw. this:

... there are vulnerabilities in XML Encryption that make signing responses advisable when the most common encryption algorithms are used

refers to situation where assertions are encrypted. If assertions are encrypted SAML SP must first decrypt assertion before it can proceed to validate integrity of assertion. Problem is that if encryption is done with algorithm which has vulnerabilities and attacker has altered encrypted content to make use of those vulnerabilities (which can be anything from DoSish to use-your-imagination) there isn't anything to block this situation (if Response would have been signed and if SAML SP would have been configured to require signed Response then SAML SP would not have tried to decrypt altered encrypted content).

@cjbarth
Copy link
Collaborator

cjbarth commented Jun 1, 2023

Closing due to inactivity. If this is still an open issue, please reply to reopen the issue.

@markstos
Copy link
Contributor

markstos commented Jun 8, 2023

Side note: @markstos @cjbarth could you check whats wrong with passport-saml's Discussions because issue description says

@srd90 I reviewed the project settings and don't see anything preventing this user from posting, but I do see several users posting there successfully. Because this is more of a support issue than a bug report, I'm still going to move this there now.

@node-saml node-saml locked and limited conversation to collaborators Jun 8, 2023
@markstos markstos converted this issue into discussion #865 Jun 8, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants