보안
문제
Claude Code가 만든 HTML을 미리보기로 띄우거나, PR을 대신 만들거나, 프로덕션에 닿는 CLI를 대신 호출하게 시키는 경우가 있습니다. 이런 자리에서는 편의 기능 하나가 격리를 통째로 무력하게 만듭니다.
추천
세 가지를 지킵니다. LLM이 만든 HTML을 iframe에 넣을 때 sandbox="allow-scripts"에
allow-same-origin을 같이 주지 않습니다. CSP는 <!doctype> 바로 뒤에 넣습니다. 셸 명령에는
사용자 입력·브랜치명을 리터럴로 넣지 말고 파일로 넘깁니다. 이 셋을 다 지켜도
조직 정책이 도구 능력보다 우선한다는 것을 설계 전제로 둡니다.
이유
allow-scripts와 allow-same-origin을 같이 주면 sandbox가 무력화됩니다. LLM이 생성한
HTML을 프리뷰할 때 방어선은 iframe sandbox와, 시스템이 srcdoc 사본에 주입하는 CSP
둘입니다. allow-scripts만 주면 iframe 안 스크립트는 실행되지만 iframe은 여전히 opaque
origin이라 부모 페이지에 접근하지 못합니다. 여기에 allow-same-origin을 같이 주면
iframe이 부모 origin 권한을 얻어 sandbox의 격리가 사라집니다. CSP 문자열 자체도 LLM이
만들게 두지 않습니다. 시스템이 주입합니다.
CSP meta의 삽입 위치가 보호 여부를 가릅니다. <!doctype> 직후(없으면 <html> 직후)에
넣어야 파서가 이것을 암묵적 <head>의 첫 자식으로 넣어 가장 먼저 적용합니다. <head>
뒤에 넣으면 그 앞에 있는 <script>가 CSP 적용 전에 이미 실행됩니다. 런타임 네트워크
차단의 핵심은 connect-src 'none'입니다. 정적 검사는 동적으로 조립되는 URL을 다 잡지
못하므로 실제 차단선은 CSP입니다.
opaque origin iframe에서는 앱 오리진 리소스에 로그인 세션이 붙는다고 가정할 수 없습니다.
site-for-cookies가 null이 되어 SameSite=Lax 세션 쿠키가 실리지 않습니다. 인증 미들웨어가
307로 리다이렉트하면 그 로그인 HTML이 <script>로 그대로 실행됩니다. 이 인증 실패는
다른 종류의 실패처럼 보일 수 있습니다. 생성된 HTML을 text/html로 그대로 흘리는 API는
iframe이 아니라 직접 네비게이션으로도 열릴 수 있어 sandbox·CSP를 통째로 우회합니다.
이런 응답에는 Content-Security-Policy: sandbox와 X-Content-Type-Options: nosniff를
답니다.
PR 본문·브랜치명을 명령 문자열에 리터럴로 넣으면 셸 인젝션이 됩니다. 사용자 입력이나
브랜치명을 셸 명령 문자열에 직접 꽂지 않고, PR 본문은 --body-file로 파일을 통해
넘깁니다. 커밋 메시지나 PR 본문에 백틱이나 $()가 섞여 들어오면 문자열 보간 방식은 그대로
명령 실행이 됩니다.
조직 정책이 도구 능력보다 우선합니다. 어떤 MCP 도구가 기술적으로 할 수 있는 동작이라도,
조직이 그 동작(예: 파일 전체 공개, 외부 공유 링크 생성)을 정책으로 금지하면 스코프를
다시 발급받아도 정책이 막습니다. 이때는 워크플로를 바꿉니다. 에이전트는 스캐폴드까지만
하고, 실제 게시·배포처럼 조직 정책이 걸리는 단계는 사람이 합니다. MCP 자격증명(.env)을
로그·커밋·문서 어디에도 남기지 않는다는 원칙도 같은 이유입니다.
예시
iframe sandbox에서 절대 같이 주면 안 되는 조합입니다.
<!-- 위험: iframe이 부모 origin에 접근할 수 있게 된다 --><iframe sandbox="allow-scripts allow-same-origin" srcdoc={html} />
<!-- 안전: 스크립트는 돌지만 부모 origin 접근은 없다 --><iframe sandbox="allow-scripts" srcdoc={html} />CSP는 문서의 맨 앞에 옵니다.
<!doctype html><meta http-equiv="Content-Security-Policy" content="default-src 'none'; connect-src 'none'; script-src 'self'"><html> ...PR 생성은 사용자 입력을 명령 문자열에 넣지 않습니다.
# 위험 — 브랜치명·제목에 백틱이나 $()가 섞이면 그대로 실행된다gh pr create --title "$TITLE" --body "$BODY"
# 안전 — 본문은 파일로 넘긴다printf '%s' "$BODY" > /tmp/pr-body.mdgh pr create --title "$TITLE" --body-file /tmp/pr-body.md하지 말 것
allow-scripts와 allow-same-origin을 함께 주기. 둘 중 하나만 있으면 안전한 조합이
같이 있으면 sandbox 자체가 무력화됩니다.
CSP 문자열을 LLM에게 생성시키기. 검증 없이 신뢰할 수 없는 산출물이 보안 경계 자체를 정의하게 됩니다.
사용자 입력·브랜치명을 셸 명령 문자열에 직접 보간하기. PR 본문·커밋 메시지·브랜치명은 항상 파일이나 별도 인자로 넘깁니다.
MCP나 스코프를 재발급받아 조직 정책을 우회하려 하기. 정책이 도구 레벨이 아니라 조직 레벨에서 걸려 있으면 스코프를 늘려도 소용없습니다. 사람이 개입하는 단계로 워크플로를 다시 설계합니다.