-
Notifications
You must be signed in to change notification settings - Fork 17
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
Enhancing signature validation in SAML Response #144 #145
Changes from 5 commits
c0e0eb0
91cd02b
33fc312
340d6e4
9b4f45e
3748d22
5f2819d
f6c7d7b
c9cf0d9
c4af8fc
d857fba
cd6c290
011ef0c
8fe0cbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ import ( | |
|
||
"github.com/jonboulle/clockwork" | ||
saml2 "github.com/russellhaering/gosaml2" | ||
"github.com/russellhaering/gosaml2/types" | ||
dsig "github.com/russellhaering/goxmldsig" | ||
|
||
"github.com/hashicorp/cap/saml/models/core" | ||
|
@@ -151,6 +152,14 @@ func (sp *ServiceProvider) ParseResponse( | |
} | ||
} | ||
|
||
if !opts.skipSignatureValidation { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we make this fully optional? Something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes agree that we need to make this optional. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made all settings optional. |
||
// func ip.ValidateEncodedResponse(...) above only requires either `response or all its `assertions` are signed, | ||
// but does not require both. Adding another check to validate that both of these are signed always. | ||
if err := validateSignature(response, op); err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
return &core.Response{Response: *response}, nil | ||
} | ||
|
||
|
@@ -245,3 +254,21 @@ func parsePEMCertificate(cert []byte) (*x509.Certificate, error) { | |
|
||
return x509.ParseCertificate(block.Bytes) | ||
} | ||
|
||
func validateSignature(response *types.Response, op string) error { | ||
// validate child object assertions | ||
for _, assert := range response.Assertions { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did some further digging on this. There were many threads on the internet were customers required to have both, 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
On your edit above in the description:
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:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks @hcjulz for researching this point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also btw the options i kept are:
Did not include the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am catching up after vacations :). yes correct. |
||
if !assert.SignatureValidated { | ||
// note: at one time func ip.ValidateEncodedResponse(...) above allows all signed or all unsigned | ||
// assertions, and will give error if there is a mix of both. We are still looping on all assertions | ||
// instead of retrieving value for one assertion, so we do not depend on dependency implementation. | ||
return fmt.Errorf("%s: %w", op, ErrInvalidSignature) | ||
} | ||
} | ||
|
||
// validate root object response | ||
if !response.SignatureValidated { | ||
return fmt.Errorf("%s: %w", op, ErrInvalidSignature) | ||
} | ||
return nil | ||
} |
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 the
cap/saml
types instead? https://github.com/hashicorp/cap/blob/main/saml/models/core/response.go#L10-L14There 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.
done