Chrome 확장 프로그램에서 평가 사용

Chrome의 확장 프로그램 시스템은 상당히 엄격한 기본 콘텐츠 보안 정책 (CSP)을 적용합니다. 정책 제한은 간단합니다. 스크립트가 다른 자바스크립트 파일로 이동되어야 하고, 인라인 이벤트 핸들러는 addEventListener을 사용하도록 변환되어야 하며, eval()는 사용 중지됩니다. Chrome 앱에는 더 엄격한 정책이 적용되며 이러한 정책이 제공하는 보안 속성에 만족합니다.

그러나 다양한 라이브러리가 성능 최적화와 표현의 용이성을 위해 eval()eval와 유사한 구성(예: new Function())을 사용한다는 것을 알고 있습니다. 템플릿 라이브러리는 특히 이러한 스타일의 구현에 취약합니다. 일부 (예: Angular.js)는 기본적으로 CSP를 지원하지만, 많은 인기 있는 프레임워크는 아직 확장 프로그램의 eval가 없는 환경과 호환되는 메커니즘으로 업데이트되지 않았습니다. 따라서 이 기능의 지원을 중단하는 것은 개발자에게 예상보다 더 큰 문제가 되는 것으로 입증되었습니다.

이 문서에서는 보안을 손상하지 않고 프로젝트에 이러한 라이브러리를 포함하는 안전한 메커니즘으로 샌드박스를 소개합니다. 간결성을 위해 확장이라는 용어를 끝까지 사용하겠지만 개념은 애플리케이션에 동일하게 적용됩니다.

샌드박스를 사용해야 하는 이유

eval은 확장 프로그램 내에서 위험합니다. 실행되는 코드가 확장 프로그램의 높은 권한 환경에 있는 모든 항목에 액세스할 수 있기 때문입니다. 사용자의 보안 및 개인 정보 보호에 심각한 영향을 미칠 수 있는 강력한 chrome.* API가 다양하게 마련되어 있습니다. 단순한 데이터 무단 반출이 가장 우려됩니다. 제공되는 솔루션은 eval이 확장 프로그램의 데이터 또는 확장 프로그램의 가치가 높은 API에 액세스하지 않고도 코드를 실행할 수 있는 샌드박스입니다. 데이터, API, 문제 없음

이를 위해 확장 프로그램 패키지 내의 특정 HTML 파일을 샌드박스 처리된 것으로 나열합니다. 샌드박스 처리된 페이지는 로드될 때마다 고유한 원본으로 이동하며 chrome.* API에 대한 액세스가 거부됩니다. iframe를 통해 샌드박스 처리된 페이지를 확장 프로그램에 로드하면 이 페이지에 메시지를 전달하고, 이러한 메시지에 대해 어떤 방식으로든 조치를 취하고, 결과가 반환될 때까지 기다릴 수 있습니다. 이 간단한 메시지 메커니즘을 통해 확장 프로그램의 워크플로에 eval 기반 코드를 안전하게 포함하는 데 필요한 모든 것을 얻을 수 있습니다.

샌드박스 생성 및 사용

코드를 바로 살펴보고 싶다면 샌드박스 샘플 확장 프로그램을 가져와 시작해 보세요. Handlebars 템플릿 라이브러리를 기반으로 빌드된 작은 메시지 API의 실제로 작동하는 예이며 시작하는 데 필요한 모든 것을 제공합니다. 좀 더 자세한 설명을 원하는 분들을 위해 여기에 있는 샘플을 함께 살펴보겠습니다.

매니페스트에 파일 나열

샌드박스 내에서 실행해야 하는 각 파일은 sandbox 속성을 추가하여 확장 프로그램 매니페스트에 나열해야 합니다. 이 단계는 중요한 단계이며 잊어버리기 쉬우므로 샌드박스 처리된 파일이 매니페스트에 나열되어 있는지 다시 한번 확인하세요. 이 샘플에서는 'sandbox.html'이라는 멋진 파일을 샌드박스로 만듭니다. 매니페스트 항목은 다음과 같습니다.

{
  ...,
  "sandbox": {
     "pages": ["sandbox.html"]
  },
  ...
}

샌드박스 처리된 파일 로드

