이 블로그 게시물에서는 최근에 도입된 문제 탭을 사용하여 콘텐츠 보안 정책 (CSP) 문제를 디버깅하기 위한 DevTools 지원 구현에 대해 설명합니다.
구현 작업은 두 번의 인턴십 과정에서 진행되었습니다. 1. 첫 번째 단계에서는 일반적인 신고 프레임워크를 구축하고 3가지 CSP 위반 문제에 대한 문제 메시지를 설계했습니다. 2. 두 번째 단계에서는 신뢰할 수 있는 유형 디버깅을 위한 몇 가지 특수 DevTools 기능과 함께 신뢰할 수 있는 유형 문제를 추가했습니다.
콘텐츠 보안 정책이란 무엇인가요?
콘텐츠 보안 정책 (CSP)을 사용하면 웹사이트에서 특정 동작을 제한하여 보안을 강화할 수 있습니다. 예를 들어 CSP를 사용하여 인라인 스크립트를 허용하지 않거나 eval를 허용하지 않을 수 있습니다. 두 경우 모두 교차 사이트 스크립팅 (XSS) 공격의 공격 표면을 줄입니다. CSP에 관한 자세한 소개는 여기를 참고하세요.
특히 새로운 CSP는 신뢰할 수 있는 유형(TT) 정책으로, 웹사이트에 대한 대규모 인젝션 공격을 체계적으로 방지할 수 있는 동적 분석을 지원합니다. 이를 위해 TT는 웹사이트가 JavaScript 코드를 감시하여 특정 유형의 항목만 innerHTML과 같은 DOM 싱크에 할당되도록 지원합니다.
웹사이트는 특정 HTTP 헤더를 포함하여 콘텐츠 보안 정책을 활성화할 수 있습니다. 예를 들어 헤더 content-security-policy: require-trusted-types-for 'script'; trusted-types default는 페이지의 TT 정책을 활성화합니다.
각 정책은 다음 모드 중 하나로 작동할 수 있습니다.
- 시행 모드 - 모든 정책 위반이 오류로 처리됨
- 보고 전용 모드 - 오류 메시지를 경고로 보고하지만 웹페이지에서 오류를 일으키지 않습니다.
문제 탭에서 콘텐츠 보안 정책 문제 구현
이 작업의 목표는 CSP 문제의 디버깅 환경을 개선하는 것이었습니다. 새로운 문제를 고려할 때 DevTools팀은 대략적으로 다음 프로세스를 따릅니다.
- 사용자 스토리 정의 웹 개발자가 문제를 조사해야 하는 방식을 다루는 DevTools 프런트엔드의 사용자 스토리 집합을 식별합니다.
- 프런트엔드 구현 사용자 스토리를 기반으로 프런트엔드에서 문제를 조사하는 데 필요한 정보 (예: 관련 요청, 쿠키 이름, 스크립트 또는 HTML 파일의 줄 등)를 식별합니다.
- 문제 감지 Chrome에서 문제를 감지할 수 있는 브라우저 내 위치를 식별하고, 2단계의 관련 정보를 포함하여 문제를 보고할 위치를 계측합니다.
- 문제를 저장하고 표시합니다. 문제를 적절한 위치에 저장하고 DevTools가 열리면 사용할 수 있도록 합니다.
- 문제 텍스트 설계 웹 개발자가 문제를 이해하고, 더 중요하게는 문제를 해결하는 데 도움이 되는 설명 텍스트를 작성합니다.
1단계: CSP 문제에 대한 사용자 스토리 정의
구현 작업을 시작하기 전에 무엇을 해야 하는지 더 잘 이해하기 위해 사용자 스토리가 포함된 설계 문서를 만들었습니다. 예를 들어 다음과 같은 사용자 스토리를 작성했습니다.
웹사이트의 일부가 차단되었다는 사실을 알게 된 개발자로서 다음을 원합니다. - …CSP가 내 웹사이트에서 iframe / 이미지가 차단된 원인인지 확인 - …특정 리소스의 차단을 유발하는 CSP 지시문 알아보기 - …현재 차단된 리소스의 표시 / 현재 차단된 js의 실행을 허용하도록 웹사이트의 CSP를 변경하는 방법 알아보기
이 사용자 스토리를 살펴보기 위해 Google은 관심 있는 CSP 위반을 보여주는 간단한 예시 웹페이지를 만들고 예시 페이지를 살펴보며 프로세스를 직접 익혔습니다. 다음은 웹페이지의 몇 가지 예입니다 (문제 탭이 열린 상태에서 데모 열기).
이 프로세스를 통해 소스 위치가 CSP 문제를 디버깅하는 데 가장 중요한 정보라는 것을 알게 되었습니다. 또한 리소스가 차단된 경우 연결된 iframe과 요청을 빠르게 찾는 것이 유용하며 DevTools의 Elements 패널에 있는 HTML 요소에 대한 직접 링크도 유용할 수 있다는 사실을 발견했습니다.
2단계: 프런트엔드 구현
이러한 정보를 바탕으로 Chrome DevTools 프로토콜 (CDP)을 통해 DevTools에 제공하려는 정보의 첫 번째 초안을 작성했습니다.
다음은 third_party/blink/public/devtools_protocol/browser_protocol.pdl에서 발췌한 내용입니다.
type ContentSecurityPolicyIssueDetails extends object
properties
# The url not included in allowed sources.
optional string blockedURL
# Specific directive that is violated, causing the CSP issue.
string violatedDirective
boolean isReportOnly
ContentSecurityPolicyViolationType contentSecurityPolicyViolationType
optional AffectedFrame frameAncestor
optional SourceCodeLocation sourceCodeLocation
optional DOM.BackendNodeId violatingNodeId
위의 정의는 기본적으로 JSON 데이터 구조를 인코딩합니다. PDL (프로토콜 데이터 언어)이라는 간단한 언어로 작성됩니다. PDL은 두 가지 목적으로 사용됩니다. 먼저 PDL을 사용하여 DevTools 프런트엔드가 사용하는 TypeScript 정의를 생성합니다. 예를 들어 위의 PDL 정의는 다음 TypeScript 인터페이스를 생성합니다.
export interface ContentSecurityPolicyIssueDetails {
/**
* The url not included in allowed sources.
*/
blockedURL?: string;
/**
* Specific directive that is violated, causing the CSP issue.
*/
violatedDirective: string;
isReportOnly: boolean;
contentSecurityPolicyViolationType: ContentSecurityPolicyViolationType;
frameAncestor?: AffectedFrame;
sourceCodeLocation?: SourceCodeLocation;
violatingNodeId?: DOM.BackendNodeId;
}
둘째, 더 중요한 것은 C++ Chromium 백엔드에서 DevTools 프런트엔드로 이러한 데이터 구조를 생성하고 전송하는 것을 처리하는 정의에서 C++ 라이브러리를 생성한다는 것입니다. 이 라이브러리를 사용하면 다음 C++ 코드를 사용하여 ContentSecurityPolicyIssueDetails 객체를 만들 수 있습니다.
protocol::Audits::ContentSecurityPolicyIssueDetails::create()
.setViolatedDirective(d->violated_directive)
.setIsReportOnly(d->is_report_only)
.setContentSecurityPolicyViolationType(BuildViolationType(
d->content_security_policy_violation_type)))
.build();
제공할 정보를 결정한 후에는 Chromium에서 이 정보를 가져올 위치를 찾아야 했습니다.
3단계: 문제 감지
마지막 섹션에 설명된 형식으로 Chrome DevTools 프로토콜 (CDP)에서 정보를 사용할 수 있도록 하려면 백엔드에서 실제로 정보를 사용할 수 있는 위치를 찾아야 했습니다. 다행히 CSP 코드에는 이미 보고서 전용 모드에 사용되는 병목 현상이 있었으며, 여기에 연결할 수 있었습니다. ContentSecurityPolicy::ReportViolation는 CSP HTTP 헤더에서 구성할 수 있는 (선택사항) 보고 엔드포인트에 문제를 보고합니다. 보고하려는 대부분의 정보가 이미 제공되고 있었기 때문에 계측이 작동하기 위해 백엔드에 큰 변경사항이 필요하지 않았습니다.
4단계: 문제 저장 및 표시
한 가지 작은 문제는 콘솔 메시지가 처리되는 방식과 마찬가지로 DevTools가 열리기 전에 발생한 문제도 보고하고 싶었다는 점입니다. 즉, 문제를 프런트엔드에 바로 보고하지 않고 DevTools가 열려 있는지 여부와 관계없이 문제가 채워진 저장소를 사용합니다. DevTools가 열리면 (또는 다른 CDP 클라이언트가 연결되면) 이전에 기록된 모든 문제를 저장소에서 재생할 수 있습니다.
이로써 백엔드 작업이 완료되었으며 이제 프런트엔드에서 문제를 표시하는 방법에 집중해야 했습니다.
5단계: 문제 텍스트 디자인
문제 텍스트를 설계하는 과정에는 Google팀 외에도 여러 팀이 참여합니다. 예를 들어 기능을 구현하는 팀 (이 경우 CSP팀)과 웹 개발자가 특정 유형의 문제를 처리하는 방식을 설계하는 DevRel팀의 인사이트를 활용하는 경우가 많습니다. 문제 텍스트는 일반적으로 완료될 때까지 몇 가지 수정 과정을 거칩니다.
일반적으로 DevTools팀은 다음과 같이 대략적인 초안으로 시작합니다.
## Header
Content Security Policy: include all sources of your resources in content security policy header to improve the functioning of your site
## General information
Even though some sources are included in the content security policy header, some resources accessed by your site like images, stylesheets or scripts originate from sources not included in content security policy directives.
Usage of content from not included sources is restricted to strengthen the security of your entire site.
## Specific information
### VIOLATED DIRECTIVES
`img-src 'self'`
### BLOCKED URLs
https://imgur.com/JuXCo1p.jpg
## Specific information
https://web.dev/strict-csp/
반복을 거쳐 다음과 같은 결과가 나왔습니다.

