Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
LavaDome — ShadowDOM을 활용한 안전한 DOM 트리 격리 및 캡슐화 | Kitploit
도구/GitHubGitHub/lavamoat/lavadome
Defensive ToolsWeb SecurityPrivacy
GitHublavamoat/lavadome

LavaDome

ShadowDOM을 활용한 안전한 DOM 트리 격리 및 캡슐화

저장소 보기웹사이트
36541년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

LavaDome 🌋️

~ DOM 노드 보안 Encapsulation을 위한 새로운 LavaMoat 도구 ~

⚠️ 실험적 [WIP] - 사용 시 위험은 본인이 감수하세요 (자세히 알아보기)

데모

LavaDome에 도전해 보세요 - 데모 앱을 방문하고, 콘솔을 열어, LavaDome 인스턴스 내부에서 비밀을 훔치기 위해 가능한 모든 것을 해보세요 (성공을 보고하세요)

미리보기 (클릭하여 펼치기)
LavaDome DEMO

동기

오늘날의 웹 표준에서는 DOM 하위 트리를 안전한 방식으로 선택적으로 격리하는 확립된 방법이 없습니다. 즉, 동일한 JavaScript 실행 환경을 공유하는 경우 일부 당사자에게는 액세스를 허용하면서 다른 당사자에게는 차단함으로써 DOM의 일부 섹션에 대한 액세스를 제어할 수 없습니다.

우리는 더 이상 우리 자신의 앱에 있는 코드를 신뢰할 수 없는 세상에 살고 있으며, 동일 출처 실행이 안전을 보장하지 않습니다. 프론트엔드에서 비밀을 보호하려면 동일 출처에서 실행되는 JavaScript 코드에 의해 손상될 수 없도록 하면서 사용자에게 콘텐츠를 제공할 수 있어야 합니다.

예시

이러한 기능의 한 사용 사례는 사용자 요청 시 개인 키를 평문으로 내보내는 MetaMask의 "개인 키 표시" 토글입니다. (클릭하여 펼치기)
Show private key feature by MetaMask

현재 이 민감한 콘텐츠는 일단 내보내지면 단순히 DOM에 첨부되어, 동일한 앱에서 실행되는 모든 주체가 완전히 액세스할 수 있게 됩니다. 즉, 개인 키에 액세스할 수 없어야 하는 코드 부분은 악성 코드가 DOM에 액세스할 수 있는 한 평문으로 쉽게 추출할 수 있습니다.

하지만 안심하세요. 우리는 이것이 해결 가능한 문제라고 믿습니다 👇

사용법

**LavaDome**은 현재 Vanilla JavaScript와 React를 지원합니다 (더 많은 지원 예정)

JavaScript```javascript

import { LavaDome as LavaDomeJavaScript } from '@lavamoat/lavadome-javascript';

const root = document.getElementById('root'); const lavadome = new LavaDomeJavaScript(root); lavadome.text(secret); lavadome.copy(); // copy to clipboard

root@kitploit:~
### [React](https://github.com/lavamoat/lavadome/blob/main/packages/react)```javascript
import { LavaDome as LavaDomeReact, toLavaDomeToken } from '@lavamoat/lavadome-react';

function Secret({ text }) {
    const {token, copy} = toLavaDomeCapabilities(text);
    return <>
        <a onClick={copy}> copy to clipboard </a>
        <LavaDomeReact token={token} />
    </>;
}

API

