웹 개발자를 위한 사이트 격리

데스크톱용 Chrome 67에는 사이트 격리라는 새로운 기능이 기본적으로 사용 설정되어 있습니다. 이 도움말에서는 사이트 격리의 정의, 필요성, 웹 개발자가 사이트 격리를 알아야 하는 이유를 설명합니다.

사이트 격리란 무엇인가요?

인터넷은 고양이 동영상을 시청하고 암호화폐 지갑을 관리하는 등 다양한 용도로 사용되지만 fluffycats.example가 소중한 암호화폐에 액세스할 수 있게 해서는 안 됩니다. 다행히 웹사이트는 일반적으로 동일 출처 정책 덕분에 브라우저 내에서 서로의 데이터에 액세스할 수 없습니다. 하지만 악성 웹사이트가 이 정책을 우회하여 다른 웹사이트를 공격하려고 시도할 수 있으며, 동일 출처 정책을 적용하는 브라우저 코드에서 보안 버그가 발견되는 경우도 있습니다. Chrome팀은 이러한 버그를 최대한 빨리 수정하는 것을 목표로 합니다.

사이트 격리는 이러한 공격이 성공할 가능성을 줄이는 추가 방어선을 제공하는 Chrome의 보안 기능입니다. 이를 통해 여러 웹사이트의 페이지가 항상 서로 다른 프로세스에 배치되며, 각 프로세스는 프로세스에서 수행할 수 있는 작업을 제한하는 샌드박스에서 실행됩니다. 또한 프로세스가 다른 사이트에서 특정 유형의 민감한 정보를 수신하지 못하도록 차단합니다. 따라서 사이트 격리를 사용하면 악성 웹사이트가 스펙터와 같은 예측 사이드 채널 공격을 사용하여 다른 사이트에서 데이터를 훔치는 것이 훨씬 더 어려워집니다. Chrome팀에서 추가 시정 조치를 완료하면 공격자의 페이지가 자체 프로세스에서 일부 규칙을 위반할 수 있는 경우에도 사이트 격리가 도움이 됩니다.

사이트 격리를 사용하면 신뢰할 수 없는 웹사이트가 다른 웹사이트의 계정에 액세스하거나 정보를 도용하는 것이 더 어려워집니다. 최근의 멜트다운 및 스펙터 측면 채널 공격과 같은 다양한 유형의 보안 버그에 대한 추가 보호 기능을 제공합니다.

사이트 격리에 관한 자세한 내용은 Google 보안 블로그의 도움말을 참고하세요.

교차 출처 읽기 차단

모든 크로스 사이트 페이지가 별도의 프로세스에 배치되더라도 페이지는 이미지, JavaScript와 같은 일부 크로스 사이트 하위 리소스를 합법적으로 요청할 수 있습니다. 악의적인 웹페이지는 <img> 요소를 사용하여 은행 잔액과 같은 민감한 정보가 포함된 JSON 파일을 로드할 수 있습니다.

<img src="https://your-bank.example/balance.json" />
<!-- Note: the attacker refused to add an `alt` attribute, for extra evil points. -->

사이트 격리가 없으면 JSON 파일의 콘텐츠가 렌더러 프로세스의 메모리에 저장되며 이때 렌더러는 유효한 이미지 형식이 아니라는 것을 감지하고 이미지를 렌더링하지 않습니다. 하지만 공격자는 Spectre와 같은 취약점을 악용해 메모리 청크를 읽을 수 있습니다.

공격자는 <img> 대신 <script>를 사용하여 민감한 정보를 메모리에 커밋할 수도 있습니다.

<script src="https://your-bank.example/balance.json"></script>

교차 출처 읽기 차단(CORB)은 balance.json의 콘텐츠가 MIME 유형에 따라 렌더러 프로세스 메모리의 메모리에 들어가지 못하도록 하는 새로운 보안 기능입니다.

CORB의 작동 방식을 알아보겠습니다. 웹사이트는 서버에 두 가지 유형의 리소스를 요청할 수 있습니다.

  1. HTML, XML, JSON 문서와 같은 데이터 리소스
  2. 이미지, JavaScript, CSS, 글꼴과 같은 미디어 리소스

웹사이트는 자체 출처 또는 허용 CORS 헤더(예: Access-Control-Allow-Origin: *)가 있는 다른 출처에서 데이터 리소스를 수신할 수 있습니다. 반면 미디어 리소스는 허용 CORS 헤더가 없어도 모든 출처에서 포함할 수 있습니다.

