브라우저 확장 보안 감사 — isTrusted · CSV 인젝션 · URL 스킴

보안3분조회

개인적으로 만들어 쓰는 브라우저 확장이 하나 있습니다. 웹페이지의 단어나 자막을 번역·요약해 주는 확장인데, 그 번역·요약을 AWS Bedrock(여러 LLM 을 API 로 부르는 AWS 서비스)에 맡깁니다. 특이한 건 서버를 따로 두지 않고, 사용자 브라우저에서 Bedrock 을 직접 부르는 구조라는 점입니다.

서버를 안 두니 편한 대신, 확장이 사용자가 연 웹페이지 안에서 같이 돌기 때문에 그 페이지가 악성이면 확장을 악용하는 경로가 생깁니다. 그래서 배포 전에 한 번 전수로 훑었고, 네 가지를 막았습니다.

1. 합성 이벤트로 비용을 유발하는 것 — isTrusted

넷 중에 제일 위험했던 게 이겁니다. 확장은 사용자가 단어를 클릭하거나 번역 버튼을 누르면 Bedrock 을 부르는데, 확장이 삽입된 페이지가 가짜 이벤트를 합성해서 그 핸들러를 대신 부를 수 있습니다.

// 악성 페이지가 이렇게 하면
element.dispatchEvent(new MouseEvent('click', { bubbles: true }))

사람이 안 눌러도 클릭 이벤트가 발생하고, 확장이 그걸 진짜로 알고 Bedrock 을 호출합니다. 악성 페이지가 이걸 반복하면 사용자 계정으로 비용이 발생합니다.

브라우저 이벤트에는 isTrusted 라는 플래그가 있습니다. 사용자의 실제 행동으로 발생한 이벤트는 true, 코드가 합성한 이벤트는 false 입니다. 모든 사용자 입력 핸들러 앞에 이 검사를 걸었습니다.

function onWordClick(e) {
  if (!e.isTrusted) return   // 합성 이벤트 차단
  // ... 실제 Bedrock 호출
}

이제 페이지가 아무리 클릭을 합성해도 확장이 무시합니다. 비용은 사람이 실제로 누를 때만 발생합니다.

2. 단어장 CSV 인젝션

단어장을 CSV 로 내보내는 기능이 있었습니다. CSV 인젝션(수식 인젝션)은, 셀 값이 =, +, -, @ 로 시작하면 엑셀·스프레드시트가 그걸 수식으로 실행하는 공격입니다.

사용자가 저장한 단어에 =cmd|... 같은 값이 들어 있으면, 그 CSV 를 엑셀로 열 때 수식이 실행될 수 있습니다. 사용자가 직접 넣은 단어라도, 웹페이지에서 자동으로 담기는 경로가 있으면 악성 값이 섞일 수 있습니다.

셀 값이 그 문자로 시작하면 앞에 작은따옴표나 공백을 붙여 수식으로 해석되지 않게 막았습니다.

3. 출처 링크의 URL 스킴

번역 카드에 원문 출처 링크를 넣는데, 그 URL 이 외부에서 들어올 수 있었습니다. javascript: 스킴 링크가 들어오면 클릭 시 스크립트가 실행됩니다.

링크의 스킴을 http/https 로만 제한했습니다. 그 외 스킴(javascript:, data: 등)은 링크로 만들지 않습니다.

4. 사용자 값이 들어가는 숫자 가드

영상 요약의 챕터에서 “그 시각으로 이동” 하는 기능이 있는데, 이동할 시각(seek 값)이 모델 출력에서 왔습니다. 그 값이 숫자가 맞는지, 영상 길이 범위 안인지 가드를 걸었습니다. 외부에서 온 값을 그대로 API 에 넘기지 않는 원칙입니다.

확장의 위협 모델 — 삽입된 페이지가 공격자

확장의 위협 모델이 일반 웹앱과 다른 지점은 확장이 삽입된 페이지가 곧 잠재적 공격자라는 데 있습니다. 페이지는 확장이 심은 요소를 건드리고, 이벤트를 합성하고, 확장이 읽는 값을 조작할 수 있습니다.

그래서 확장에서 “사용자 입력” 을 받는 모든 지점은, 그게 진짜 사용자에게서 왔는지(isTrusted), 값이 안전한 범위인지, 밖으로 내보낼 때 실행 가능한 형태가 되진 않는지(CSV 수식·javascript: 스킴)를 봐야 했습니다.

특히 확장이 돈이 드는 API 를 직접 부르는 경우, 합성 이벤트로 비용을 유발하는 경로는 꼭 막아야 합니다. isTrusted 한 줄이 그걸 막습니다.

  1. 불러오는 중