루트 노드 외에도 모든 생성자는 선택적 옵션인 두 번째 인수를 허용합니다:```javascript // javascript new LavaDomeJavaScript(root, { // boolean unsafeOpenModeShadow: false, });

// react function Secret({ text }) { const {token} = toLavaDomeCapabilities(text); return <LavaDomeReact token={token} // boolean unsafeOpenModeShadow={false} /> }

root@kitploit:~
### 안전한 사용

웹 코어의 한계로 인해 LavaDome을 안전하게 통합하려면 통합 개발자가 적극적으로 주의해야 할 몇 가지 사항이 있습니다:

#### 실행 순서

LavaDome은 다른 JavaScript 보안 소프트웨어와 마찬가지로 그보다 먼저 실행되는 코드에 항상 취약합니다.

즉, 절대적으로 신뢰하는 코드를 제외하고 LavaDome은 웹 애플리케이션 프로그램에서 가장 먼저 로드되는 코드여야 합니다.

이는 개발자가 즉시 사용해야 한다는 뜻은 아니지만(필요할 때만 사용하면 됨), 가능한 한 빨리 프로그램을 포함해야 합니다.

이를 올바르게(안전하게) 수행하려면 전체 프로그램에서 첫 번째 import/require 선언이어야 합니다:```javascript
import '@lavamoat/lavadome-react';
import 'other-stuff';

console.log('Program starts here');

그렇게 함으로써 LavaDome이 안전한 사용을 위해 스스로 준비할 수 있음을 보장합니다.

이는 @lavamoat/lavadome-react뿐만 아니라 나머지 LavaDome 패키지에도 동일하게 적용된다는 점에 유의하세요(따라서 여러 개를 가져올 필요가 없습니다).

자세한 내용은 보안(방어적 코딩)을 참조하세요.

CSP

사이드 채널링 공격과 웹 제약 때문에 원격 글꼴을 가져오는 방식이 LavaDome을 상대로 유효한 기법이 될 수 있습니다. CSS 영역에 포함되어 있기 때문에 현재 LavaDome으로 이 문제를 해결하는 것은 불가능합니다.

다행히도 이 문제는 CSP의 font-src 지시문을 사용해 효과적으로 해결할 수 있습니다.

이러한 공격을 완화하려면 웹 앱이 알 수 없는 서버에서 글꼴을 가져오는 것을 허용하지 않도록 하세요.

자세한 내용은 보안(사이드 채널링)을 참조하세요.

예측 불가능한 텍스트

개발자가 LavaDome에 제공하는 텍스트는 100% 예측 불가능해야 합니다. 그렇지 않으면 공격을 받아 유출될 수 있습니다.

따라서 앱이 "your key is 234789"를 표시해야 한다면 DOM 구조는 다음과 같아야 합니다.```html your key is 234789

root@kitploit:~
그리고 다음과 같아서는 안 됩니다:```html
<span> <lavadome>your key is 234789</lavadome> </span>

자세히 알아보려면 보안(검색 가능성)로 이동하세요.

테스트

LavaDome를 테스트 환경에 통합하는 것은 까다로울 수 있습니다. LavaDome이 비밀을 숨기는 데 훌륭한 역할을 하기 때문에, 테스트에서도 그 비밀을 꽤 잘 숨기기 때문입니다!

LavaDome를 테스트 환경에 성공적으로 통합하려면 @lavamoat/lavadome-core에서 내보내는 LavaDomeDebug의 도움이 필요할 수 있습니다:```javascript // IMPORT/USE FOR TESTING/DEBUGGING PURPOSES ONLY - NEVER IN PRODUCTION! import { LavaDomeDebug } from '@lavamoat/lavadome-core';

root@kitploit:~
다음은 `LavaDome` 기반 구성 요소를 테스트하는 데 도움이 되는 `LavaDomeDebug`가 내보내는 몇 가지 디버깅 유틸 메서드입니다:

#### `getTextByRoot()`

`LavaDome`이 연결된 루트(root)가 주어지면 `getTextByRoot()`는 내부 비밀(secret)을 재귀적으로 추출하여 재구성합니다. 이를 허용하려면 `LavaDome` 인스턴스가 원래 UNSAFE 옵션 `@unsafeOpenModeShadow`로 초기화되어 있어야 합니다. 이 옵션은 `LavaDome`의 내부 섀도우를 외부에서 접근 가능하게 만듭니다.

당연히 이는 UNSAFE하며 `LavaDome`을 완전히 취약하게 만들지만, 테스트/디버깅 목적으로만 사용하는 것이 타당합니다 - 프로덕션 환경에서는 이 옵션을 절대 활성화하지 않도록 하십시오!```javascript
new LavaDomeJavaScript(root, {
    unsafeOpenModeShadow: isThisTestingEnv, // boolean
}).text('123456');
LavaDomeDebug.getTextByRoot(root) === '123456'; // true

