-
Notifications
You must be signed in to change notification settings - Fork 23
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
[#174710289] Better SAML Response validation #49
Conversation
Affected stories
|
Codecov Report
@@ Coverage Diff @@
## master #49 +/- ##
==========================================
+ Coverage 65.05% 65.81% +0.75%
==========================================
Files 11 11
Lines 724 740 +16
Branches 120 122 +2
==========================================
+ Hits 471 487 +16
Misses 252 252
Partials 1 1
Continue to review full report at Codecov.
|
src/strategy/spid.ts
Outdated
@@ -60,7 +60,7 @@ export class SpidStrategy extends SamlStrategy { | |||
super(options, verify); | |||
if (!options.requestIdExpirationPeriodMs) { | |||
// 1 hour | |||
options.requestIdExpirationPeriodMs = 3600 * 1000; | |||
options.requestIdExpirationPeriodMs = 3600 * 1000; // TODO: Change the default value or set it into io-backend? |
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.
that would be better, probably keeping a sane default here
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.
Fixed in 3198ea4
fromOption(new Error("Assertion element must be present"))( | ||
fromNullable( | ||
_1.Response.getElementsByTagNameNS( | ||
SAML_NAMESPACE.ASSERTION, | ||
"Assertion" | ||
).item(0) | ||
) |
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.
isn't this implied by the previous check?
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.
This code is only for type check. We need a cast like .item(0) as Element
to avoid the fromNullable
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.
can you add this as a comment?
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've changed the predicate to discriminate 2 different errors:
- multiple Responses or Assertions
- missing Response or Assertion
Fixed in a729df3
src/utils/saml.ts
Outdated
const signatureOfResponseCount = (xpath.select( | ||
"count(//*[local-name(.)='Response']/*[local-name(.)='Signature'])", | ||
doc |
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.
can we use DOM parser and remove xpath dependency instead?
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.
Yes, but the code will be a little more complex
const signatureOfResponseCount =
_.Response.getElementsByTagNameNS(SAML_NAMESPACE.XMLDSIG, "Signature")
.length -
_.Assertion.getElementsByTagNameNS(
SAML_NAMESPACE.XMLDSIG,
"Signature"
).length;
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.
yes that's right, we must chose one between the two approaches and try to not mix them.
(if I'd had to start again I'd rather have used https://www.npmjs.com/package/xml2js instead of xpath / dom :-)
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.
Fixed in a729df3
src/utils/saml.ts
Outdated
error => { | ||
if (telemetryClient) { | ||
telemetryClient.trackEvent({ | ||
name: "backend.login.prevalidate", |
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.
name: "backend.login.prevalidate", | |
name: "spid.error.generic", |
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.
Fixed in a729df3
src/utils/saml.ts
Outdated
) as unknown) as number; | ||
if (telemetryClient && signatureOfResponseCount === 0) { | ||
telemetryClient.trackEvent({ | ||
name: "backend.login.prevalidate", |
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.
name: "backend.login.prevalidate", | |
name: "spid.error.signature", |
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.
Fixed in a729df3
src/index.ts
Outdated
getPreValidateResponse(serviceProviderConfig.strictResponseValidation), | ||
getPreValidateResponse( | ||
serviceProviderConfig.strictResponseValidation, | ||
appConfig.applicationInsights |
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.
can we take the AI client from the backend? (so we will share the configuration)
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.
Yes, the appConfig
comes from the backend (provided inside the withSpid
method)
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've thought about this a little bit. Since this may be a general purpose library used by other projects as well (there're no parts tied to the IO platform), it's better to do not introduce any deps to AI (which is Azure specific). That would be better to pass some track({eventName, type: "error"|"event", data: any })
method from the backend that uses its own instance of AI.
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.
Fixed in a729df3
type: "INFO" | ||
}); | ||
} | ||
return callback(null, true, _.InResponseTo); |
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.
signatureOfResponseCount
=== 0 is considered a successulf case?
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.
Yes, the Signature of the Response element is optional (link), we just log that we have received a SAML response without a signature.
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.
why do we check response#signature - assertion#signature ? can you add some comments?
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.
Fixed with a comment in 7e7ff8e
@@ -59,8 +59,8 @@ export class SpidStrategy extends SamlStrategy { | |||
) { | |||
super(options, verify); | |||
if (!options.requestIdExpirationPeriodMs) { | |||
// 1 hour | |||
options.requestIdExpirationPeriodMs = 3600 * 1000; | |||
// 5 minutes |
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.
after a quick chat with @pp-ps we agreed to put this to 15 minutes
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.
Fixed in 7e7ff8e
type: "INFO" | ||
}); | ||
} | ||
return callback(null, true, _.InResponseTo); |
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.
why do we check response#signature - assertion#signature ? can you add some comments?
Improve the SAML validation inside
getPreValidateResponse
:appInsights
Some unit tests were added for the previous scenarios.