Conversation
… on signature for saml response & extending tests
|
Thank you for your submission! We require that all contributors sign our Contributor License Agreement ("CLA") before we can accept the contribution. Read and sign the agreement Learn more about why HashiCorp requires a CLA and what the CLA includes Have you signed the CLA already but the status is still pending? Recheck it. |
hcjulz
left a comment
There was a problem hiding this comment.
Hey @himran92 👋 Thx a lot for opening this PR.
A general comment:
I think gosaml2 follows the SAML spec and it is not a requirement to sign both Response and Assertions. If the Reponse is signed signature inheritance applies, and if the assertions are signed it leaves only the Response unsigned. The fields in the Response object are not really an issue if tempered. The reason is mainly because relevant fields like the InResponseTo field is (should be) checked on the service provider. The cap/saml implementation by default requires the InResponseTo field to be checked and explicitly prefixes the option to leave it out with Insecure. If additional protection is required.
If we, however, still want to have the entire response to be signed, I'd suggest to make this optional. If this is forced it could lead to be incompatible with identity providers that do not provide the option to signed the full response.
I left a few comments/questions.
saml/response.go
Outdated
|
|
||
| "github.com/jonboulle/clockwork" | ||
| saml2 "github.com/russellhaering/gosaml2" | ||
| "github.com/russellhaering/gosaml2/types" |
There was a problem hiding this comment.
Can we use the cap/saml types instead? https://github.com/hashicorp/cap/blob/main/saml/models/core/response.go#L10-L14
saml/response.go
Outdated
|
|
||
| func validateSignature(response *types.Response, op string) error { | ||
| // validate child object assertions | ||
| for _, assert := range response.Assertions { |
There was a problem hiding this comment.
gosaml2 requires either the response or the assertions to be signed. It doesn't require both.
My qustion would be if we need to sign both (response and assertions)? Because signature inheritance applies (if the parent object is signed, the child object is included).
Having both objects signed makes sense in some instances where the service provider could retrieve the assertion outside of a SAML Response, but that's not the case here.
There was a problem hiding this comment.
I did some further digging on this. There were many threads on the internet were customers required to have both, response and assertions to be signed. So there is a customer need for that.
Most SAML IDPs (e.g. Entra ID and Okta) do provide the option to sign both. However, it looks like there are indeed some IDPs that do not provide the option, such as Auth0. This is from their application settings:
signResponse (bool): Whether or not the SAML Response should be signed. By default the SAML Assertion will be signed, but not the SAML Response. If true, SAML Response will be signed instead of SAML Assertion.
On your edit above in the description:
After few major version, the vault variable will be removed and user will always have to sign both.
Making both a requirement might lead to incompatibilities with some IDPs (like Auth0 for example). So I wonder if supporting all 3 options would be the ideal case:
- Either
ResponseorAssertionhas to be signed Responsehas to be signed- Both have to be signed.
There was a problem hiding this comment.
thanks @hcjulz for researching this point.
I do agree that with this point it does not make sense to add just a strict validation option today and make the setting permanent in future version , rather what makes sense is to give user the control to opt for right validation.
I still would need to check with team on thoughts here as it was called out as a gap from trail of bits. Will update shortly if team aligns with these thoughts.
There was a problem hiding this comment.
Changes done to add separate options for all signature validation. Really appreciate for you finding the info on Auth0 as IDP.
There was a problem hiding this comment.
Also btw the options i kept are:
- assertion is at least signed
- response is at least signed
- both are signed
Did not include the either option as i thought user should makes a known decision relative to their IDP. Also either is the default case today when you do not opt for any option.
Let me know if you think otherwise.
There was a problem hiding this comment.
Hey @himran92 sorry for the late response and thx a lot for addressing my comments 🙇
Sounds great. The options makes totally sense.
The default would still be either, correct?
There was a problem hiding this comment.
I am catching up after vacations :).
yes correct.
The default where neither of these options is provided is still either.
This is given by these unit tests.
saml/response.go
Outdated
| } | ||
| } | ||
|
|
||
| if !opts.skipSignatureValidation { |
There was a problem hiding this comment.
Can we make this fully optional? Something like opts.ResponseSigned or opts.ResponseAndAssertionsSigned?
There was a problem hiding this comment.
Yes agree that we need to make this optional.
I had a discussion internally and moved the PR to draft again to bring the optional field change + test the e2e flow with vault.
There was a problem hiding this comment.
Made all settings optional.
saml/response.go
Outdated
| skipAssertionConditionValidation bool | ||
| skipSignatureValidation bool | ||
| assertionConsumerServiceURL string | ||
| requireSignatureForResponseAndAssertion bool |
There was a problem hiding this comment.
For requireSignatureForResponseAndAssertion :
On the vault usage side i opted for a name EnableStrictResponseSignatureValidation as that sounded more user friendly for a user exposed setting. The description will share the impact of the setting. Whereas kept it requireSignatureForResponseAndAssertion on cap side thinking of it as more of an internal lib so can have any name.
Any preference of using EnableStrictResponseSignatureValidation here?
There was a problem hiding this comment.
I don't have a strong preference. Based on my comment about having all 3 options signature validation, it would just be important to think about the general naming pattern:
- Validate Response And Assertion Signatures =
EnableStrictSignatureValidation - Validate Response Signature =
EnableResponseSignatureValidation?
Alternatively, we could use a more explicit naming pattern:
ValidateResponseSignatureValidateResponseAndAssertionSignatures
I'm ok either way.
There was a problem hiding this comment.
Updated the name of the setting.
saml/response.go
Outdated
| return nil, fmt.Errorf("%s: missing request ID: %w", op, ErrInvalidParameter) | ||
| case opts.skipSignatureValidation && callValidateSignature: | ||
| return nil, fmt.Errorf("%s: option `skip signature validation` cannot be true with any validate signature option : %w", op, ErrInvalidParameter) | ||
| case multipleSignatureOptionEnabled(opts.validateResponseAndAssertionSignatures, opts.validateResponseSignature, opts.validateAssertionSignature): |
There was a problem hiding this comment.
I think I see discussion on these options already in the discussion, so I'll defer to cap people on the ergonomics/patterns, but if we need to check if the options are sensible, would it be better to reshape the options so that any pattern is valid? (for example, can we remove the "both" boolean and let having both validateResponse and validateAssertion be true imply it instead?
If there is a need/desire to have an explicit "both" option mutually exclusive with the other two, would it be worth turning it into a single field set to const values, e.g.
const (
NoSignatureValidation = iota
ValidateAssertionSignature
ValidateResponseSignature
ValidateResponseAndAssertionSignature
)
I understand we wouldn't want to remove the InsecureSkipValidation functions, for compatibility reasons.
There was a problem hiding this comment.
@hcjulz Do you have any recommendation for above w.r.t cap?
I like Kay point to remove the both option and ask user to set individual option to achieve signatures-validation-on-both for cap level and then similarly at Vault Plugin level too
There was a problem hiding this comment.
@mgaffney just randomly tagging you as you are mentioned as the repo owner.
Would you be able to assist in the review as Julian is out till end of the year, thanks
Let me know if you prefer/recommend someone else.
There was a problem hiding this comment.
Mike was not familiar with this piece of code..
tagging @jimlambrt for review for this Pr.
There was a problem hiding this comment.
I think, removing ValidateResponseAndAssertionSignatures makes for a better
dev experience.
There was a problem hiding this comment.
PR is updated now with only keeping two options
saml/response.go
Outdated
| return nil, fmt.Errorf("%s: missing request ID: %w", op, ErrInvalidParameter) | ||
| case opts.skipSignatureValidation && callValidateSignature: | ||
| return nil, fmt.Errorf("%s: option `skip signature validation` cannot be true with any validate signature option : %w", op, ErrInvalidParameter) | ||
| case multipleSignatureOptionEnabled(opts.validateResponseAndAssertionSignatures, opts.validateResponseSignature, opts.validateAssertionSignature): |
There was a problem hiding this comment.
I think, removing ValidateResponseAndAssertionSignatures makes for a better
dev experience.
The SAML protocol allows signing of a Response, its Assertion(s), neither, or both. Since Assertion(s) are sub-elements of a Response, they are signed if the Response is signed. Today we are depending on the gosaml2 for signature validation. It only checks only one or the other be signed.
For enhancing security, we would like to give user the options to sign just assign response or assertion or both.
Changes in PR include:
Points before/after merging:
EDIT: there is another PR merged in cap related to
Adds an option to enable sAMAccountname logins when upndomain is set