[BRS] 브라우저 런타임 보안 플랫폼

과정
정보 보안
노출 페이지
대표 이미지
대표이미지
서비스 한 줄 소개
회차
5 more properties

배경(Problem)

보안뉴스,”폴리필 도메인 대상 공급망 공격, 글로벌 사이트 공격 악용해 더욱 가세” 발표 → 중국 기업 펀눌, Polyfill.io 도메인 인수 후 10만개 이상 도메인에 악성코드 주입 → https://www.boannews.com/media/view.asp?idx=131258
MDPI, “브라우저 보안 수준을 CVE 기반 지표(ICVE/ICVSS/IR/IT)로 정량 비교한 연구” 발표 → 브라우저 런타임 기반 공격이 크게 활성화 됐음을 알려주는 경고 문서 → https://www.mdpi.com/1999-5903/17/3/104
사람들의 안일한 생각, 고도화되는 공격자의 기법, 늦은 피해 사실 자각으로 피해량 증가 → 침해 사고 피해 최소화 및 적절한 사후 대응을 제공하기 위한 플랫폼 개발
웹 서비스의 보안을 이야기할 때 대부분은 서버를 기준으로 생각합니다.
서버 로그가 정상이면 괜찮다고 넘어가는 경우가 많습니다.
그런데 실제 피해는 서버가 아니라 브라우저가 실행되는 순간, 사용자의 화면 앞에서 일어납니다.
2024년에 발생한 polyfill.io 공급망 공격 사건이 그 대표적인 사례입니다.
전 세계 10만 개 이상의 사이트에서 사용하던 외부 JS 라이브러리 경로가 악성 코드로 오염되었고,
서버 자체는 정상이었지만 사용자의 브라우저에서는 전혀 다른 스크립트가 실행되고 있었습니다.
서버 로그만 봐서는 이 공격을 탐지하는 것이 거의 불가능했습니다.
이런 문제는 구조적인 원인이 있습니다.
현대 웹 서비스는 광고 SDK, 분석 도구, 위젯, 결제 모듈 등 수많은 서드파티 JS에 의존하고 있고,
이 스크립트들은 페이지 동작 자체를 좌우할 수 있습니다.
공격자는 이 틈을 노려 사용자가 링크를 클릭하거나 폼을 제출하는 바로 그 순간,
DOM이나 요청 내용을 조작합니다.
사용자는 자신이 변조된 행위를 한다는 사실을 인지할 수 없고,
서버도 정상적인 요청이 들어왔다고 판단합니다.
구체적으로 문제는 세 가지로 정리됩니다.
첫째, 서버 로그의 한계입니다.
사용자가 실제로 어떤 링크를 클릭했는지, 폼에 어떤 값을 입력했는지,
요청이 중간에 변조되지 않았는지를 서버 로그만으로 검증하기 어렵습니다.
둘째, 사용자 보호 공백입니다.
클릭재킹, 폼 전송 변조, 스크립트 주입 등의 공격이 발생해도
사용자는 브라우저 화면만 보기 때문에 피해 사실을 인지하지 못합니다.
셋째, 사후 분석 의존입니다.
사고가 터진 뒤에야 그때 브라우저에서 이런 동작이 있었구나 하고 분석하게 되지만,
그 근거가 될 브라우저 측 기록이 남아 있지 않아 원인 파악이 어렵습니다.
BRS는 이 지점에서 출발했습니다.
서버가 정상이더라도 브라우저 런타임에서 무슨 일이 일어나는지 감시하고,
단순히 이상하다고 경고하는 것을 넘어
왜 위험한지에 대한 근거까지 기록으로 남기는 것을 목표로 했습니다.

서비스 소개(Solution)

BRS(Browser Runtime Security)는 브라우저 런타임에서 발생하는 위협 행위를 탐지하고,
그 근거를 Evidence로 기록해 관리자가 사후에도 원인을 추적할 수 있게 해주는 보안 플랫폼입니다.
Chrome 확장프로그램 형태의 센서가 브라우저 안에서 직접 동작하며,
탐지된 이벤트는 클라우드 백엔드에 저장되고 웹 대시보드에서 조회할 수 있습니다.

주요 링크

PoC Hub (공격 시나리오 테스트): https://brs-poc-hub.onrender.com