보시다시피 기능팀과 DevRel을 참여시키면 설명이 훨씬 더 명확하고 정확해집니다.
페이지의 CSP 문제는 CSP 위반 전용 탭에서도 확인할 수 있습니다.
신뢰할 수 있는 유형 문제 디버깅
적절한 개발자 도구가 없으면 대규모로 TT를 사용하는 것이 어려울 수 있습니다.
콘솔 인쇄 개선
신뢰할 수 있는 객체를 사용할 때는 신뢰할 수 없는 객체와 최소한 동일한 양의 정보를 표시하고자 합니다. 현재는 신뢰할 수 있는 객체를 표시할 때 래핑된 객체에 관한 정보가 표시되지 않습니다.
콘솔에 표시되는 값은 기본적으로 객체에서 .valueOf()를 호출하여 가져오기 때문입니다. 하지만 신뢰할 수 있는 유형의 경우 반환된 값이 그다지 유용하지 않습니다. 대신 .toString()를 호출할 때와 유사한 결과를 얻고자 합니다. 이를 위해 V8과 Blink를 수정하여 신뢰할 수 있는 유형 객체에 대한 특수 처리를 도입해야 합니다.
역사적인 이유로 이 맞춤 처리는 V8에서 이루어졌지만 이러한 접근 방식에는 중요한 단점이 있습니다. 맞춤 표시가 필요하지만 JS 수준에서 유형이 동일한 객체가 많습니다. V8은 순수 JS이므로 신뢰할 수 있는 유형과 같은 웹 API에 해당하는 개념을 구분할 수 없습니다. 따라서 V8은 이를 구분하기 위해 삽입기 (Blink)에 도움을 요청해야 합니다.
따라서 코드의 해당 부분을 Blink 또는 삽입 도구로 이동하는 것이 논리적인 선택으로 보입니다. 노출된 문제 외에도 다음과 같은 여러 이점이 있습니다.
- 각 임베더는 자체 설명 생성을 가질 수 있습니다.
- Blink API를 통해 설명을 생성하는 것이 훨씬 쉽습니다.
- Blink는 객체의 원래 정의에 액세스할 수 있습니다. 따라서
.toString()를 사용하여 설명을 생성하면.toString()가 재정의될 위험이 없습니다.
위반 시 중단 (보고 전용 모드)
현재 TT 위반을 디버깅하는 유일한 방법은 JS 예외에 중단점을 설정하는 것입니다. 강제된 TT 위반은 예외를 트리거하므로 이 기능은 어떤 면에서 유용할 수 있습니다. 하지만 실제 시나리오에서는 TT 위반을 더 세밀하게 제어해야 합니다. 특히 다른 예외가 아닌 TT 위반에 대해서만 중단하고, 보고서 전용 모드에서도 중단하며, 다양한 유형의 TT 위반을 구분하고자 합니다.
DevTools는 이미 다양한 중단점을 지원하므로 아키텍처를 확장할 수 있습니다. 새 중단점 유형을 추가하려면 백엔드 (Blink), CDP, 프런트엔드를 변경해야 합니다.
새로운 CDP 명령어를 도입해야 합니다. 이를 setBreakOnTTViolation라고 부르겠습니다. 이 명령어는 프런트엔드에서 백엔드에 중단해야 하는 TT 위반 유형을 알리는 데 사용됩니다. 백엔드, 특히 InspectorDOMDebuggerAgent는 TT 위반이 발생할 때마다 호출되는 '프로브' onTTViolation()를 제공합니다. 그러면 InspectorDOMDebuggerAgent는 해당 위반이 중단점을 트리거해야 하는지 확인하고, 그렇다면 프런트엔드에 메시지를 전송하여 실행을 일시중지합니다.
완료된 작업과 다음 단계
여기에 설명된 문제가 도입된 이후 문제 탭에 몇 가지 변경사항이 적용되었습니다.
- DevTools의 다른 패널과의 상호 연결성이 개선되었습니다.
- 낮은 대비, 신뢰할 수 있는 웹 활동, 쿼크 모드, Attribution Reporting API, CORS 관련 문제 등 여러 추가 문제의 보고가 문제 탭으로 이동했습니다.
- 문제를 숨길 수 있는 기능이 도입되었습니다.
앞으로는 문제 탭을 사용하여 더 많은 문제를 표시할 계획이며, 이를 통해 장기적으로는 읽을 수 없는 오류 메시지 흐름을 콘솔에서 언로드할 수 있게 될 것입니다.
미리보기 채널 다운로드
Chrome Canary, Dev 또는 베타를 기본 개발 브라우저로 사용하는 것이 좋습니다. 미리보기 채널을 사용하면 최신 DevTools 기능에 액세스하고, 최신 웹 플랫폼 API를 테스트하고, 사용자가 문제를 발견하기 전에 사이트에서 문제를 찾을 수 있습니다.
Chrome DevTools팀에 문의하기
다음 옵션을 사용하여 DevTools와 관련된 새로운 기능, 업데이트 또는 기타 사항에 관해 논의하세요.
- crbug.com에서 의견 및 기능 요청을 제출합니다.
- DevTools에서 옵션 더보기 > 도움말 > DevTools 문제 신고를 사용하여 DevTools 문제를 알립니다.
- @ChromeDevTools에 트윗합니다.
- 'DevTools의 새로운 기능' YouTube 동영상 또는 'DevTools 팁' YouTube 동영상에 댓글을 남깁니다.