From 56349459d2b51b5ffed3adc8d58194559ea830a1 Mon Sep 17 00:00:00 2001 From: "H.J. van Meerveld" Date: Mon, 22 Mar 2021 15:08:13 +0100 Subject: [PATCH] Add WantAssertionsSigned (#536) Add tests for WantAssertionsSigned Co-authored-by: Chris Barth --- README.md | 1 + src/passport-saml/saml.ts | 12 +++- src/passport-saml/types.ts | 1 + ...d.assertion-invalidly-signed-encrypted.xml | 16 +++++ ...root-signed.assertion-invalidly-signed.xml | 47 +++++++++++++++ ...ot-signed.assertion-unsigned-encrypted.xml | 16 +++++ test/test-signatures.spec.ts | 58 +++++++++++++++++-- test/tests.spec.ts | 19 ++++++ 8 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed-encrypted.xml create mode 100644 test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed.xml create mode 100644 test/static/signatures/valid/response.root-signed.assertion-unsigned-encrypted.xml diff --git a/README.md b/README.md index 70d9cfa9..0e0896fe 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,7 @@ type Profile = { - `additionalParams`: dictionary of additional query params to add to all requests; if an object with this key is passed to `authenticate`, the dictionary of additional query params will be appended to those present on the returned URL, overriding any specified by initialization options' additional parameters (`additionalParams`, `additionalAuthorizeParams`, and `additionalLogoutParams`) - `additionalAuthorizeParams`: dictionary of additional query params to add to 'authorize' requests - `identifierFormat`: optional name identifier format to request from identity provider (default: `urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress`) +- `wantAssertionsSigned`: if truthy, add `WantAssertionsSigned="true"` to the metadata, to specify that the IdP should always sign the assertions. - `acceptedClockSkewMs`: Time in milliseconds of skew that is acceptable between client and server when checking `OnBefore` and `NotOnOrAfter` assertion condition validity timestamps. Setting to `-1` will disable checking these conditions entirely. Default is `0`. - `attributeConsumingServiceIndex`: optional `AttributeConsumingServiceIndex` attribute to add to AuthnRequest to instruct the IDP which attribute set to attach to the response ([link](http://blog.aniljohn.com/2014/01/data-minimization-front-channel-saml-attribute-requests.html)) - `disableRequestedAuthnContext`: if truthy, do not request a specific authentication context. This is [known to help when authenticating against Active Directory](https://github.com/node-saml/passport-saml/issues/226) (AD FS) servers. diff --git a/src/passport-saml/saml.ts b/src/passport-saml/saml.ts index 579aa7c3..247641e6 100644 --- a/src/passport-saml/saml.ts +++ b/src/passport-saml/saml.ts @@ -147,6 +147,7 @@ class SAML { ctorOptions.identifierFormat === undefined ? "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" : ctorOptions.identifierFormat, + wantAssertionsSigned: ctorOptions.wantAssertionsSigned ?? false, authnContext: ctorOptions.authnContext ?? [ "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport", ], @@ -789,7 +790,10 @@ class SAML { } if (assertions.length == 1) { - if (!validSignature && !this.validateSignature(xml, assertions[0], certs)) { + if ( + (this.options.wantAssertionsSigned || !validSignature) && + !this.validateSignature(xml, assertions[0], certs) + ) { throw new Error("Invalid signature"); } return await this.processValidlySignedAssertionAsync( @@ -820,7 +824,7 @@ class SAML { if (decryptedAssertions.length != 1) throw new Error("Invalid EncryptedAssertion content"); if ( - !validSignature && + (this.options.wantAssertionsSigned || !validSignature) && !this.validateSignature(decryptedXml, decryptedAssertions[0], certs) ) { throw new Error("Invalid signature from encrypted assertion"); @@ -1423,6 +1427,10 @@ class SAML { metadata.EntityDescriptor.SPSSODescriptor.NameIDFormat = this.options.identifierFormat; } + if (this.options.wantAssertionsSigned) { + metadata.EntityDescriptor.SPSSODescriptor["@WantAssertionsSigned"] = true; + } + metadata.EntityDescriptor.SPSSODescriptor.AssertionConsumerService = { "@index": "1", "@isDefault": "true", diff --git a/src/passport-saml/types.ts b/src/passport-saml/types.ts index eacd63a1..420fd871 100644 --- a/src/passport-saml/types.ts +++ b/src/passport-saml/types.ts @@ -64,6 +64,7 @@ export interface SamlOptions extends SamlSigningOptions, MandatorySamlOptions { idpIssuer?: string; audience?: string; scoping?: SamlScopingConfig; + wantAssertionsSigned?: boolean; // InResponseTo Validation validateInResponseTo: boolean; diff --git a/test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed-encrypted.xml b/test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed-encrypted.xml new file mode 100644 index 00000000..e1f4fa51 --- /dev/null +++ b/test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed-encrypted.xml @@ -0,0 +1,16 @@ + + + https://evil-corp.com + + + xa3vNTi+LNOhWxNoA+Hew8cIAqY=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 + + \ No newline at end of file diff --git a/test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed.xml b/test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed.xml new file mode 100644 index 00000000..4efb92b3 --- /dev/null +++ b/test/static/signatures/invalid/response.root-signed.assertion-invalidly-signed.xml @@ -0,0 +1,47 @@ + + + https://evil-corp.com + + + BbH821SAM/jagHd7Pql43JU71Do=W+ALJZHsfn7ZXGBx9wqgFsO8lAi80+908NddeL32g0hknAM9bDLmEXzGacscbs9g4XeIZyO+wPC28Y2MA0VXv/E13VOm54MxJdXiqF1wqfHnYHFP1TsyAR6CIJuux8yhijopoh1cJXWTzbUWRDDcgmTnUwA6ZBKl491hxuhWvN4dsLg3M0n0R2hPuZf1ywCVBR9vo4w7Hssw4hfSEDTyGDw0WTDnh8Xzaw6dzrKFbsIlVKRDWwG2FKpIdMJhoyMf4/947JhIWPE4T0EB73+/Mv7/LmJlimQTK2kbMSainQtZrdsVXYH7ErxMsYmRPiaXd33YrxOVaK7IML8PI9xe1Q== +MIIDtTCCAp2gAwIBAgIJAKg4VeVcIDz1MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTUwODEzMDE1NDIwWhcNMTUwOTEyMDE1NDIwWjBFMQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxG3ouM7U+fXbJt69X1H6d4UNg/uRr06pFuU9RkfIwNC+yaXyptqB3ynXKsL7BFt4DCd0fflRvJAx3feJIDp16wN9GDVHcufWMYPhh2j5HcTW/j9JoIJzGhJyvO00YKBt+hHy83iN1SdChKv5y0iSyiPP5GnqFw+ayyHoM6hSO0PqBou1Xb0ZSIE+DHosBnvVna5w2AiPY4xrJl9yZHZ4Q7DfMiYTgstjETio4bX+6oLiBnYktn7DjdEslqhffVme4PuBxNojI+uCeg/sn4QVLd/iogMJfDWNuLD8326Mi/FE9cCRvFlvAiMSaebMI3zPaySsxTK7Zgj5TpEbmbHI9wIDAQABo4GnMIGkMB0GA1UdDgQWBBSVGgvoW4MhMuzBGce29PY8vSzHFzB1BgNVHSMEbjBsgBSVGgvoW4MhMuzBGce29PY8vSzHF6FJpEcwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKg4VeVcIDz1MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJu1rqs+anD74dbdwgd3CnqnQsQDJiEXmBhG2leaGt3ve9b/9gKaJg2pyb2NyppDe1uLqh6nNXDuzg1oNZrPz5pJL/eCXPl7FhxhMUi04TtLf8LeNTCIWYZiFuO4pmhohHcv8kRvYR1+6SkLTC8j/TZerm7qvesSiTQFNapa1eNdVQ8nFwVkEtWl+JzKEM1BlRcn42sjJkijeFp7DpI7pU+PnYeiaXpRv5pJo8ogM1iFxN+SnfEs0EuQ7fhKIG9aHKi7bKZ7L6SyX7MDIGLeulEU6lf5D9BfXNmcMambiS0pXhL2QXajt96UBq8FT2KNXY8XNtR4y6MyyCzhaiZZcc8= + + + + + https://evil-corp.com + + + frcmIha8lU6d04GedZS99GMSZr0=Cc6ARuJvTbrP9/3JieRPQD0I5bj2WGzSByswheohtwEjQxCviMBLIqfAL92E+BMi3DRqbFFL0upuFpSXbZiLUyrAWaGCs0yvHEF1w7q9i/EdCRND33IbyJCluCBkNmOmduP1hF3+Duf/MduL2FShV3INsynl5awW1aLNYZo1sBk0dFuJVTLjJIhoqihhD6yqXSZhmyI7lWWBnrUyXR0SKyrmrLfgjhZsNobibC8xqHTgeXsDiWJeHGHyaU0uRk3P0vUAJsjy0RA9J5rEkpLhMQ3T0G/0QODVhf+IPImwR6Aasw7kUXYL4v/iO2RQEM0i+l/UrM2mj55oDyrky5jRYw== +MIIDtTCCAp2gAwIBAgIJAKg4VeVcIDz1MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTUwODEzMDE1NDIwWhcNMTUwOTEyMDE1NDIwWjBFMQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxG3ouM7U+fXbJt69X1H6d4UNg/uRr06pFuU9RkfIwNC+yaXyptqB3ynXKsL7BFt4DCd0fflRvJAx3feJIDp16wN9GDVHcufWMYPhh2j5HcTW/j9JoIJzGhJyvO00YKBt+hHy83iN1SdChKv5y0iSyiPP5GnqFw+ayyHoM6hSO0PqBou1Xb0ZSIE+DHosBnvVna5w2AiPY4xrJl9yZHZ4Q7DfMiYTgstjETio4bX+6oLiBnYktn7DjdEslqhffVme4PuBxNojI+uCeg/sn4QVLd/iogMJfDWNuLD8326Mi/FE9cCRvFlvAiMSaebMI3zPaySsxTK7Zgj5TpEbmbHI9wIDAQABo4GnMIGkMB0GA1UdDgQWBBSVGgvoW4MhMuzBGce29PY8vSzHFzB1BgNVHSMEbjBsgBSVGgvoW4MhMuzBGce29PY8vSzHF6FJpEcwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKg4VeVcIDz1MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJu1rqs+anD74dbdwgd3CnqnQsQDJiEXmBhG2leaGt3ve9b/9gKaJg2pyb2NyppDe1uLqh6nNXDuzg1oNZrPz5pJL/eCXPl7FhxhMUi04TtLf8LeNTCIWYZiFuO4pmhohHcv8kRvYR1+6SkLTC8j/TZerm7qvesSiTQFNapa1eNdVQ8nFwVkEtWl+JzKEM1BlRcn42sjJkijeFp7DpI7pU+PnYeiaXpRv5pJo8ogM1iFxN+SnfEs0EuQ7fhKIG9aHKi7bKZ7L6SyX7MDIGLeulEU6lf5D9BfXNmcMambiS0pXhL2QXajt96UBq8FT2KNXY8XNtR4y6MyyCzhaiZZcc8= + + vincent.vega@hacker-corp.com + + + + + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport + + + + + + + vincent.vega@evil-corp.com + + + + Vincent + + + + VEGA + + + + + \ No newline at end of file diff --git a/test/static/signatures/valid/response.root-signed.assertion-unsigned-encrypted.xml b/test/static/signatures/valid/response.root-signed.assertion-unsigned-encrypted.xml new file mode 100644 index 00000000..4030b18e --- /dev/null +++ b/test/static/signatures/valid/response.root-signed.assertion-unsigned-encrypted.xml @@ -0,0 +1,16 @@ + + + https://evil-corp.com + + + qFEvXFTMcZweXH2ex4ZNhRlBRNA=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 + + \ No newline at end of file diff --git a/test/test-signatures.spec.ts b/test/test-signatures.spec.ts index 8bf8f7b8..caae2df8 100644 --- a/test/test-signatures.spec.ts +++ b/test/test-signatures.spec.ts @@ -1,22 +1,25 @@ import { SAML } from "../lib/passport-saml/index.js"; import * as fs from "fs"; import * as sinon from "sinon"; +import { SamlConfig } from "../lib/passport-saml/types.js"; import assert = require("assert"); const cert = fs.readFileSync(__dirname + "/static/cert.pem", "ascii"); describe("Signatures", function () { const INVALID_SIGNATURE = "Invalid signature", + INVALID_ENCRYPTED_SIGNATURE = "Invalid signature from encrypted assertion", createBody = (pathToXml: string) => ({ SAMLResponse: fs.readFileSync(__dirname + "/static/signatures" + pathToXml, "base64"), }), testOneResponseBody = async ( samlResponseBody: Record, shouldErrorWith: string | false | undefined, - amountOfSignatureChecks = 1 + amountOfSignatureChecks = 1, + options: Partial = {} ) => { //== Instantiate new instance before every test - const samlObj = new SAML({ cert }); + const samlObj = new SAML({ cert, ...options }); //== Spy on `validateSignature` to be able to count how many times it has been called const validateSignatureSpy = sinon.spy(samlObj, "validateSignature"); @@ -30,11 +33,17 @@ describe("Signatures", function () { testOneResponse = ( pathToXml: string, shouldErrorWith: string | false, - amountOfSignaturesChecks: number | undefined + amountOfSignaturesChecks: number | undefined, + options?: Partial ) => { //== Create a body based on an XML and run the test return async () => - await testOneResponseBody(createBody(pathToXml), shouldErrorWith, amountOfSignaturesChecks); + await testOneResponseBody( + createBody(pathToXml), + shouldErrorWith, + amountOfSignaturesChecks, + options + ); }; describe("Signatures on saml:Response - Only 1 saml:Assertion", () => { @@ -73,6 +82,47 @@ describe("Signatures", function () { "R1A - asrt signed => error", testOneResponse("/invalid/response.root-unsigned.assertion-signed.xml", INVALID_SIGNATURE, 2) ); + it( + "R1A - root signed - wantAssertionsSigned=true => error", + testOneResponse("/valid/response.root-signed.assertion-unsigned.xml", INVALID_SIGNATURE, 2, { + wantAssertionsSigned: true, + }) + ); + it( + "R1A - root signed - asrt unsigned encrypted -wantAssertionsSigned=true => error", + testOneResponse( + "/valid/response.root-signed.assertion-unsigned-encrypted.xml", + INVALID_ENCRYPTED_SIGNATURE, + 2, + { + decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"), + wantAssertionsSigned: true, + } + ) + ); + it( + "R1A - root signed - asrt invalidly signed wantAssertionsSigned=true => error", + testOneResponse( + "/invalid/response.root-signed.assertion-invalidly-signed.xml", + INVALID_SIGNATURE, + 2, + { + wantAssertionsSigned: true, + } + ) + ); + it( + "R1A - root signed - asrt invalidly signed encrypted wantAssertionsSigned=true => error", + testOneResponse( + "/invalid/response.root-signed.assertion-invalidly-signed-encrypted.xml", + INVALID_ENCRYPTED_SIGNATURE, + 2, + { + decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"), + wantAssertionsSigned: true, + } + ) + ); }); describe("Signatures on saml:Response - 1 saml:Assertion + 1 saml:Advice containing 1 saml:Assertion", () => { diff --git a/test/tests.spec.ts b/test/tests.spec.ts index fbe25f4e..63652f03 100644 --- a/test/tests.spec.ts +++ b/test/tests.spec.ts @@ -424,6 +424,25 @@ describe("passport-saml /", function () { metadata.should.containEql(samlConfig.logoutCallbackUrl); }); + it("generateServiceProviderMetadata contains WantAssertionsSigned", function () { + const samlConfig = { + cert: TEST_CERT, + issuer: "http://example.serviceprovider.com", + callbackUrl: "http://example.serviceprovider.com/saml/callback", + identifierFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", + decryptionPvk: fs.readFileSync(__dirname + "/static/testshib encryption pvk.pem"), + wantAssertionsSigned: true, + }; + + const samlObj = new SAML(samlConfig); + const decryptionCert = fs.readFileSync( + __dirname + "/static/testshib encryption cert.pem", + "utf-8" + ); + const metadata = samlObj.generateServiceProviderMetadata(decryptionCert); + metadata.should.containEql('WantAssertionsSigned="true"'); + }); + it("#certToPEM should generate valid certificate", function () { const samlConfig = { entryPoint: "https://app.onelogin.com/trust/saml2/http-post/sso/371755",