stripDistractionFromText()

웹 드라이버를 테스트에 사용하고 LavaDome 인스턴스 루트의 내부 텍스트를 추출하도록 지시하면, 비밀과 LavaDome의 디스트랙션 텍스트를 모두 포함하는 문자열이 반환됩니다.

디스트랙션 텍스트는 보안에 중요하지만 (보안(side-channeling) 참조), 웹 드라이버가 실제로 비밀의 일부가 아닌 문자를 추출하게 만듭니다.

이 문제를 해결하기 위해, 웹 드라이버로 얻은 텍스트가 주어지면 stripDistractionFromText()는 그 텍스트에서 디스트랙션 텍스트를 제거하여 테스트가 찾을 것으로 기대하는 정확한 문자열만 남깁니다.

디스트랙션 텍스트에 대해 걱정하지 마세요. 그 텍스트는 앱에서 사용자에게 절대 표시되거나 상호작용할 수 없지만, 보안상의 이유로 존재해야 합니다.```javascript new LavaDomeJavaScript(root).text('123456'); const element = await driver.findElement('#ROOT'); // driver const text = await element.getText(); // driver LavaDomeDebug.stripDistractionFromText(text) === '123456'; // true

root@kitploit:~
## 개발

**`LavaDome`**의 로컬 개발 빌드를 설정하려면 이 저장소를 클론하고 다음 명령어 중 하나를 실행하세요:```bash
npm install && npm install --global serve

