diff --git a/apps/browser/src/vault/fido2/content/content-script.ts b/apps/browser/src/vault/fido2/content/content-script.ts index bcd32fc173..96af5a6e64 100644 --- a/apps/browser/src/vault/fido2/content/content-script.ts +++ b/apps/browser/src/vault/fido2/content/content-script.ts @@ -1,3 +1,8 @@ +import { + AssertCredentialParams, + CreateCredentialParams, +} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; + import { Message, MessageType } from "./messaging/message"; import { Messenger } from "./messaging/messenger"; @@ -40,6 +45,14 @@ async function hasActiveUser() { return activeUserStorageValue[activeUserIdKey] !== undefined; } +function isSameOriginWithAncestors() { + try { + return window.self === window.top; + } catch { + return false; + } +} + function initializeFido2ContentScript() { const s = document.createElement("script"); s.src = chrome.runtime.getURL("content/fido2/page-script.js"); @@ -58,10 +71,16 @@ function initializeFido2ContentScript() { if (message.type === MessageType.CredentialCreationRequest) { return new Promise((resolve, reject) => { + const data: CreateCredentialParams = { + ...message.data, + origin: window.location.origin, + sameOriginWithAncestors: isSameOriginWithAncestors(), + }; + chrome.runtime.sendMessage( { command: "fido2RegisterCredentialRequest", - data: message.data, + data, requestId: requestId, }, (response) => { @@ -80,10 +99,16 @@ function initializeFido2ContentScript() { if (message.type === MessageType.CredentialGetRequest) { return new Promise((resolve, reject) => { + const data: AssertCredentialParams = { + ...message.data, + origin: window.location.origin, + sameOriginWithAncestors: isSameOriginWithAncestors(), + }; + chrome.runtime.sendMessage( { command: "fido2GetCredentialRequest", - data: message.data, + data, requestId: requestId, }, (response) => { diff --git a/apps/browser/src/vault/fido2/content/messaging/message.ts b/apps/browser/src/vault/fido2/content/messaging/message.ts index 01a19a1f8a..e516dd9b37 100644 --- a/apps/browser/src/vault/fido2/content/messaging/message.ts +++ b/apps/browser/src/vault/fido2/content/messaging/message.ts @@ -15,9 +15,19 @@ export enum MessageType { ErrorResponse, } +/** + * The params provided by the page-script are created in an insecure environemnt and + * should not be trusted. This type is used to ensure that the content-script does not + * trust the `origin` or `sameOriginWithAncestors` params. + */ +export type InsecureCreateCredentialParams = Omit< + CreateCredentialParams, + "origin" | "sameOriginWithAncestors" +>; + export type CredentialCreationRequest = { type: MessageType.CredentialCreationRequest; - data: CreateCredentialParams; + data: InsecureCreateCredentialParams; }; export type CredentialCreationResponse = { @@ -25,9 +35,19 @@ export type CredentialCreationResponse = { result?: CreateCredentialResult; }; +/** + * The params provided by the page-script are created in an insecure environemnt and + * should not be trusted. This type is used to ensure that the content-script does not + * trust the `origin` or `sameOriginWithAncestors` params. + */ +export type InsecureAssertCredentialParams = Omit< + AssertCredentialParams, + "origin" | "sameOriginWithAncestors" +>; + export type CredentialGetRequest = { type: MessageType.CredentialGetRequest; - data: AssertCredentialParams; + data: InsecureAssertCredentialParams; }; export type CredentialGetResponse = { diff --git a/apps/browser/src/vault/fido2/content/page-script.ts b/apps/browser/src/vault/fido2/content/page-script.ts index e64fb92617..2071dbb149 100644 --- a/apps/browser/src/vault/fido2/content/page-script.ts +++ b/apps/browser/src/vault/fido2/content/page-script.ts @@ -52,16 +52,7 @@ const browserCredentials = { get: navigator.credentials.get.bind(navigator.credentials) as typeof navigator.credentials.get, }; -const messenger = Messenger.forDOMCommunication(window); - -function isSameOriginWithAncestors() { - try { - return window.self === window.top; - } catch { - return false; - } -} - +const messenger = ((window as any).messenger = Messenger.forDOMCommunication(window)); navigator.credentials.create = async ( options?: CredentialCreationOptions, abortController?: AbortController @@ -76,17 +67,10 @@ navigator.credentials.create = async ( (options?.publicKey?.authenticatorSelection.authenticatorAttachment !== "platform" && browserNativeWebauthnSupport); try { - const isNotIframe = isSameOriginWithAncestors(); - const response = await messenger.request( { type: MessageType.CredentialCreationRequest, - data: WebauthnUtils.mapCredentialCreationOptions( - options, - window.location.origin, - isNotIframe, - fallbackSupported - ), + data: WebauthnUtils.mapCredentialCreationOptions(options, fallbackSupported), }, abortController ); @@ -124,12 +108,7 @@ navigator.credentials.get = async ( const response = await messenger.request( { type: MessageType.CredentialGetRequest, - data: WebauthnUtils.mapCredentialRequestOptions( - options, - window.location.origin, - true, - fallbackSupported - ), + data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported), }, abortController ); diff --git a/apps/browser/src/vault/fido2/webauthn-utils.ts b/apps/browser/src/vault/fido2/webauthn-utils.ts index b747825e9c..a26c56ea57 100644 --- a/apps/browser/src/vault/fido2/webauthn-utils.ts +++ b/apps/browser/src/vault/fido2/webauthn-utils.ts @@ -1,18 +1,19 @@ import { - CreateCredentialParams, CreateCredentialResult, - AssertCredentialParams, AssertCredentialResult, } from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; import { Fido2Utils } from "@bitwarden/common/vault/services/fido2/fido2-utils"; +import { + InsecureAssertCredentialParams, + InsecureCreateCredentialParams, +} from "./content/messaging/message"; + export class WebauthnUtils { static mapCredentialCreationOptions( options: CredentialCreationOptions, - origin: string, - sameOriginWithAncestors: boolean, fallbackSupported: boolean - ): CreateCredentialParams { + ): InsecureCreateCredentialParams { const keyOptions = options.publicKey; if (keyOptions == undefined) { @@ -20,7 +21,6 @@ export class WebauthnUtils { } return { - origin, attestation: keyOptions.attestation, authenticatorSelection: { requireResidentKey: keyOptions.authenticatorSelection?.requireResidentKey, @@ -48,7 +48,6 @@ export class WebauthnUtils { name: keyOptions.user.name, }, timeout: keyOptions.timeout, - sameOriginWithAncestors, fallbackSupported, }; } @@ -93,10 +92,8 @@ export class WebauthnUtils { static mapCredentialRequestOptions( options: CredentialRequestOptions, - origin: string, - sameOriginWithAncestors: boolean, fallbackSupported: boolean - ): AssertCredentialParams { + ): InsecureAssertCredentialParams { const keyOptions = options.publicKey; if (keyOptions == undefined) { @@ -104,14 +101,12 @@ export class WebauthnUtils { } return { - origin, allowedCredentialIds: keyOptions.allowCredentials?.map((c) => Fido2Utils.bufferToString(c.id)) ?? [], challenge: Fido2Utils.bufferToString(keyOptions.challenge), rpId: keyOptions.rpId, userVerification: keyOptions.userVerification, timeout: keyOptions.timeout, - sameOriginWithAncestors, fallbackSupported, }; }