기능 소개

1. 런타임 행위 탐지 및 실시간 경고
확장프로그램을 설치하면 즉시 감시가 시작됩니다.
브라우저가 페이지를 로드하는 document_start 시점부터
DOM 변화, 스크립트·iframe 삽입, 폼 전송 변조, XHR/fetch 후킹, 링크 하이재킹, 클릭재킹,
서비스워커 등록 시도 등 크게 7가지 유형의 런타임 위협을 탐지합니다.
위협이 감지되면 화면에 팝업 경고가 표시되고,
위험도(LOW / MEDIUM / HIGH)와 함께 어떤 위협인지 사용자가 이해할 수 있는 설명을 제공합니다.
2. Evidence(근거) 기록
단순 경고로 끝나지 않고, 탐지 시점의 변조 전·후 값, 스크립트 출처(initiator/stack), 오리진 변화,
타이밍(deltaMs) 등을 Payload로 함께 저장합니다.
이 기록이 있어야 "왜 위험한지"를 설명할 수 있고, 사고 후 원인 분석이 가능합니다.
3. 룰 기반 스코어링
99개 룰셋(기본 동적 탐지 55개 + 스크립트 주입 탐지 44개)이 이벤트 타입과 데이터 조건을 매칭해 scoreDelta를 누적합니다.
점수 구간에 따라 LOW(~49) / MEDIUM(50~79) / HIGH(80~)로 분류되며,
여러 룰이 연쇄적으로 적용되면 점수가 올라가는 체인 구조도 지원합니다.
4. AI 악성 스크립트 분석
탐지된 스크립트 주입 이벤트의 점수가 MEDIUM 구간(50~79)에 해당할 경우,
GPT를 통한 악성 판별을 추가로 수행합니다.
난독화된 스크립트는 WebCrack으로 복호화한 후 재평가합니다.
AI 판단 결과(MALICIOUS/BENIGN)와 신뢰도, 주요 위협 유형이 대시보드 상세 페이지에 함께 표시됩니다.
5. 관제 대시보드
React 기반 웹 대시보드에서 세션 타임라인, 위험도 비율 도넛 차트,
TOP5 탐지 룰, 도메인별 탐지 현황, 이벤트 상세 Evidence, 그리고 SOC 추천 대응 조치까지
한 화면에서 확인할 수 있습니다.
6. PoC Hub
공격 시나리오를 직접 재현하고 BRS가 탐지하는지 검증할 수 있는 공개 테스트 환경입니다.
PoC-A부터 PoC-H까지 총 8종의 시나리오가 준비되어 있으며,
각 PoC 상세 페이지에서 단계별 체크리스트를 따라가면 누구나 테스트할 수 있습니다.

시연 영상

아키텍처 및 핵심 기능

BRS는 Browser Sensor → Analysis Core → Visualization 세 블록이 순서대로 연결되는 구조입니다.
1. 사용자가 일반 또는 악성 웹페이지에 접속합니다 (PoC Hub로 테스트 가능). 2. Chrome Extension이 브라우저 안에서 자동으로 런타임 감시를 시작합니다. 3. 위협이 감지되면 브라우저 내부에서 즉시 Local Risk & Alert UI로 경고를 표시합니다. 4. 탐지된 이벤트 로그를 Analysis Core(AWS)로 전송합니다. 5. 분석·저장된 결과를 대시보드(Visualization)에서 조회합니다.

상세 설명