다음과 같은 경우 CORB는 렌더러 프로세스가 교차 출처 데이터 리소스 (예: HTML, XML 또는 JSON)를 수신하지 못하도록 합니다.

  • 리소스에 X-Content-Type-Options: nosniff 헤더가 있음
  • CORS에서 리소스에 대한 액세스를 명시적으로 허용하지 않음

교차 출처 데이터 리소스에 X-Content-Type-Options: nosniff 헤더가 설정되어 있지 않으면 CORB은 응답 본문을 스니핑하여 HTML, XML 또는 JSON인지 확인하려고 시도합니다. 이는 일부 웹 서버가 잘못 구성되어 이미지를 text/html로 제공하기 때문에 필요합니다.

CORB 정책에 의해 차단된 데이터 리소스는 프로세스에 빈 것으로 표시되지만 요청은 백그라운드에서 계속 실행됩니다. 따라서 악성 웹페이지는 교차 사이트 데이터를 프로세스로 가져와 훔치기가 어렵습니다.

최적의 보안을 유지하고 CORB의 이점을 누리려면 다음을 권장합니다.

  • 올바른 Content-Type 헤더로 응답을 표시합니다. 예를 들어 HTML 리소스는 text/html로, JSON 리소스는 JSON MIME 유형으로, XML 리소스는 XML MIME 유형으로 제공해야 합니다.
  • X-Content-Type-Options: nosniff 헤더를 사용하여 스니핑을 선택 해제합니다. 이 헤더가 없으면 Chrome은 유형이 올바른지 확인하기 위해 빠른 콘텐츠 분석을 실행하지만, JavaScript 파일과 같은 항목을 차단하지 않기 위해 응답을 허용하는 쪽으로 오류가 발생하므로 올바른 작업을 직접 실행하는 것이 좋습니다.

자세한 내용은 웹 개발자를 위한 CORB 도움말 또는 심층 CORB 설명을 참고하세요.

웹 개발자가 사이트 격리에 관심을 가져야 하는 이유는 무엇인가요?

대부분의 경우 사이트 격리는 웹 개발자에게 직접 노출되지 않는 백엔드 브라우저 기능입니다. 예를 들어 새로운 웹 노출 API는 없습니다. 일반적으로 웹 페이지는 사이트 격리 유무와 관계없이 실행 시 차이를 구분할 수 없습니다.

하지만 이 규칙에는 몇 가지 예외가 있습니다. 사이트 격리를 사용 설정하면 웹사이트에 영향을 줄 수 있는 몇 가지 미묘한 부작용이 있습니다. Google에서는 알려진 사이트 격리 문제 목록을 유지하며 아래에서 가장 중요한 문제를 자세히 설명합니다.

전체 페이지 레이아웃이 더 이상 동기식 레이아웃이 아님

사이트 격리를 사용하면 페이지 프레임이 여러 프로세스에 걸쳐 있을 수 있으므로 전체 페이지 레이아웃이 더 이상 동기식일 수 없습니다. 레이아웃 변경사항이 페이지의 모든 프레임에 즉시 전파된다고 가정하는 페이지에 영향을 미칠 수 있습니다.

예를 들어 social-widget.example에 호스팅된 소셜 위젯과 통신하는 fluffykittens.example라는 웹사이트를 생각해 보겠습니다.

<!-- https://fluffykittens.example/ -->
<iframe src="https://social-widget.example/" width="123"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  iframe.width = 456;
  iframe.contentWindow.postMessage(
    // The message to send:
    'Meow!',
    // The target origin:
    'https://social-widget.example'
  );
</script>

처음에는 소셜 위젯의 <iframe> 너비가 123픽셀입니다. 하지만 FluffyKittens 페이지에서 너비를 456픽셀로 변경하고 (레이아웃 트리거) 다음 코드가 포함된 소셜 위젯에 메시지를 전송합니다.

<!-- https://social-widget.example/ -->
<script>
  self.onmessage = () => {
    console.log(document.documentElement.clientWidth);
  };
</script>

소셜 위젯이 postMessage API를 통해 메시지를 수신할 때마다 루트 <html> 요소의 너비를 로깅합니다.

어떤 너비 값이 로깅되나요? Chrome에서 사이트 격리를 사용 설정하기 전에는 답변이 456였습니다. document.documentElement.clientWidth에 액세스하면 Chrome에서 사이트 격리를 사용 설정하기 전에 동기식이었으나 이제는 비동기식인 레이아웃이 강제됩니다. 그러나 사이트 격리를 사용 설정하면 이제 교차 출처 소셜 위젯 재레이아웃이 별도의 프로세스에서 비동기식으로 실행됩니다. 따라서 이제 답변은 123(즉, 이전 width 값)일 수도 있습니다.

