Skip to content

Commit c3d608e

Browse files
fix(webauthn): default rp.id to effective domain and reject ip-literal rp ids
1 parent dca2054 commit c3d608e

3 files changed

Lines changed: 105 additions & 6 deletions

File tree

libwebauthn/src/ops/webauthn/get_assertion.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,9 @@ impl FromIdlModel<PublicKeyCredentialRequestOptionsJSON> for GetAssertionRequest
192192
}
193193
parsed.0
194194
} else {
195-
effective_rp_id.to_string()
195+
RelyingPartyId::try_from(effective_rp_id)
196+
.map_err(|err| GetAssertionPrepareError::InvalidRelyingPartyId(err.to_string()))?
197+
.0
196198
};
197199

198200
let prf = match inner.extensions.as_ref() {
@@ -872,6 +874,42 @@ mod tests {
872874
));
873875
}
874876

877+
#[tokio::test]
878+
async fn test_request_from_json_rejects_ip_effective_domain() {
879+
let request_origin: RequestOrigin = "https://127.0.0.1:8443".parse().unwrap();
880+
let req_json = json_field_rm(REQUEST_BASE_JSON, "rpId");
881+
882+
let result = from_json(
883+
&request_origin,
884+
&MockPublicSuffixList,
885+
RelatedOrigins::Disabled,
886+
&req_json,
887+
)
888+
.await;
889+
assert!(matches!(
890+
result,
891+
Err(GetAssertionPrepareError::InvalidRelyingPartyId(_))
892+
));
893+
}
894+
895+
#[tokio::test]
896+
async fn test_request_from_json_rejects_ipv6_effective_domain() {
897+
let request_origin: RequestOrigin = "https://[::1]:8443".parse().unwrap();
898+
let req_json = json_field_rm(REQUEST_BASE_JSON, "rpId");
899+
900+
let result = from_json(
901+
&request_origin,
902+
&MockPublicSuffixList,
903+
RelatedOrigins::Disabled,
904+
&req_json,
905+
)
906+
.await;
907+
assert!(matches!(
908+
result,
909+
Err(GetAssertionPrepareError::InvalidRelyingPartyId(_))
910+
));
911+
}
912+
875913
#[tokio::test]
876914
async fn test_request_from_json_mismatching_rp_id() {
877915
let request_origin: RequestOrigin = "https://example.org".parse().unwrap();

libwebauthn/src/ops/webauthn/idl/create.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
ops::webauthn::{
55
MakeCredentialsRequestExtensions, ResidentKeyRequirement, UserVerificationRequirement,
66
},
7-
proto::ctap2::{Ctap2CredentialType, Ctap2PublicKeyCredentialRpEntity},
7+
proto::ctap2::Ctap2CredentialType,
88
};
99

1010
use serde::Deserialize;
@@ -28,6 +28,13 @@ fn default_user_verification() -> UserVerificationRequirement {
2828
UserVerificationRequirement::Preferred
2929
}
3030