샌드박스 처리된 파일에서 흥미로운 작업을 하려면 확장 프로그램의 코드로 처리할 수 있는 컨텍스트에서 파일을 로드해야 합니다. 여기서 sandbox.htmliframe을 통해 확장 프로그램의 이벤트 페이지 (eventpage.html)에 로드되었습니다. eventpage.js에는 페이지에서 iframe를 찾고 contentWindow에서 postMessage 메서드를 실행하여 브라우저 작업을 클릭할 때마다 샌드박스로 메시지를 전송하는 코드가 포함되어 있습니다. 이 메시지는 contextcommand, 이렇게 두 가지 속성을 포함하는 객체입니다. 두 가지 모두에 대해서는 잠시 후에 살펴보겠습니다.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
postMessage API에 관한 일반적인 정보는 MDN에 관한 postMessage 문서 를 참고하세요. 읽어볼 만한 자료입니다. 특히 데이터는 직렬화할 수 있는 경우에만 앞뒤로 전달될 수 있습니다. 예를 들어 함수는 그렇지 않습니다.

위험한 행동

sandbox.html가 로드되면 Handlebars 라이브러리가 로드되고 Handlebars에서 제안하는 방식으로 인라인 템플릿을 만들고 컴파일합니다.

<script src="handlebars-1.0.0.beta.6.js"></script>
<script id="hello-world-template" type="text/x-handlebars-template">
  <div class="entry">
    <h1>Hello, !</h1>
  </div>
</script>
<script>
  var templates = [];
  var source = document.getElementById('hello-world-template').innerHTML;
  templates['hello'] = Handlebars.compile(source);
</script>

이것은 실패하지 않습니다. Handlebars.compilenew Function를 사용하게 되더라도 작업은 정확히 예상대로 작동하고 templates['hello']에 컴파일된 템플릿이 생성됩니다.

결과 전달

이벤트 페이지의 명령어를 수신하는 메시지 리스너를 설정하여 이 템플릿을 사용할 수 있도록 합니다. 전달된 command를 사용하여 해야 할 작업을 결정합니다. 단순한 렌더링 이상의 작업을 상상할 수도 있습니다. 템플릿을 만들거나 어떤 방식으로든 관리하나요?) context는 렌더링을 위해 템플릿에 직접 전달됩니다. 렌더링된 HTML은 이벤트 페이지로 다시 전달되므로 확장 프로그램에서 나중에 유용한 작업을 할 수 있습니다.

<script>
  window.addEventListener('message', function(event) {
    var command = event.data.command;
    var name = event.data.name || 'hello';
    switch(command) {
      case 'render':
        event.source.postMessage({
          name: name,
          html: templates[name](event.data.context)
        }, event.origin);
        break;

      // case 'somethingElse':
      //   ...
    }
  });
</script>

이벤트 페이지로 돌아가서, 이 메시지를 받으면 전달받은 html 데이터를 이용해 흥미로운 작업을 할 수 있습니다. 이 경우 데스크톱 알림을 통해 출력하지만 이 HTML을 확장 프로그램 UI의 일부로 안전하게 사용할 수 있습니다. innerHTML를 통해 이를 삽입해도 상당한 보안 위험이 발생하지 않습니다. 약간의 공격을 통해 샌드박스 처리된 코드를 완전히 손상하더라도 위험한 스크립트 또는 플러그인 콘텐츠를 높은 권한의 확장 프로그램 컨텍스트에 삽입할 수 없기 때문입니다.

이 메커니즘으로 템플릿을 쉽게 만들 수 있지만 물론 템플릿만으로 제한되지는 않습니다. 엄격한 콘텐츠 보안 정책에 따라 즉시 작동하지 않는 코드는 샌드박스 처리될 수 있습니다. 실제로 올바르게 실행되는 데 필요한 최소한의 권한으로 프로그램의 각 부분을 제한하기 위해 올바르게 실행되는 확장 프로그램의 샌드박스 구성요소를 사용하는 것이 유용한 경우가 많습니다. Google I/O 2012의 보안 웹 앱 및 Chrome 확장 프로그램 작성 프레젠테이션은 이러한 기술이 실제로 작동하는 예를 보여주며, 56분만 시간을 할애해 주시기 바랍니다.