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:
@@ -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) => {
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user