-
Notifications
You must be signed in to change notification settings - Fork 80
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
feat: top origin verification #217
Changes from all commits
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 |
---|---|---|
|
@@ -20,6 +20,8 @@ type CollectedClientData struct { | |
Type CeremonyType `json:"type"` | ||
Challenge string `json:"challenge"` | ||
Origin string `json:"origin"` | ||
TopOrigin string `json:"topOrigin"` | ||
CrossOrigin bool `json:"crossOrigin,omitempty"` | ||
TokenBinding *TokenBinding `json:"tokenBinding,omitempty"` | ||
|
||
// Chromium (Chrome) returns a hint sometimes about how to handle clientDataJSON in a safe manner. | ||
|
@@ -77,7 +79,10 @@ func FullyQualifiedOrigin(rawOrigin string) (fqOrigin string, err error) { | |
// new credential and steps 7 through 10 of verifying an authentication assertion | ||
// See https://www.w3.org/TR/webauthn/#registering-a-new-credential | ||
// and https://www.w3.org/TR/webauthn/#verifying-assertion | ||
func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyType, rpOrigins []string) error { | ||
// | ||
// Note: the rpTopOriginsVerify parameter does not accept the TopOriginVerificationMode value of | ||
// TopOriginDefaultVerificationMode as it's expected this value is updated by the config validation process. | ||
func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyType, rpOrigins, rpTopOrigins []string, rpTopOriginsVerify TopOriginVerificationMode) (err error) { | ||
// Registration Step 3. Verify that the value of C.type is webauthn.create. | ||
|
||
// Assertion Step 7. Verify that the value of C.type is the string webauthn.get. | ||
|
@@ -101,8 +106,9 @@ func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyTy | |
|
||
// Registration Step 5 & Assertion Step 9. Verify that the value of C.origin matches | ||
// the Relying Party's origin. | ||
fqOrigin, err := FullyQualifiedOrigin(c.Origin) | ||
if err != nil { | ||
var fqOrigin string | ||
|
||
if fqOrigin, err = FullyQualifiedOrigin(c.Origin); err != nil { | ||
return ErrParsingData.WithDetails("Error decoding clientData origin as URL") | ||
} | ||
|
||
|
@@ -121,6 +127,54 @@ func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyTy | |
WithInfo(fmt.Sprintf("Expected Values: %s, Received: %s", rpOrigins, fqOrigin)) | ||
} | ||
|
||
if rpTopOriginsVerify != TopOriginIgnoreVerificationMode { | ||
switch len(c.TopOrigin) { | ||
case 0: | ||
break | ||
default: | ||
if !c.CrossOrigin { | ||
return ErrVerification. | ||
WithDetails("Error validating topOrigin"). | ||
WithInfo("The topOrigin can't have values unless crossOrigin is true.") | ||
} | ||
|
||
var ( | ||
fqTopOrigin string | ||
possibleTopOrigins []string | ||
) | ||
|
||
if fqTopOrigin, err = FullyQualifiedOrigin(c.TopOrigin); err != nil { | ||
return ErrParsingData.WithDetails("Error decoding clientData topOrigin as URL") | ||
} | ||
|
||
switch rpTopOriginsVerify { | ||
case TopOriginExplicitVerificationMode: | ||
possibleTopOrigins = rpTopOrigins | ||
case TopOriginAutoVerificationMode: | ||
possibleTopOrigins = append(rpTopOrigins, rpOrigins...) | ||
case TopOriginImplicitVerificationMode: | ||
possibleTopOrigins = rpOrigins | ||
default: | ||
return ErrNotImplemented.WithDetails("Error handling unknown Top Origin verification mode") | ||
} | ||
|
||
found = false | ||
|
||
for _, origin := range possibleTopOrigins { | ||
if strings.EqualFold(fqTopOrigin, origin) { | ||
found = true | ||
break | ||
} | ||
} | ||
|
||
if !found { | ||
return ErrVerification. | ||
WithDetails("Error validating top origin"). | ||
WithInfo(fmt.Sprintf("Expected Values: %s, Received: %s", possibleTopOrigins, fqTopOrigin)) | ||
} | ||
} | ||
} | ||
|
||
// Registration Step 6 and Assertion Step 10. Verify that the value of C.tokenBinding.status | ||
// matches the state of Token Binding for the TLS connection over which the assertion was | ||
// obtained. If Token Binding was used on that TLS connection, also verify that C.tokenBinding.id | ||
Comment on lines
127
to
180
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.
The modifications in the |
||
|
@@ -140,3 +194,28 @@ func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyTy | |
|
||
return nil | ||
} | ||
|
||
type TopOriginVerificationMode int | ||
|
||
const ( | ||
// TopOriginDefaultVerificationMode represents the default verification mode for the Top Origin. At this time this | ||
// mode is the same as TopOriginIgnoreVerificationMode until such a time as the specification becomes stable. This | ||
// value is intended as a fallback value and implementers should very intentionally pick another option if they want | ||
// stability. | ||
TopOriginDefaultVerificationMode TopOriginVerificationMode = iota | ||
|
||
// TopOriginIgnoreVerificationMode ignores verification entirely. | ||
TopOriginIgnoreVerificationMode | ||
|
||
// TopOriginAutoVerificationMode represents the automatic verification mode for the Top Origin. In this mode the | ||
// If the Top Origins parameter has values it checks against this, otherwise it checks against the Origins parameter. | ||
TopOriginAutoVerificationMode | ||
|
||
// TopOriginImplicitVerificationMode represents the implicit verification mode for the Top Origin. In this mode the | ||
// Top Origin is verified against the allowed Origins values. | ||
TopOriginImplicitVerificationMode | ||
|
||
// TopOriginExplicitVerificationMode represents the explicit verification mode for the Top Origin. In this mode the | ||
// Top Origin is verified against the allowed Top Origins values. | ||
TopOriginExplicitVerificationMode | ||
) |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -36,6 +36,15 @@ type Config struct { | |||||||||||||||||||||||||||||||||||||||||||||||
// qualified origins. | ||||||||||||||||||||||||||||||||||||||||||||||||
RPOrigins []string | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
// RPTopOrigins configures the list of Relying Party Server Top Origins that are permitted. These should be fully | ||||||||||||||||||||||||||||||||||||||||||||||||
// qualified origins. | ||||||||||||||||||||||||||||||||||||||||||||||||
RPTopOrigins []string | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
// RPTopOriginVerificationMode determines the verification mode for the Top Origin value. By default the | ||||||||||||||||||||||||||||||||||||||||||||||||
// TopOriginIgnoreVerificationMode is used however this is going to change at such a time as WebAuthn Level 3 | ||||||||||||||||||||||||||||||||||||||||||||||||
// becomes recommended, implementers should explicitly set this value if they want stability. | ||||||||||||||||||||||||||||||||||||||||||||||||
RPTopOriginVerificationMode protocol.TopOriginVerificationMode | ||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+39
to
+46
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. The addition of + // Ensure RPTopOrigins contains well-formed URLs.
+ for _, origin := range config.RPTopOrigins {
+ if _, err := url.ParseRequestURI(origin); err != nil {
+ return fmt.Errorf("RPTopOrigins contains an invalid URL: %s", origin)
+ }
+ } Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
// AttestationPreference sets the default attestation conveyance preferences. | ||||||||||||||||||||||||||||||||||||||||||||||||
AttestationPreference protocol.ConveyancePreference | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -153,6 +162,15 @@ func (config *Config) validate() error { | |||||||||||||||||||||||||||||||||||||||||||||||
return fmt.Errorf("must provide at least one value to the 'RPOrigins' field") | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
switch config.RPTopOriginVerificationMode { | ||||||||||||||||||||||||||||||||||||||||||||||||
case protocol.TopOriginDefaultVerificationMode: | ||||||||||||||||||||||||||||||||||||||||||||||||
config.RPTopOriginVerificationMode = protocol.TopOriginIgnoreVerificationMode | ||||||||||||||||||||||||||||||||||||||||||||||||
case protocol.TopOriginImplicitVerificationMode: | ||||||||||||||||||||||||||||||||||||||||||||||||
if len(config.RPTopOrigins) == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||
return fmt.Errorf("must provide at least one value to the 'RPTopOrigins' field when 'RPTopOriginVerificationMode' field is set to protocol.TopOriginImplicitVerificationMode") | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
if config.AuthenticatorSelection.RequireResidentKey == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||
config.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyNotRequired() | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
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.
The addition of
nil
andTopOriginIgnoreVerificationMode
as parameters in theVerify
method call withinTestParsedCredentialAssertionData_Verify
aligns with the new verification logic introduced fortopOrigin
. However, it's crucial to ensure that corresponding test cases are added or updated to specifically test the behavior oftopOrigin
verification under different scenarios, including theTopOriginIgnoreVerificationMode
. This will help in validating the correctness and robustness of the new feature.