1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00

[PM-4830] Fix unsafe origin source (#6884)

* feat: add inscure types and remove the insecure fields from the page-script

* feat: securely set variables in content-script

* chore: clean up comments
This commit is contained in:
Andreas Coroiu
2023-11-27 13:14:33 +01:00
committed by GitHub
parent 4c8193060d
commit 61ca0e893e
4 changed files with 59 additions and 40 deletions

View File

@@ -1,3 +1,8 @@
import {
AssertCredentialParams,
CreateCredentialParams,
} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction";
import { Message, MessageType } from "./messaging/message"; import { Message, MessageType } from "./messaging/message";
import { Messenger } from "./messaging/messenger"; import { Messenger } from "./messaging/messenger";
@@ -40,6 +45,14 @@ async function hasActiveUser() {
return activeUserStorageValue[activeUserIdKey] !== undefined; return activeUserStorageValue[activeUserIdKey] !== undefined;
} }
function isSameOriginWithAncestors() {
try {
return window.self === window.top;
} catch {
return false;
}
}
function initializeFido2ContentScript() { function initializeFido2ContentScript() {
const s = document.createElement("script"); const s = document.createElement("script");
s.src = chrome.runtime.getURL("content/fido2/page-script.js"); s.src = chrome.runtime.getURL("content/fido2/page-script.js");
@@ -58,10 +71,16 @@ function initializeFido2ContentScript() {
if (message.type === MessageType.CredentialCreationRequest) { if (message.type === MessageType.CredentialCreationRequest) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const data: CreateCredentialParams = {
...message.data,
origin: window.location.origin,
sameOriginWithAncestors: isSameOriginWithAncestors(),
};
chrome.runtime.sendMessage( chrome.runtime.sendMessage(
{ {
command: "fido2RegisterCredentialRequest", command: "fido2RegisterCredentialRequest",
data: message.data, data,
requestId: requestId, requestId: requestId,
}, },
(response) => { (response) => {
@@ -80,10 +99,16 @@ function initializeFido2ContentScript() {
if (message.type === MessageType.CredentialGetRequest) { if (message.type === MessageType.CredentialGetRequest) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const data: AssertCredentialParams = {
...message.data,
origin: window.location.origin,
sameOriginWithAncestors: isSameOriginWithAncestors(),
};
chrome.runtime.sendMessage( chrome.runtime.sendMessage(
{ {
command: "fido2GetCredentialRequest", command: "fido2GetCredentialRequest",
data: message.data, data,
requestId: requestId, requestId: requestId,
}, },
(response) => { (response) => {

View File

@@ -15,9 +15,19 @@ export enum MessageType {
ErrorResponse, 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 = { export type CredentialCreationRequest = {
type: MessageType.CredentialCreationRequest; type: MessageType.CredentialCreationRequest;
data: CreateCredentialParams; data: InsecureCreateCredentialParams;
}; };
export type CredentialCreationResponse = { export type CredentialCreationResponse = {
@@ -25,9 +35,19 @@ export type CredentialCreationResponse = {
result?: CreateCredentialResult; 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 = { export type CredentialGetRequest = {
type: MessageType.CredentialGetRequest; type: MessageType.CredentialGetRequest;
data: AssertCredentialParams; data: InsecureAssertCredentialParams;
}; };
export type CredentialGetResponse = { export type CredentialGetResponse = {

View File

@@ -52,16 +52,7 @@ const browserCredentials = {
get: navigator.credentials.get.bind(navigator.credentials) as typeof navigator.credentials.get, get: navigator.credentials.get.bind(navigator.credentials) as typeof navigator.credentials.get,
}; };
const messenger = Messenger.forDOMCommunication(window); const messenger = ((window as any).messenger = Messenger.forDOMCommunication(window));
function isSameOriginWithAncestors() {
try {
return window.self === window.top;
} catch {
return false;
}
}
navigator.credentials.create = async ( navigator.credentials.create = async (
options?: CredentialCreationOptions, options?: CredentialCreationOptions,
abortController?: AbortController abortController?: AbortController
@@ -76,17 +67,10 @@ navigator.credentials.create = async (
(options?.publicKey?.authenticatorSelection.authenticatorAttachment !== "platform" && (options?.publicKey?.authenticatorSelection.authenticatorAttachment !== "platform" &&
browserNativeWebauthnSupport); browserNativeWebauthnSupport);
try { try {
const isNotIframe = isSameOriginWithAncestors();
const response = await messenger.request( const response = await messenger.request(
{ {
type: MessageType.CredentialCreationRequest, type: MessageType.CredentialCreationRequest,
data: WebauthnUtils.mapCredentialCreationOptions( data: WebauthnUtils.mapCredentialCreationOptions(options, fallbackSupported),
options,
window.location.origin,
isNotIframe,
fallbackSupported
),
}, },
abortController abortController
); );
@@ -124,12 +108,7 @@ navigator.credentials.get = async (
const response = await messenger.request( const response = await messenger.request(
{ {
type: MessageType.CredentialGetRequest, type: MessageType.CredentialGetRequest,
data: WebauthnUtils.mapCredentialRequestOptions( data: WebauthnUtils.mapCredentialRequestOptions(options, fallbackSupported),
options,
window.location.origin,
true,
fallbackSupported
),
}, },
abortController abortController
); );

View File

@@ -1,18 +1,19 @@
import { import {
CreateCredentialParams,
CreateCredentialResult, CreateCredentialResult,
AssertCredentialParams,
AssertCredentialResult, AssertCredentialResult,
} from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction"; } from "@bitwarden/common/vault/abstractions/fido2/fido2-client.service.abstraction";
import { Fido2Utils } from "@bitwarden/common/vault/services/fido2/fido2-utils"; import { Fido2Utils } from "@bitwarden/common/vault/services/fido2/fido2-utils";
import {
InsecureAssertCredentialParams,
InsecureCreateCredentialParams,
} from "./content/messaging/message";
export class WebauthnUtils { export class WebauthnUtils {
static mapCredentialCreationOptions( static mapCredentialCreationOptions(
options: CredentialCreationOptions, options: CredentialCreationOptions,
origin: string,
sameOriginWithAncestors: boolean,
fallbackSupported: boolean fallbackSupported: boolean
): CreateCredentialParams { ): InsecureCreateCredentialParams {
const keyOptions = options.publicKey; const keyOptions = options.publicKey;
if (keyOptions == undefined) { if (keyOptions == undefined) {
@@ -20,7 +21,6 @@ export class WebauthnUtils {
} }
return { return {
origin,
attestation: keyOptions.attestation, attestation: keyOptions.attestation,
authenticatorSelection: { authenticatorSelection: {
requireResidentKey: keyOptions.authenticatorSelection?.requireResidentKey, requireResidentKey: keyOptions.authenticatorSelection?.requireResidentKey,
@@ -48,7 +48,6 @@ export class WebauthnUtils {
name: keyOptions.user.name, name: keyOptions.user.name,
}, },
timeout: keyOptions.timeout, timeout: keyOptions.timeout,
sameOriginWithAncestors,
fallbackSupported, fallbackSupported,
}; };
} }
@@ -93,10 +92,8 @@ export class WebauthnUtils {
static mapCredentialRequestOptions( static mapCredentialRequestOptions(
options: CredentialRequestOptions, options: CredentialRequestOptions,
origin: string,
sameOriginWithAncestors: boolean,
fallbackSupported: boolean fallbackSupported: boolean
): AssertCredentialParams { ): InsecureAssertCredentialParams {
const keyOptions = options.publicKey; const keyOptions = options.publicKey;
if (keyOptions == undefined) { if (keyOptions == undefined) {
@@ -104,14 +101,12 @@ export class WebauthnUtils {
} }
return { return {
origin,
allowedCredentialIds: allowedCredentialIds:
keyOptions.allowCredentials?.map((c) => Fido2Utils.bufferToString(c.id)) ?? [], keyOptions.allowCredentials?.map((c) => Fido2Utils.bufferToString(c.id)) ?? [],
challenge: Fido2Utils.bufferToString(keyOptions.challenge), challenge: Fido2Utils.bufferToString(keyOptions.challenge),
rpId: keyOptions.rpId, rpId: keyOptions.rpId,
userVerification: keyOptions.userVerification, userVerification: keyOptions.userVerification,
timeout: keyOptions.timeout, timeout: keyOptions.timeout,
sameOriginWithAncestors,
fallbackSupported, fallbackSupported,
}; };
} }