31+
#[derive(Debug, Clone, Deserialize)]
32+
#[serde(rename_all = "camelCase")]
33+
pub struct PublicKeyCredentialRpEntityJSON {
34+
pub id: Option<String>,
35+
pub name: Option<String>,
36+
}
37+
3138
#[derive(Debug, Clone, PartialEq, Deserialize)]
3239
#[serde(rename_all = "camelCase")]
3340
pub struct PublicKeyCredentialUserEntity {
@@ -39,7 +46,7 @@ pub struct PublicKeyCredentialUserEntity {
3946
#[derive(Debug, Clone, Deserialize)]
4047
#[serde(rename_all = "camelCase")]
4148
pub struct PublicKeyCredentialCreationOptionsJSON {
42-
pub rp: Ctap2PublicKeyCredentialRpEntity,
49+
pub rp: PublicKeyCredentialRpEntityJSON,
4350
pub user: PublicKeyCredentialUserEntity,
4451
pub challenge: Base64UrlString,
4552
#[serde(rename = "pubKeyCredParams")]

libwebauthn/src/ops/webauthn/make_credential.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -409,16 +409,18 @@ impl FromIdlModel<PublicKeyCredentialCreationOptionsJSON> for MakeCredentialRequ
409409
inner: PublicKeyCredentialCreationOptionsJSON,
410410
) -> Result<Self, MakeCredentialPrepareError> {
411411
let effective_rp_id = request_origin.origin.host.as_str();
412-
let rp_id = RelyingPartyId::try_from(inner.rp.id.as_str())
412+
let rp_id = RelyingPartyId::try_from(inner.rp.id.as_deref().unwrap_or(effective_rp_id))
413413
.map_err(|err| MakeCredentialPrepareError::InvalidRelyingPartyId(err.to_string()))?;
414414
if !rp_id_authorised(request_origin, &rp_id, settings).await {
415415
return Err(MakeCredentialPrepareError::MismatchingRelyingPartyId(
416416
rp_id.0,
417417
effective_rp_id.to_string(),
418418
));
419419
}
420-
let mut relying_party = inner.rp;
421-
relying_party.id = rp_id.0;
420+
let relying_party = Ctap2PublicKeyCredentialRpEntity {
421+
id: rp_id.0,
422+
name: inner.rp.name,
423+
};
422424
let resident_key = if inner
423425
.authenticator_selection
424426
.as_ref()
@@ -1138,6 +1140,58 @@ mod tests {
11381140
));
11391141
}
11401142

1143+
#[tokio::test]
1144+
async fn test_request_from_json_rp_id_defaults_to_effective_domain() {
1145+
let request_origin: RequestOrigin = "https://example.org".parse().unwrap();
1146+
let req_json = json_field_add(REQUEST_BASE_JSON, "rp", r#"{"name": "example.org"}"#);
1147+
1148+
let req = from_json(
1149+
&request_origin,
1150+
&MockPublicSuffixList,
1151+
RelatedOrigins::Disabled,
1152+
&req_json,
1153+
)
1154+
.await
1155+
.unwrap();
1156+
assert_eq!(req.relying_party.id, "example.org");
1157+
}
1158+
1159+
#[tokio::test]
1160+
async fn test_request_from_json_rejects_ipv4_effective_domain() {
1161+
let request_origin: RequestOrigin = "https://127.0.0.1:8443".parse().unwrap();
1162+
let req_json = json_field_add(REQUEST_BASE_JSON, "rp", r#"{"name": "example.org"}"#);
1163+
1164+
let result = from_json(
1165+
&request_origin,
1166+
&MockPublicSuffixList,
1167+
RelatedOrigins::Disabled,
1168+
&req_json,
1169+
)
1170+
.await;
1171+
assert!(matches!(
1172+
result,
1173+
Err(MakeCredentialPrepareError::InvalidRelyingPartyId(_))
1174+
));
1175+
}
1176+
1177+
#[tokio::test]
1178+
async fn test_request_from_json_rejects_ipv6_effective_domain() {
1179+
let request_origin: RequestOrigin = "https://[::1]:8443".parse().unwrap();
1180+
let req_json = json_field_add(REQUEST_BASE_JSON, "rp", r#"{"name": "example.org"}"#);
1181+
1182+
let result = from_json(
1183+
&request_origin,
1184+
&MockPublicSuffixList,
1185+
RelatedOrigins::Disabled,
1186+
&req_json,
1187+
)
1188+
.await;
1189+
assert!(matches!(
1190+
result,
1191+
Err(MakeCredentialPrepareError::InvalidRelyingPartyId(_))
1192+
));
1193+
}
1194+
11411195
#[tokio::test]
11421196
async fn origin_trust_accepts_mismatching_rp_id() {
11431197
let request_origin: RequestOrigin = "https://app.example.org".parse().unwrap();

0 commit comments

Comments
 (0)