Browser Sensor (Chrome Extension)
사용자의 브라우저 안에서 직접 동작하는 탐지 센서입니다.
Script Injection Detector: page_hook.js가 MAIN world에 주입되어 atob, eval, Function 등 동적 실행 API를 후킹합니다. 외부 스크립트·iframe의 동적 삽입과 XSS 패턴을 감지합니다.
Frame Clickjacking / UI Hijack Detector: 투명 오버레이나 숨김 iframe을 통한 클릭 가로채기, 링크 클릭 직전 href 스왑 등 UI 하이재킹 행위를 탐지합니다.
Network / XHR Monitor: XMLHttpRequest.prototype과 HTMLFormElement.prototype을 후킹해 요청 가로채기, 폼 전송 변조, 외부 오리진 데이터 유출 정황을 감시합니다.
Local Risk & Alert UI: 위협 감지 시 팝업 경고를 즉시 표시하고, 확장프로그램 팝업에서 현재 탭의 탐지 현황과 위험도를 확인할 수 있습니다.
Analysis Core (AWS)
브라우저 센서에서 전송된 이벤트를 수신·분석·저장하는 서버 사이드 파이프라인입니다.
Event Ingest API (API Gateway): 확장프로그램이 전송하는 이벤트 로그를 수신하는 진입점입니다. CORS 처리 및 요청 검증을 담당합니다.
Rule-based Analyzer (Lambda): Node.js 24 Lambda에서 룰셋(총 99개)을 적용해 이벤트를 분류하고 위험도 점수를 산정합니다. 기본 동적 탐지 55개 룰 + 스크립트 주입 탐지 44개 룰로 구성되며, 여러 룰이 연쇄 적용되는 체인 구조를 지원합니다.
AI Scoring System (GPT): 룰 점수가 MEDIUM 구간(50~79)에 해당하는 이벤트에 대해 GPT를 활용한 악성 판별을 추가 실행합니다. 난독화된 스크립트는 WebCrack으로 복호화한 후 재평가하며, MALICIOUS/BENIGN 판정과 신뢰도, 주요 위협 유형을 함께 기록합니다.
Event Store (DynamoDB): 원본 및 분석된 위협 이벤트를 저장합니다. Hot partition 방지를 위해 8 shard 구조를 적용했고, TTL 30일로 자동 삭제됩니다. 대용량 스크립트 덤프는 S3에 별도 보관합니다.
Visualization (React Dashboard)
저장된 위협 이벤트를 조회·분석할 수 있는 관제 대시보드입니다.
Event Timeline View: 세션 단위로 탐지 이벤트를 시간순으로 누적한 타임라인 그래프입니다. 각 이벤트의 위험도와 탐지 룰을 한눈에 확인하고, 상세 페이지에서 Evidence(변조 전·후 값, initiator/stack, 오리진 변화)와 SOC 추천 대응 조치를 확인할 수 있습니다.
Risk Overview & Charts: 전체 위험도 비율 도넛 차트와 TOP5 탐지 룰 막대 그래프로 위협 트렌드를 시각화합니다.
Suspicious Domains Ranking: 탐지 빈도 기준으로 의심 도메인을 순위화해 보여줍니다. 어떤 도메인에서 위협이 집중되는지 파악할 수 있습니다.
Detailed Analyzer & Export: 도메인별 상세 탐지 내역 조회 및 리포트 내보내기 기능을 제공합니다.

핵심 기능 목록

기능
설명
런타임 행위 탐지
스크립트 주입, 클릭재킹, 링크 하이재킹, XHR 후킹, 폼 제출 후킹, SW 지속성 등 7가지 탐지
Evidence 기록화
변조 전·후 값, initiator/stack 출처, crossSite 여부, 타이밍을 Payload로 저장
룰 기반 스코어링
99개 룰셋, scoreDelta 누적, LOW/MEDIUM/HIGH 분류
AI 악성 스크립트 판별
GPT 활용, WebCrack 난독화 해제 후 재평가, 신뢰도 및 위협 유형 표시
관제 대시보드
세션 타임라인, 통계 차트, 이벤트 상세, SOC 추천 대응 조치 제공
PoC Hub
8종 공격 시나리오, 체크리스트 기반 단계별 재현·검증 환경
화이트리스트 정책
신뢰 도메인 예외 처리, 위험도별 경고 알림 ON/OFF 설정

활용 라이브러리 및 개발 환경

확장프로그램