페이지가 교차 출처 <iframe>의 크기를 변경한 후 postMessage를 전송하는 경우 사이트 격리를 사용하면 수신 프레임이 메시지를 수신할 때 아직 새 크기를 알지 못할 수 있습니다. 더 일반적으로 레이아웃 변경사항이 페이지의 모든 프레임에 즉시 전파된다고 가정하면 페이지가 손상될 수 있습니다.

이 특정 예에서 더 강력한 솔루션은 상위 프레임에서 width를 설정하고 resize 이벤트를 리슨하여 <iframe>에서 변경사항을 감지하는 것입니다.

로드 취소 핸들러의 시간 초과가 더 자주 발생할 수 있습니다.

프레임이 탐색되거나 닫히면 이전 문서와 그 안에 삽입된 모든 하위 프레임 문서가 모두 unload 핸들러를 실행합니다. 새 탐색이 동일한 렌더러 프로세스에서 발생하는 경우 (예: 동일한 출처 탐색) 새 탐색을 커밋하기 전에 이전 문서 및 하위 프레임의 unload 핸들러가 임의로 긴 시간 동안 실행될 수 있습니다.

addEventListener('unload', () => {
  doSomethingThatMightTakeALongTime();
});

이 경우 모든 프레임의 unload 핸들러는 매우 안정적입니다.

그러나 사이트 격리가 없어도 일부 기본 프레임 탐색은 교차 프로세스이므로 언로드 핸들러 동작에 영향을 미칩니다. 예를 들어 주소 표시줄에 URL을 입력하여 old.example에서 new.example로 이동하면 새 프로세스에서 new.example 탐색이 발생합니다. old.example 및 하위 프레임의 언로드 핸들러는 new.example 페이지가 표시된 후 백그라운드의 old.example 프로세스에서 실행되며 특정 제한 시간 내에 완료되지 않으면 이전 언로드 핸들러가 종료됩니다. 언로드 핸들러가 제한 시간 전에 완료되지 않을 수 있으므로 언로드 동작의 안정성이 떨어집니다.

사이트 격리를 사용하면 모든 교차 사이트 탐색이 교차 프로세스가 되므로 서로 다른 사이트의 문서가 프로세스를 공유하지 않습니다. 따라서 위의 상황이 더 많은 경우에 적용되며 <iframe>의 언로드 핸들러에는 위에 설명된 백그라운드 및 시간 제한 동작이 있는 경우가 많습니다.

사이트 격리로 인한 또 다른 차이점은 새로운 병렬로 정렬된 언로드 핸들러입니다. 사이트 격리가 없으면 언로드 핸들러가 프레임 전체에서 엄격한 위에서 아래로 순서대로 실행됩니다. 그러나 사이트 격리를 사용하면 언로드 핸들러가 여러 프로세스에서 동시에 실행됩니다.

이는 사이트 격리를 사용 설정하면 발생하는 기본적인 결과입니다. Chrome팀은 가능한 경우 일반적인 사용 사례에 관한 언로드 핸들러의 안정성을 개선하기 위해 노력하고 있습니다. 하위 프레임 언로드 핸들러가 아직 특정 기능을 활용할 수 없는 버그도 알고 있으며 이를 해결하기 위해 노력하고 있습니다.

언로드 핸들러의 중요한 사례는 세션 종료 핑을 전송하는 것입니다. 일반적으로 다음과 같이 실행됩니다.

addEventListener('pagehide', () => {
  const image = new Image();
  img.src = '/end-of-session';
});

이 변경사항을 고려할 때 더 강력한 접근 방식은 대신 navigator.sendBeacon를 사용하는 것입니다.

addEventListener('pagehide', () => {
  navigator.sendBeacon('/end-of-session');
});

요청을 더 세부적으로 제어해야 하는 경우 Fetch API의 keepalive 옵션을 사용할 수 있습니다.

addEventListener('pagehide', () => {
  fetch('/end-of-session', {keepalive: true});
});

결론

사이트 격리를 사용하면 각 사이트를 자체 프로세스로 격리하여 신뢰할 수 없는 웹사이트가 다른 웹사이트의 계정에 액세스하거나 정보를 도용하는 것을 방지할 수 있습니다. 그 일환으로 CORB는 민감한 정보 리소스를 렌더러 프로세스 외부에 유지하려고 합니다. 위의 권장사항을 따르면 이러한 새로운 보안 기능을 최대한 활용할 수 있습니다.

이 도움말의 초안을 읽고 의견을 제공해 주신 Alex Moshchuk, Charlie Reis, Jason Miller, Nasko Oskov, Philip Walton, Shubhie Panicker, Thomas Steiner님께 감사드립니다.