[No input content provided.]```bash yarn install && yarn global add serve

root@kitploit:~
## 해결책

[`ShadowDom`](https://web.dev/articles/`ShadowDom`-v1) Web API는 DOM 노드를 격리하고 캡슐화할 수 있게 해줍니다. 비록 [보안 기능으로 설계된 것은 아니지만](https://web.dev/articles/`ShadowDom`-v1#:~:text=Note%3A%20Closed%20shadow%20roots%20are%20not%20very%20useful.%20Some%20developers%20will%20see%20closed%20mode%20as%20an%20artificial%20security%20feature.%20But%20let%27s%20be%20clear%2C%20it%27s%20not%20a%20security%20feature.for%20Closed%20mode%20simply%20prevents%20outside%20JS%20from%20drilling%20into%20an%20element%27s%20internal%20DOM.), `ShadowDom`은 페이지의 다른 곳에서 실행되는 JavaScript 및 CSS로부터 DOM 하위 트리를 격리하는 데 잘 작동합니다.

**`LavaDome`**의 기본 접근 방식은 `ShadowDom`을 활용하면서 [잠재적인 보안 허점](https://blog.ankursundara.com/shadow-dom/)을 신중하게 해결하는 것입니다.

**[LavaDome](https://github.com/lavamoat/lavadome/)**는 사용자 및 신뢰할 수 있는 코드와의 상호작용만 허용하면서 앱의 신뢰할 수 없는 JavaScript 및 CSS 코드의 접근 시도를 차단하는 프런트엔드 전용 구성 요소를 구현하기 위한 **LavaMoat 도구 모음의 보안 도구**입니다.

> [@arxenix](https://github.com/arxenix) 님의 `ShadowDom` 보안에 대한 [연구](https://blog.ankursundara.com/shadow-dom/)에 감사를 표합니다. 이 연구는 **`LavaDome`**에 구현된 주요 보안 개선 사항의 기반이 되었습니다.

## 목표

**`LavaDome`** 프로젝트는 다음 핵심 원칙을 따릅니다:

### 보안

우리의 최우선 과제는 빈틈없는 보안을 제공하는 것입니다. 우리는 민감한 정보를 표시할 때 안전하게 사용할 수 있도록 `ShadowDom` API를 고급 보안 속성으로 감쌌습니다.

이 노력에 대해 자세히 알아보려면 [보안](#Security)을 방문하세요.

### DX

우리는 간소화된 개발자 경험을 제공하기 위해 노력합니다. 이를 위해 다음을 수행할 것입니다:

1. 가능한 한 많은 인기 프레임워크(React, Angular 등)를 지원합니다;
2. API를 쉽고 간단하게 사용할 수 있게 만듭니다.

### 읽기 전용 모드

현재 단계에서는 쓰기 모드를 지원할 계획이 없습니다. 즉, **`LavaDome`**은 보호를 위해 일반 텍스트 콘텐츠만 허용하며, 그보다 복잡한 것은 허용하지 않습니다.

이는 쓰기 모드를 지원하려면 다루기 어려운 격리된 DOM을 구현해야 하기 때문이며, 이는 현재 시점에서 아직 감당할 준비가 되지 않은 여러 보안 문제를 야기합니다. 예를 들면:

1. 이벤트 리스너 보안 - 외부 코드가 LavaDome 내부 노드로 향하는 입력을 가로채는 것을 방지합니다.
2. 오버레이 보안 - 악성 코드가 **`LavaDome`** 위에 피싱 DOM을 깔아 사용자가 민감한 입력을 잘못된 대상에게 제공하도록 만드는 것을 방지합니다.

## 설계

이 프로젝트의 설계 복잡성은 높지 않습니다. 그러나 구현하는 보안 원칙의 결합된 요구 사항을 충족하는 것은 결코 사소한 작업이 아닙니다 ([보안](#Security) 참조).

**`LavaDome`**은 다음 패키지로 구성됩니다:

### [Core](https://github.com/lavamoat/lavadome/blob/main/packages/core)

소비자와 보호된 격리 구성 요소 간의 통신을 중재하는 기본 API 레이어를 구현합니다. API는 가능한 한 높은 보안 수준을 유지하기 위해 격리 구성 요소 내부의 실제 DOM 노드를 누구에게도 제공하지 않으면서(심지어 LavaDome의 소비자에게도) 외부 조작을 최대한 허용하는 것을 목표로 합니다.

또한, 기본적으로 보안 기능이 아닌 `ShadowDom`의 본래 특성과 대조적으로, `ShadowDom` 기능 사용이 진정으로 안전하도록 필요한 모든 보안 강화를 구현할 책임을 집니다 ([보안](#Security) 참조).

> 기억하세요: 코어 패키지는 프로덕션 용도로 사용해서는 안 됩니다!

### [JavaScript](https://github.com/lavamoat/lavadome/blob/main/packages/javascript) / [React](https://github.com/lavamoat/lavadome/blob/main/packages/react) / etc

개발자가 JavaScript 또는 React 컴포넌트(또는 기타 다른 플랫폼 - [언제든 문의하세요!](https://github.com/lavamoat/lavadome/issues/new?title=**`LavaDome`**+misses+support+for+...))를 통해 선호하는 방식으로 **`LavaDome`**을 사용할 수 있도록 기능을 내보냅니다.

> 참고: **`LavaDome`**의 프레임워크 지원을 제공하려면 우리가 통제할 수 없는 타사 코드가 통합되며, 이로 인해 "보안 사각지대"가 발생합니다.

> 타사 프레임워크와 함께 **`LavaDome`**을 사용할 때 최대한 안전하게 유지하는 방법을 알아보려면 [보안](#Security) 섹션을 읽어주세요.

## 보안

프로젝트에 **`LavaDome`**을 사용할 계획이라면, 알아두어야 할 보안 측면은 다음과 같습니다:

### `ShadowDom` vs `iframe`

다시 말하지만, 이 프로젝트는 아직 실험적이지만, 우리는 이 결정에 대해 많은 고민을 했습니다. `ShadowDom`을 사용하는 것의 자연스러운 대안은 교차 출처 `iframe`들을 활용하는 것입니다. 교차 출처 `iframe`을 침투하는 것은 불가능하며, W3C 사양에 의해 보안에 중요한 메커니즘으로 인정받고 있습니다. 즉, 만약 침해가 어떻게든 발생한다면 보안 취약점으로 취급되어 브라우저 공급업체가 긴급하게 수정합니다.

그러나 이 접근 방식의 단점은 iframe 기반 솔루션을 통합하는 것이 UI/UX/DX 측면에서 훨씬 더 어렵다는 점입니다, 특히 대규모 채택을 목표로 하는 도구로서는 더욱 그렇습니다.

**`LavaDome`**은 호스트 DOM 트리 내에서 캡슐화된 섀도 DOM 노드의 안전한 통합을 촉진하면서 원활하고 자연스러운 개발자 경험을 제공해야 하며, `ShadowDom`은 정확히 그 목적을 위해 만들어진 DOM 지향 API입니다. 이로 인해 `ShadowDom`이 우리의 목표에 더 적합했습니다.

`ShadowDom` API는 제작자에 의해 공식적으로 보안 도구로 승인되지는 않았지만, 그 구현은 매우 안전하며 매우 특정한 시나리오를 제외하고는 섀도 DOM 트리 내부에서 캡슐화된 정보를 유출하지 않습니다.

우리는 바로 그러한 시나리오를 신중하게 해결함으로써 `ShadowDom`이 안전한 DOM 캡슐화 API로 강화될 수 있다고 믿습니다 (시도해 볼 가치가 있습니다).

### 위협

`LavaDome`과 같은 `ShadowDom` 기반 솔루션에 존재하는 현재의 보안 위협을 해결하는 것이 중요합니다.

#### 1. 주입

개발자는 **`LavaDome`**에 HTML/JS/CSS 콘텐츠를 제공할 수 있으며, 로드될 때 예를 들어 런타임에 JavaScript 코드를 동적으로 추가하여 실수로 또는 의도적으로 `ShadowDom` 내부의 DOM 노드를 유출할 수 있습니다.

이 기술에 대해 자세히 알아보려면 [@arxenix](https://github.com/arxenix)의 [연구](https://blog.ankursundara.com/shadow-dom/#contenteditable-or-css-injection)를 읽어보세요.

이러한 가능성을 방지하기 위해 **`LavaDome`**은 섀도 DOM 트리에 DOM 노드를 전혀 허용하지 않으며, 일반 텍스트 캡슐화만 지원합니다. 이를 통해 사용자가 제공한 HTML/JS/CSS 콘텐츠를 신뢰하는 데 내재된 보안 문제를 처리하지 않아도 됩니다.

우리는 DOM 노드 및 하위 트리 입력을 지원하는 안정적이고 안전한 방법을 연구하면서 향후 이 결정을 재검토하고 싶습니다.

#### 2. 검색 가능성

[find()](https://developer.mozilla.org/en-US/docs/Web/API/Window/find) API를 사용하면 개발자가 포함된 텍스트를 검색하여 DOM 노드를 찾고 추출할 수 있습니다. 이것은 지금까지 `ShadowDom` 내부에서 DOM 노드를 성공적으로 유출하는 것으로 알려진 유일한 API입니다.

<details>
<summary>
    Firefox에서는 텍스트를 찾은 후 <code>getSelection()</code> API를 사용하여 `ShadowDom` 내부의 DOM 노드를 유출할 수 있으며, 이로 인해 전체 아이디어가 손상됩니다: <i>(클릭하여 펼치기)</i>
</summary>```js
// defender
const secret = 'AN UNPREDICTABLE SECRET';
const opts = { mode:'closed' };
const root = document.body.firstElementChild.firstElementChild;
const p = document.createElement('p');
const shadow = root.attachShadow(opts);
shadow.append(p);
p.innerText = 'Secret is: ' + secret;