Chrome Extension Manifest V3
MV2 대비 보안이 강화된 최신 표준으로, Service Worker 기반 background를 사용해 메모리 효율이 높습니다. 다만 MV3에서는 eval 등 동적 코드 실행 후킹이 제한되어 있습니다. 이를 해결하기 위해 MAIN world 스크립트 주입과 content script를 window.postMessage로 연결하는 이중 구조를 구현했습니다.
Web Crypto API
인라인 스크립트 원문의 SHA-256 해시를 생성해 중복 덤프를 방지합니다. 동일한 악성 스크립트가 반복 탐지될 때 DynamoDB 중복 저장 비용을 줄이는 핵심 기제입니다. 브라우저 네이티브 API이므로 별도 라이브러리 없이 사용 가능하며, 확장프로그램 번들 크기에 영향이 없습니다.
MutationObserver
DOM 변화를 비동기적으로 감시하는 브라우저 내장 API입니다. 폴링 없이 script 태그·iframe의 동적 삽입을 즉시 감지할 수 있어 스크립트 주입 탐지의 핵심 인프라로 사용했습니다. detectors/mutation_observer.js와 dom_mutation.js 두 모듈에서 활용합니다.
JSON Ruleset Engine
탐지 정책을 코드에서 완전히 분리한 JSON 기반 룰셋 아키텍처입니다. 기본 동적 탐지 55개 + 스크립트 주입 탐지 44개 = 총 99개 룰이 적용됩니다. Chrome Storage에 저장된 룰셋을 우선 사용하고, 없으면 번들 기본 룰셋을 사용하므로 코드 수정 없이 룰셋만 교체해 탐지 정책을 변경할 수 있습니다.

백엔드

AWS Lambda
서버리스 구조로 인프라 관리 없이 이벤트 양에 따라 자동 스케일 아웃됩니다. 역할별로 4개 함수를 분리해 단일 책임 원칙을 지켰습니다.
BRS_Events_Ingest — 이벤트 수신·저장
BRS_Events_Query — 목록·상세 조회
BRS_Events_Aggregates — 집계·트렌드
BRS_Dumps_Ingest — 스크립트 덤프 S3 저장
AWS DynamoDB
이벤트 저장소입니다. 트래픽 집중으로 인한 hot partition 방지를 위해 EVENT_SHARDS=8 환경변수로 파티션을 8개로 분산 저장합니다. TTL 30일로 오래된 데이터를 자동 삭제하고, DynamoDB 아이템 크기 제한(400KB)을 초과하는 대용량 payload는 S3로 오프로드합니다.
AWS S3
스크립트 덤프 원문 보관에 사용합니다. DynamoDB 크기 제한을 초과하는 대용량 payload와 스크립트 원본 파일을 분리 저장합니다. DynamoDB에는 S3 참조 경로(key)만 기록합니다.
AWS API Gateway
Extension과 Dashboard에 HTTP 엔드포인트를 제공합니다. CORS 설정과 요청 검증을 담당하며, /events/dumps/aggregates 라우팅을 처리합니다.
GPT (OpenAI)
MEDIUM 구간(50~79점) 이벤트에 GPT 기반 악성 판별을 추가 실행합니다. WebCrack으로 난독화를 해제한 후 재평가하며, MALICIOUS / BENIGN 판정 + 신뢰도 + 위협 유형을 대시보드에 표시합니다. 비용 문제로 GPT-5 대신 GPT-4를 적용했습니다.

대시보드

React 18
컴포넌트 기반으로 관제 대시보드 UI를 구성했습니다. 세션 타임라인, 이벤트 리스트, 위험도 차트, 상세 페이지(요약 / 환경 / 행위상세 / 원인·출처 / 증거 탭)를 각각 컴포넌트로 분리했습니다. Query API 클라이언트(BRSQuery.ts)에는 TypeScript를 부분 적용해 API 응답 타입 안전성을 확보했습니다.
Tailwind CSS + DaisyUI
유틸리티 기반 스타일링으로 빠른 UI 구성이 가능합니다. DaisyUI Admin Dashboard 템플릿을 베이스로 BRS 관제 UI에 맞게 커스터마이징했습니다. 테마 토글, 사이드바 네비게이션, 모달 등 DaisyUI 컴포넌트를 적극 활용했습니다.
Chart.js + react-chartjs-2
위험도 비율 도넛 차트, TOP5 탐지 룰 막대 그래프, 도메인별 탐지 현황, 세션 타임라인 그래프 등 대시보드 전반의 데이터 시각화에 활용했습니다.
Redux Toolkit
이벤트 목록, 필터 상태, 날짜 범위, 선택된 세션 등 대시보드 전역 상태를 Store로 일관성 있게 관리합니다. Redux Toolkit의 createAsyncThunk로 API 비동기 상태 처리를 단순화했습니다.
TypeScript
Query API 클라이언트 BRSQuery.ts에 부분 적용해 API 응답 타입 안전성을 확보했습니다. DynamoDB 이벤트 스키마를 인터페이스로 정의해 대시보드 컴포넌트에서 안전하게 데이터를 사용할 수 있도록 했습니다.
Vercel
대시보드 배포 플랫폼입니다. GitHub push 시 자동 빌드·배포가 이루어지고, CDN 기반으로 전 세계에서 빠른 응답을 제공합니다. PoC 데모 중 로컬 React dev server 포트(3000) 충돌을 배포 URL로 우회하는 데도 활용했습니다.

PoC · 로컬 수집기 · 난독화 해제

Node.js + Express
8종 PoC 시나리오 서버와 로컬 수집기(collector-local) 모두 Express로 구현했습니다. 로컬 수집기는 이벤트와 덤프를 파일 시스템에 저장하는 경량 대체 서버로, AWS 없이도 로컬에서 전체 파이프라인(탐지→수집→조회)을 테스트할 수 있습니다. PoC 서버는 기본 포트 3000, 로컬 수집기는 포트 8080을 사용합니다.
PoC-A~H: 링크 변조 / 클릭재킹 / XHR 후킹
폼 변조 / SW 지속성 / 스크립트 주입 등 8종
WebCrack
탐지된 스크립트 중 난독화된 코드를 자동 복호화하는 오픈소스 라이브러리입니다. 난독화된 상태로는 AI가 악성 여부를 판단하기 어렵기 때문에, WebCrack으로 복호화한 후 GPT에 재전달합니다. 오픈소스 의존이라는 한계가 있지만 프로젝트 범위 내에서 가장 실용적인 선택이었습니다.

개발 환경

Chrome 개발자 모드에서 extension-sensor/ 폴더를 unpacked 방식으로 로드해 개발·테스트했습니다. React dev server와 PoC 서버가 모두 기본 3000번 포트를 사용해 충돌이 발생할 수 있으므로, PoC 데모 중에는 배포된 Vercel 대시보드를 병행 사용하거나 포트를 변경해 운영했습니다. 버전 관리는 GitHub, 이슈 및 문서는 Notion을 사용했습니다.

트러블 슈팅

1. MV3 환경에서 네이티브 API 후킹 불가 문제

문제 배경

Chrome Extension Manifest V3에서는 content script가 ISOLATED world에서 실행됩니다. 이 환경은 페이지 JS와 완전히 분리된 별도의 실행 컨텍스트라, window.XMLHttpRequest, window.eval, HTMLFormElement.prototype 등 페이지의 네이티브 API에 접근해 후킹하는 것이 원천적으로 불가능했습니다. 때문에 BRS의 핵심 탐지 메커니즘인 XHR 후킹, 폼 제출 후킹, eval 감시를 content script에서 구현할 수 없는 상황이었습니다.

시도한 방법과 실패

처음에는 content script 안에서 document.createElement('script')src를 지정해 MAIN world에 동적으로 스크립트를 주입하는 방식을 시도했습니다. 하지만 MV3의 강화된 CSP(Content Security Policy) 정책 때문에 inline script 실행이 차단되었고, data-brs-internal 속성을 붙여 자체 스크립트와 구분하더라도 주입 타이밍이 document_start보다 늦어 실제 악성 스크립트가 먼저 실행되는 경우가 발생했습니다.

해결: MAIN world 직접 주입 + postMessage 브릿지