// attacker
setTimeout(() => {
    find('Secret is:'); // assuming the Shadow includes predictable text
    console.log('stolen secret: ', getSelection().anchorNode.textContent);
});
`ShadowDom` Firefox 우회

이 기법에 대해 자세히 알아보려면 @arxenix의 연구를 읽어 보세요.

이 공격을 방어하려면 LavaDome 소비자는 LavaDome API에 예측 가능한 콘텐츠를 전달해서는 안 됩니다. 당연하게 들릴 수 있지만, 개발자는 The secret is: ldsjf9304rjdkn처럼 보이는 입력을 **LavaDome**에 전달하고 싶은 유혹을 쉽게 받을 수 있으며, 이는 **LavaDome**의 보안을 완전히 손상시킬 수 있습니다. ldsjf9304rjdkn 부분은 추측할 수 없더라도, 고정된 문구 "The secret is: "는 특히 그 문구가 이전에 DOM에 노출된 적이 있다면 비밀을 드러내는 데 악용될 수 있습니다.

따라서 **LavaDome**을 사용할 때 개발자는 반드시 100% 예측 불가능한 텍스트만 입력으로 전달해야 합니다.

Chromium은 위 공격에 대해 안전합니다. 그러나 `ShadowDom` 내에서 선택된 DOM 노드가 content-editable인 경우, 공격자는 document.execCommand('insertHTML', ...)를 활용하여 `ShadowDom`의 내부 스코프에서 임의 코드 실행을 달성하고, 이를 사용하여 캡슐화된 DOM 노드에 접근할 수 있습니다. (클릭하여 펼치기) ```js // defender const secret = 'AN UNPREDICTABLE SECRET'; const opts = { mode:'closed' }; const root = document.body.firstElementChild.firstElementChild; const div = document.createElement('div'); const shadow = root.attachShadow(opts); shadow.append(div); const p = document.createElement('p'); p.innerText = 'Secret is: ' + secret; div.appendChild(p); div.setAttribute('contenteditable', 'true');

// attacker setTimeout(() => { console.log(1, 'stolen secret:'); const bypass = '<audio/src/onerror=console.log(2,this.nextSibling.innerHTML)>'; find('Secret is:'); // assuming the Shadow includes predictable text // assuming the found node is contenteditable=true document.execCommand('insertHTML', false, bypass); });

root@kitploit:~
<div align="center"><img width="800" src="https://assets.kitploit.com/production/public/readmes/48843/b3f770ae9d6ea6070e8ed984e33a2f4df5112128d21d3c01000dd66ee1143733.png" alt="`ShadowDom` bypass Chromium"/></div>
</details>

이 공격 벡터를 방어하기 위해, **`LavaDome`**은 가능한 가장 높은 우선순위의 스타일 속성(`-webkit-user-modify: unset;`)을 사용하여 커스텀 엘리먼트에서 모든 스타일 속성을 제거합니다. 이는 해당 엘리먼트가 `ShadowDom` 엘리먼트를 `contenteditable`로 만드는 `-webkit-user-modify:read-write` 속성을 적용하는 악성 외부 CSS 주입에 취약하지 않도록 보장합니다.

`contenteditable`을 속성으로 사용하는 두 번째 기법은 현재 관련이 없습니다. **`LavaDome`**은 DOM 노드 수용을 지원하지 않기 때문입니다.

#### 3. 선택 가능성 & 비밀 분할

위의 공격 벡터들은 [`getSelection`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getSelection)이 완화된다면 그다지 유용하지 않습니다. **`LavaDome`**에 포함된 텍스트를 선택 불가능하게 만들면 위에서 설명한 것과 같은 가능한 주입에 대한 보안을 강화할 수 있습니다. 이는 Chromium에서는 잘 작동하지만, Firefox에서는 몇 가지 문제를 해결 중입니다.

공격자가 비밀의 일부 하위 집합을 추측할 수 있다면, 전체 비밀을 손상시킬 수 있습니다(Firefox에서처럼 `getSelection`이 범위가 지정된 노드를 캡처한다고 가정할 때). 하위 집합을 검색하면 그 하위 집합을 포함하는 텍스트 노드가 유출되어 공격자에게 전체 비밀에 대한 접근 권한을 주기 때문입니다.

대응책으로, **`LavaDome`**은 비밀의 각 문자를 자체 `ShadowDom`에 저장하여, 비밀의 일부 하위 집합이 손상되더라도 나머지가 함께 손상되지 않도록 보장합니다. 이 보호 장치는 비밀이 길수록, 그리고 포함할 수 있는 문자 옵션이 많을수록 공격자가 전체 비밀을 유출하는 것을 기하급수적으로 더 어렵게 만드는 추가적인 이점도 있습니다.

침해는 여전히 가능하지만, 공격자가 가능한 모든 문자를 하나씩 무차별 대입하고, 찾은 모든 섀도를 유출한 다음, **`LavaDome`** 메인 호스트 내의 각각의 위치에 맞게 모든 섀도를 동기적으로 올바르게 재정렬하는 경우에만 가능합니다.

#### 4. 사이드 채널링

또 다른 잘 알려진 공격은 `@font-face`와 같은 상속 가능한 CSS 속성을 사용하여 ShadowDOM의 내용을 원격 서버로 문자 단위로 유출하는 것입니다.

다음 [연구](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html)를 고려해 보세요. [@masatokinugawa](https://github.com/masatokinugawa)가 문서화한 내용입니다.

이를 해결하기 위해 LavaDome은 부모 Shadow에 가능한 모든 문자를 추가하여, 이러한 유출 시도가 모든 가능한 문자를 찾을 때 혼란을 겪게 만들어 이 공격을 무용지물로 만듭니다(https://github.com/LavaMoat/LavaDome/issues/16 참조).

물론 사이드 채널링은 다양한 형태로 나타나며, 그중 일부는 해결하기 더 어렵습니다. 예를 들어 [@securityMB](https://github.com/securityMB)의 [연구](https://research.securitum.com/stealing-data-in-great-style-how-to-use-css-to-attack-web-application/)에서는 합자(ligature) 글꼴을 사용합니다([@masatokinugawa](https://github.com/masatokinugawa)의 익스플로잇 @ https://github.com/LavaMoat/LavaDome/issues/40).

이를 해결하기 위해 LavaDome을 도입하는 개발자는 엄격한 `font-src` CSP 정책을 지정하여 글꼴을 통한 원격의 통제 불가능한 서버로의 유출이 불가능하도록 해야 합니다.

주목할 점은 이 공격이 Safari에서는 (이론적으로) 효과가 없을 수 있다는 것입니다. Safari에서는 로컬 SVG를 사용하여 글꼴을 구성할 수 있어 공격자가 CSP와 무관하게 공격을 수행할 수 있기 때문입니다(WIP @ https://github.com/LavaMoat/LavaDome/issues/40#issuecomment-2090318009 참조).

사이드 채널링 공격의 또 다른 훌륭한 예 - 이번에는 글꼴을 사용하지 않는 - 는 텍스트 조각을 활용하는 것입니다([@masatokinugawa](https://github.com/masatokinugawa)의 [익스플로잇](https://github.com/LavaMoat/LavaDome/issues/35) 참조).

#### 5. 방어적 코딩

안전한 솔루션은 방어적 코딩 관행을 요구합니다.

- 이를 위해 우리가 사용하는 모든 네이티브 API는 내부 사용을 위해 캐시되어, 공격자가 전역 API를 재구성하여 **`LavaDome`**의 실행 흐름을 방해하는 것을 방지합니다.

- 소스 코드에서 비전통적인 스타일 선택을 발견한다면, 그 이유는 방어적 코딩 원칙에 기반했을 가능성이 높습니다.

- 신뢰하지 않는 스크립트보다 먼저, 가급적이면 모든 스크립트보다 먼저 **`LavaDome`**을 앱에 포함시키는 것이 **중요**합니다.

- **`LavaDome`**의 프레임워크 버전을 사용할 때는 이러한 프레임워크가 방어적으로 작성되지 않았으며, 사용되는 네이티브 API가 악의적인 간섭으로부터 안전하지 않다고 가정해야 합니다. 외부 코드의 보안은 **`LavaDome`**의 통제 범위 밖이라는 점에 유의하세요.

따라서 우리는 항상 이러한 보안 솔루션을 [@agoric](https://github.com/agoric)이 개발한 [SES](https://github.com/endojs/endo/tree/master/packages/ses#ses) 기술과 통합할 것을 권장합니다. 이는 [LavaMoat](https://github.com/lavamoat/lavamoat)과 [MetaMask](https://github.com/MetaMask/metamask-extension)에서 따르는 보안 관행입니다.

#### 6. React 내부 처리 유출

걱정해야 할 또 다른 사항(특히 React 컨텍스트에서)은 React 컴포넌트에 제공된 입력이 React에 의해 전역 객체로 적극적으로 유출되어, 앱에서 실행되는 신뢰할 수 없는 엔터티가 이를 차지할 수 있게 된다는 사실입니다(이는 `LavaDome`의 목표를 완전히 훼손합니다).

자세한 내용은 [naugtur](https://github.com/naugtur)의 [발견](https://github.com/LavaMoat/LavaDome/pull/23#issue-2093459897)을 참조하세요.

React 지원 의도와 비밀을 React에 맡길 수 없다는 사실 사이에서 균형을 맞추기 위해, `LavaDomeReact` 패키지는 비밀을 React에 전달하기 전에 특수 토큰과 교환하는 최소한의(그러나 안전한) 기능을 내보냅니다. 이때 해당 토큰을 다시 비밀로 교환할 수 있는 유일한 엔터티는 `LavaDome` 자체입니다.

강력하지만, 불행히도 React 사용자는 비밀을 `LavaDomeReact`에 전달하기 전에 교환을 적극적으로 수행해야 합니다.

사용자가 잘 알려진 토큰 외의 다른 것을 수신하면 `LavaDome`에서 생성된 예외가 발생하여 개발자가 `LavaDomeReact`를 안전하게 사용하도록 강제합니다.

## 면책 조항

위의 모든 내용을 읽었다면, **`LavaDome`**이 여전히 매우 실험적이라는 이유를 충분히 이해했을 것입니다. 보안 기능이 아닌 기능을 안전하게 만드는 것은 본질적으로 위험하지만, 이 문제 영역에는 좋은 기존 솔루션이 없기 때문에 이 시도가 올바른 방향으로의 한 걸음이라고 생각합니다.

우리는 여전히 **`LavaDome`**을 사용할 것을 권장합니다. 현재 웹 표준에만 의존하는 것과 비교할 때 명백한 개선을 나타내기 때문입니다. 다만 우리의 솔루션이 코드를 "더 안전하게(safer)" 만들 뿐 "안전하게(safe)" 만들지는 않는다는 점을 기억하세요.

추가로, 다음을 기억하세요: LavaDome은 비밀을 안전하게 DOM으로 가져오는 데 도움을 줍니다. 비밀이 LavaDome에 전달되기 전에 유출되었는지 여부는 LavaDome의 범위 밖입니다.

즉, 비밀을 LavaDome과 공유하는 시점까지 비밀이 안전하도록 보장하는 것은 사용자의 책임입니다.

이를 달성하는 가장 좋은 방법은 [SES](https://github.com/endojs/endo/tree/master/packages/ses#ses) / [LavaMoat](https://github.com/lavamoat/lavamoat)을 사용하여 잠긴 환경에서 실행하는 것입니다.
도구 다운로드