MV3에 추가된 "world": "MAIN" 옵션을 활용해 page_hook.js를 페이지 컨텍스트에 직접 주입했습니다.
manifest.json 설정:
{ "content_scripts": [ { "js": ["page_hook.js"], "run_at": "document_start", "world": "MAIN", "all_frames": true }, { "js": ["engine/rule_engine.js", "content.js", ...], "run_at": "document_start" } ] }
JSON
복사
page_hook.js는 MAIN world에서 실행되므로 XMLHttpRequest.prototype.open, HTMLFormElement.prototype.submit, atob, eval, Function 등을 자유롭게 후킹할 수 있습니다. 탐지 결과는 window.postMessage로 브로드캐스트하고, ISOLATED world의 content.js가 수신해 처리합니다.
page_hook.js 중복 주입 방지:
(() => { if (window.__BRS_PAGE_HOOK_LOADED__) return; window.__BRS_PAGE_HOOK_LOADED__ = true; const send = (type, data) => { window.postMessage({ __BRS__: true, type, data }, "*"); }; // ... 이후 API 후킹 로직 })();
JavaScript
복사
content.js 수신 측:
window.addEventListener("message", (e) => { if (e.source !== window || !e.data.__BRS__) return; const { type, data } = e.data; // 이벤트 처리 });
JavaScript
복사
__BRS__: true 플래그로 BRS 내부 메시지와 일반 페이지 메시지를 구분합니다. e.source !== window 검사로 외부 iframe에서 온 메시지도 차단합니다.

2. 인라인 스크립트 중복 탐지 및 대용량 Payload 문제

문제 배경

동일한 페이지를 반복 방문하거나 SPA에서 라우팅이 일어날 때, 동일한 인라인 스크립트가 DOM에 재삽입되면서 같은 스크립트가 수십 번 탐지·전송되는 문제가 발생했습니다. 특히 분석용 라이브러리나 광고 스크립트처럼 정상이지만 대용량인 스크립트가 반복 전송되면 DynamoDB 쓰기 비용과 저장 공간이 폭발적으로 증가했습니다. 더불어 일부 스크립트 원문이 DynamoDB 아이템 최대 크기 제한인 400KB를 초과하는 경우도 있었습니다.

해결: SHA-256 해시 기반 중복 제거 + 크기별 분기 저장

클라이언트(page_hook.js) — SHA-256 해시로 중복 차단:
const __BRS_INLINE_SEEN__ = new Set(); const __brsSha256Hex__ = async (text) => { const buf = new TextEncoder().encode(String(text || "")); const hash = await crypto.subtle.digest("SHA-256", buf); return Array.from(new Uint8Array(hash)) .map(b => b.toString(16).padStart(2, "0")).join(""); }; // 탐지 루프 내부 const sha256 = await __brsSha256Hex__(raw); if (__BRS_INLINE_SEEN__.has(sha256)) continue; // 중복 스킵 __BRS_INLINE_SEEN__.add(sha256);
JavaScript
복사
__BRS_INLINE_SEEN__은 페이지 생명주기 동안 유지되는 인메모리 Set으로, 이미 처리한 스크립트의 해시를 기록합니다. Web Crypto API(crypto.subtle)를 사용해 외부 라이브러리 없이 구현했습니다.
서버(BRS_Events_Ingest.mjs) — 크기별 분기:
const MAX_JSON_BYTES = Number(process.env.MAX_JSON_BYTES || 20_000); // 20KB const MAX_PAYLOAD_BYTES = Number(process.env.MAX_PAYLOAD_BYTES || 350_000); // 350KB // 페이로드 전체가 350KB 초과면 즉시 거부 if (jsonByteLength(payload) > MAX_PAYLOAD_BYTES) { return resp(413, { ok: false, error: "PAYLOAD_TOO_LARGE" }); } // DynamoDB에 저장할 payloadJson은 20KB로 제한 const limited = limitUtf8(payloadStr, MAX_JSON_BYTES); const payloadJson = limited.text; // 최대 20KB (잘린 경우 포함) const payloadTruncated = limited.truncated; // 잘렸는지 여부 const payloadHash = sha256Hex(payloadStr); // 원문 기준 해시 // DynamoDB 저장 (잘린 버전 + 해시 + 잘렸는지 여부) await ddb.send(new PutCommand({ TableName: TABLE, Item: { payloadJson, // 최대 20KB 문자열 payloadTruncated, // boolean payloadHash, // 원문 sha256 (중복 조회·정합성 검증용) ... } }));
JavaScript
복사
스크립트 원문 덤프는 BRS_Dumps_Ingest.mjs를 통해 S3(brs-threat-dumps-prod)에 별도 저장하고, DynamoDB에는 S3 참조 경로만 기록하는 방식으로 크기 제한을 우회했습니다.

3. DynamoDB Hot Partition 문제

문제 배경

이벤트 발생이 특정 시간대에 몰릴 경우, 단일 파티션 키에 쓰기가 집중되어 DynamoDB 스로틀링(ProvisionedThroughputExceededException)이 발생할 수 있었습니다. 특히 PoC 테스트 중 단시간에 수십 개의 이벤트가 연속 발생하면 쓰기가 실패하는 케이스가 나왔습니다.

해결: shard 기반 파티션 분산

이벤트 테이블 샤딩:
const EVENT_SHARDS = Number(process.env.EVENT_SHARDS || 8); // eventId의 SHA-256 앞 8자리를 정수로 변환 후 8로 나눈 나머지 const shard = parseInt( crypto.createHash("sha256").update(eventId).digest("hex").slice(0, 8), 16 ) % EVENT_SHARDS; const pk = `DAY#${day}#S#${shard}`; // ex) DAY#2026-02-19#S#3 const sk = `T#${revTs}#E#${eventId}`;
JavaScript
복사
집계 테이블도 동일하게 처리:
const AGG_SHARDS = Number(process.env.AGG_SHARDS || 4); const aggShardDomain = aggShardFor(`D|${orgKey}|${domainKey}`); // pk: `DAY#${day}#S#${aggShardDomain}`, sk: `DOMAIN#${domainKey}`
JavaScript
복사
eventId 자체를 해시해 shard 번호를 결정하므로 동일 이벤트는 항상 같은 shard로 라우팅됩니다. 조회 시에는 0~7번 shard를 병렬로 스캔해 합산합니다. 집계 테이블(Threat_Aggregates)도 AGG_SHARDS=4로 동일하게 처리했습니다.

4. 이벤트 전송 실패 및 유실 방지

문제 배경

사용자가 페이지를 빠르게 이동하거나 네트워크가 불안정한 경우, background.js의 Service Worker가 슬립 상태에 진입하면서 이벤트 전송이 중간에 끊기는 현상이 있었습니다. MV3의 Service Worker는 비활성 상태에서 임의로 종료될 수 있어 MV2의 persistent background page와 달리 전송 보장이 어려웠습니다.

해결: 실패 큐 + Exponential Backoff Retry + Chrome Alarm 재전송

실패 시 큐에 저장 (httpSink.js):
async function pushToQueue(items) { return runWithLock(LOCK_KEYS.HTTP_QUEUE, async () => { let { [STORAGE_KEYS.FAILED_QUEUE]: queue } = await chrome.storage.local.get({ [STORAGE_KEYS.FAILED_QUEUE]: [] }); const newQueue = queue.concat(items); // 최대 100개 유지 (오래된 것부터 제거) const finalQueue = newQueue.length > SINK_CONFIG.HTTP_MAX_QUEUE_SIZE ? newQueue.slice(-SINK_CONFIG.HTTP_MAX_QUEUE_SIZE) : newQueue; await chrome.storage.local.set({ [STORAGE_KEYS.FAILED_QUEUE]: finalQueue }); }); }
JavaScript
복사
Exponential Backoff Retry (최대 3회):
// SINK_CONFIG HTTP_MAX_RETRY: 3, HTTP_RETRY_BASE_DELAY_MS: 600, // 600ms → 1200ms → 2400ms for (let i = 0; i < SINK_CONFIG.HTTP_MAX_RETRY; i++) { try { const res = await fetchWithTimeout(url, options, HTTP_FETCH_TIMEOUT_MS); // 429, 5xx → 재시도 대상 if (res.status === 429 || res.status >= 500) throw new Error(...); // 4xx (400, 401 등) → Fatal, 재시도 없이 드롭 if (res.status >= 400) { err.isFatal = true; throw err; } return; // 성공 } catch (e) { if (e.isFatal) throw e; const delay = HTTP_RETRY_BASE_DELAY_MS * Math.pow(2, i); // 지수 백오프 await sleep(delay); } }
JavaScript
복사
Chrome Alarm으로 주기적 재전송 (2분마다):
// Service Worker 재시작 후에도 알람이 큐 재전송을 트리거 chrome.alarms.create(ALARM_KEYS.RETRY_HTTP_LOGS, { periodInMinutes: 2 }); chrome.alarms.onAlarm.addListener(async (alarm) => { if (alarm.name === ALARM_KEYS.RETRY_HTTP_LOGS) { await flushFailedQueue(SYSTEM_CONFIG.API_ENDPOINT); } });
JavaScript
복사
큐에 쌓인 이벤트는 만료 시간(HTTP_LOG_EXPIRY_MS = 24시간)이 지나면 자동 드롭됩니다. 동시 접근으로 인한 큐 데이터 충돌은 navigator.locks.request(runWithLock)로 방지했습니다.

5. PoC · 대시보드 포트 충돌

문제 배경

React dev server(npm start)와 PoC 서버(Express) 모두 기본 포트 3000을 사용합니다. 또한 로컬 수집기(collector-local)는 포트 8080을 썼습니다. PoC 데모와 대시보드 개발을 동시에 진행할 때마다 포트 충돌이 발생했고, 확장프로그램의 API 엔드포인트 설정도 AWS와 로컬 사이에서 매번 바꿔야 하는 번거로움이 있었습니다.

해결: config.js 엔드포인트 분리 + Vercel 배포 대시보드 병행

config.js 엔드포인트 토글:
export const SYSTEM_CONFIG = { // 로컬 테스트 시 아래 주석을 해제 // API_ENDPOINT: "http://localhost:8080/events", // DUMPS_ENDPOINT: "http://localhost:8080/dumps", // 운영 (기본값) API_ENDPOINT: "https://bdct33dfx1.execute-api.ap-northeast-2.amazonaws.com/prod/events", DUMPS_ENDPOINT: "https://bdct33dfx1.execute-api.ap-northeast-2.amazonaws.com/prod/dumps", };
JavaScript
복사
로컬 전체 파이프라인 테스트가 필요할 때는 API_ENDPOINTlocalhost:8080으로 변경하고, 일반 PoC 데모 시에는 배포된 PoC 허브(https://https://brs-poc-hub.onrender.com)를 사용해 React dev server 충돌을 우회했습니다. PoC 서버 포트는 .env 또는 PORT 환경변수로 변경 가능하도록 처리해두었습니다.

6. Chrome 확장프로그램 스토어 미등록

문제 배경

프로젝트 종료 시점까지 Chrome 웹스토어 배포를 완료하지 못했습니다. Google Developer 계정 등록 자체는 간단했지만, 스토어 심사에서 "host_permissions": ["<all_urls>"](모든 사이트 접근 권한)에 대한 상세한 정책 설명과 개인정보 처리방침 URL이 요구됐습니다. 또한 심사 기간(보통 1~3주)이 프로젝트 데드라인을 초과했습니다.

현재 상태 및 임시 배포 방법

현재는 Chrome 개발자 모드를 통한 unpacked 로드 방식으로만 사용 가능합니다.
1. chrome://extensions/ 접속 2. "개발자 모드" 토글 ON 3. "압축해제된 확장 프로그램을 로드합니다" 클릭 4. extension-sensor/ 폴더 선택
JavaScript
복사

팀 소개

팀명 소개

저희 팀 이름은 ‘한번 더 생각했나요?(Think Twice?)’ 입니다.
서버 로그가 정상이면 문제없다고 넘어가기 쉽지만,
브라우저 런타임까지 한 번 더 의심해보는 태도가 보안에서 얼마나 중요한지를 이름에 담았습니다.
아무리 서버가 안전해도 사용자 앞의 브라우저에서 일어나는 일을 놓치면 안 된다는 메시지이기도 합니다.

팀 구성

역할
담당
PM / 보안 개발자(팀장)
탐지 로직 및 룰셋 설계, PoC 제작 및 시나리오 설계, 브라우저 기반 공격 분석, 악성 PoC Hub 구축
보안 연구 / 분석 지원
PoC 제작 및 시나리오 설계, JS 주입 공격 차단 기준 설계, PoC 시나리오 안정화, 난독화 스크립트 복호화 구축
크롬 확장프로그램 개발
MV3 기반 확장프로그램 개발, 탐지 엔진 프로토타입 구현, 확장프로그램 팝업 및 경고 팝업 UI 구현
프론트엔드 / 대시보드 개발
React 기반 관제 UI 구현, 이벤트 리스트·통계 차트 및 UX 설계, 대시보드 도메인 구축
백엔드 / 클라우드 개발
API Gateway 및 Lambda 수집 API 구현, 저장소 설계 및 운영 요소 구성, 대시보드 디테일 페이지 설계