전 세계적으로 오염된 JavaScript 코드 추적

다음은 “Find what JavaScript variables are leaking into the global scope”입니다. 자바스크립트 프로그램의 전역 오염 문제를 해결하는 데 도움을 주는 또 다른 게시물입니다.
이전 기사에서 JavaScript 코드를 통해 전역 범위에 추가된 변수의 이름을 발견할 수 있는 기술을 배웠습니다.일반적으로 글로벌 변수 이름만 알고 있으면 1) 변수가 글로벌 범위에 존재할 수 있는지 여부와 그렇지 않으면 2) 글로벌 범위의 JavaScript 코드 행에 추가할 수 있는지 여부를 결정합니다.
그럼에도 불구하고 전역 변수를 만드는 자바스크립트 코드를 추적하는 것은 그리 간단하지 않다. 예를 들어 전역 변수의 이름이 매우 통용될 때 (예: item, x, 전역 변수를 만드는 코드가 자바스크립트 프로그램의 라이버 트리에 깊이 들어갈 때.
다음은 코드 내부에서 발생하는 전체 위치를 디버깅하는 데 도움을 줄 자바스크립트 유틸리티를 만드는 방법입니다.

Here’s a real-world example where this utility has helped me track-down a global variable leak coming from third-party JavaScript code.



전 세계 오염의 예
예를 들어, 이전 기사에서 공유한 HTML 문서에 대해 다시 한 번 살펴보겠습니다.
<html>
  <body>
    <h1>Hello world!</h1>
    <script src="https://unpkg.com/[email protected]/dist/jquery.js"></script>
    <script>
      function doSomethingTwice() {
        for (i = 0; i <= 2; i++) {
          const myString = `hello-world-${i}`;
          // Let's imagine we're going to do something with myString here...
        }
      }
      doSomethingTwice();
    </script>
  </body>
</html>
페이지의 두 스크립트 (jquery.js 와 내연 스크립트 $ 는 네 개의 서로 다른 전역 변수를 추가했습니다. jQueryjquery.js, 그리고 doSomethingTwicei 내연 스크립트입니다.jQuery가 매우 유행하기 때문에 $jQuery 전역 이름은 라이브러리를 만드는 것과 쉽게 연결됩니다. (전역 유출이 아니라는 것을 알아야 합니다.)
그러나 다른 두 전 세계 선수의 상황은 다르다.
  • doSomethingTwice는 루트 범위에서 정의되었기 때문에 전역 범위에 추가되었습니다. (더 깨끗한 방법은closure/iLife에 봉인하는 것입니다.)코드 라이브러리에서 검색과 교체를 통해 이 전역을 만드는 코드를 찾는 것은 어렵지 않을 것이다. doSomethingTwice 는 매우 독특한 이름이기 때문이다.그러나 전역 이름이 더 통용되거나 (예: run 코드가 희화화되거나 축소되거나 의존항에서 왔다면 어떻게 해야 합니까?그 이름만으로 성명을 추적하는 것은 더욱 어려워질 것이다.
  • i가 전역 범위에 추가되었습니다. 왜냐하면 우리가 그것을 성명할 때 var/let/const가 없고 strict mode에 없기 때문입니다.이 작은 예에서, 어느 줄의 코드가 그것을 성명했는지 매우 분명하다.하지만 행운을 빕니다. 더 큰 프로그램에서 검색하고 바꾸십시오.😅.
  • 코드 라이브러리에 전역 변수를 설정하는 코드 줄을 쉽게 추적할 수 있는 방법을 살펴봅시다.

    디버그 전역 누설: 호출 창고 검사
    다음은 우리가 이런 얄미운 글로벌 변수를 추적하는 데 도움을 줄 수 있는 고차원 개술이다.
  • 내가 추적하고 싶은 정확한 전역 변수 이름을 적어라(이하“Find what JavaScript variables are leaking into the global scope”.
  • 에이전트set 대상에 있는 이 변수의 window 명령은 변수를 설정할 때 사용자 정의 코드를 터치합니다.이 코드의 목표는 전역 변수를 설정하는 내용을 가리키는 것이다.
  • 과거에 나는 이미 첫 번째 단계를 소개했기 때문에 두 번째 단계인 대리window(또는 globalThis 대상에 주목하자.
    이 아이디어는 window.i = 1와 같은 값이 발생할 때마다 우리는 코드를 실행해서 이 값이 발생하는 상하문을 알려주기를 원한다는 것이다.유용성을 위해서, 이 상하문은 그것을 실행하는 코드에 대한 정보를 제공해야 한다. (예를 들어, 발생하는 코드 줄이나 파일을 알려주는 것)
    다음은 이러한 정보를 얻는 몇 가지 방법입니다.
  • 전역 성명이 발생할 때 debugger; 문구의 정지 코드를 사용하여 위아래 문장을 검사합니다. 이것은 스크립트 원본에 단점을 추가하는 것과 똑같습니다. 디버깅 필드와 패키지를 닫는 데 도움이 됩니다.
  • 글로벌 선언 발생 시 console.trace() 인쇄 스택 추적을 사용합니다.이것은 실행 과정에서 창고 추적 코드를 검사하는 데 도움이 된다.
  • 우리는 onGlobalDeclaration 함수를 사용하여 이 두 가지 해결 방안을 실현할 것이다.
    function onGlobalDeclaration(globalName) {
      // Print the stack trace to the console.
      console.trace();
      // Halt the code execution (only if the DevTools are running).
      debugger;
    }
    
    // TODO: Code that attaches the onGlobalDeclaration listener.
    

    디버그 전역 누설: 에이전트 window 속성
    기왕 우리가 창고에 관한 상하문 정보를 얻을 수 있는 이상 전역 변수를 설정할 때, 우리는 어떻게 invokeonGlobalDeclaration를 부가합니까?
    과거에 나는 몇 가지 다른 선택을 시도했지만, 나에게 더 좋은 방법은 전역 변수가 다른 코드 라이브러리에 설정되기 전에 그 자체를 에이전트로 실례화하는 것이다.기본적으로 window.i = 1 문장이 실행되기 전에 우리는 그것을 호출할 때 우리도 호출할 수 있도록 window.isetter 함수를 실례화하고 다시 쓰기를 희망한다onGlobalDeclaration.
    function addGlobalToInspect(globalName) {
      function onGlobalDeclaration(globalName) {
        // Print the stack trace to the console.
        console.trace();
        // Halt the code execution (only if the DevTools are running).
        debugger;
      }
    
      // Proxy the global variable that we're interested in.
      Object.defineProperty(window, globalName, {
        set: function (value) {
          // Invoke onGlobalDeclaration and set the value in a proxy attribute.
          onGlobalDeclaration(globalName);
          window[` __globals-debugger-proxy-for-${globalName}__ `] = value;
        },
        get: function () {
          // When the global is requested, return the proxy attribute value.
          return window[` __globals-debugger-proxy-for-${globalName}__ `];
        },
        configurable: true,
      });
    }
    
    // Inspect the strack whenever an "i" variable is added to the global scope.
    addGlobalToInspect("i");
    

    Note on ES6 proxies : ES6 introduced the Proxy object to cover similar use-cases. If you’re not familiar with it, the Proxy object allows you to create an object that can be used in place of the original object, but which may redefine fundamental Object operations like getting, setting, and defining properties.

    Unfortunately, the Proxy object doesn’t work well for our use case because of how the window object is implemented at the browser level (with proxies, we’d need to override it with its own proxy instance, which we can’t do), so we need to fallback to the monkey-patching approach.


    좋아요!현재 우리의 코드는 글로벌 성명을 차단할 준비가 되어 있다.다음 단계는 전역 성명문 앞에서 실행하는 것을 확보하는 것이다. addGlobalToInspect
    디버그 글로벌 누출:통합 글로벌 검사기
    우리는 디버깅 절차를 완성하기 위해 여전히 두 가지 일을 해야 한다.
    우선, 우리는 검사할 전역 변수를 설정하기 전에 실행해야 한다. addGlobalToInspect어떻게, 언제 이렇게 할지 결정하지만, 제 조언은 글로벌 inspector 코드를 자신의 코드에 넣는 것입니다.js 파일 (예: globals-debugger.js 은 다른 모든 스크립트를 불러오기 전에 불러올 수 있도록 합니다.
    <html>
      <body>
        <h1>Hello world!</h1>
        <!--- 
        Make sure to load globals-debugger.js first. 
        It might be wise to load it conditionally depending 
        on the environment (e.g., do not load it in production).
        —-->
        <script src="./globals-debugger.js"></script>
        <script src="https://unpkg.com/[email protected]/dist/jquery.js">. 
        </script>
        <script>
          function doSomethingTwice() {
            for (i = 0; i <= 2; i++) {
              const myString = `hello-world-${i}`;
              // Let's imagine we're going to do something with myString here...
            }
          }
          doSomethingTwice();
        </script>
      </body>
    </html>
    
    그리고 전역 변수를 선택해서 동적 검사를 하는 것이 좋습니다. 지금처럼 코드에서 그것들을 하드코딩하는 것이 아니라. (우리가 addGlobalToInspect("i") 에서 한 것처럼)
    스크립트가 빨리 실행되기 때문에, 전역 이름을 매개 변수로 전달하는 가장 간단한 방법은 그것들을 조회 매개 변수로 URL에 추가하는 것이라고 생각합니다.
    예를 들어, URL의 ?globalsToInspect=i,jQuery를 페이지로 로드할 때 자동으로 체크ijQuery 글로벌 변수를 시작하도록 스크립트를 변경할 수 있습니다.
    // Grab the global to inspect from the URL's "globalsToInspect" query parameter.
    const parsedUrl = new URL(window.location.href);
    (parsedUrl.searchParams.get("globalsToInspect") || "")
      .split(",")
      .filter(Boolean)
      .forEach((globalToInspect) => addGlobalToInspect(globalToInspect));
    

    전체 솔루션: globals-debugger.js글로벌 디버거를 최종적으로 시도하기 전에 다음은 완전한 코드(주석과 추가 보안 검사가 있음)입니다.


    globals-debugger.js 사용 예


    마지막으로, 여기는 우리가 방금 구축한 것을 사용하여 i 전 세계가 창조한 예를 추적하는 것이다


    Note: This is just a simplified use-case to show you the globals-debugger use-flow. In a more realistic scenario you would probably use it to track down globals added in bigger codebases or from third-party libraries deep down the dependencies tree.


    변수?globalsToInspect=i를 설정할 때 i 조회 매개 변수를 사용하여 위의 HTML 페이지를 열면 코드 실행을 즉시 정지합니다 (현재 패키지에 있는 globalName 변수가 오른쪽 패널에 있는 것을 주의하십시오 i:



    debugger;문장이 우리 코드에 있기 때문에 현재 함수(Shift+F11)를 종료하고 설정i 변수의 코드 줄에 들어가야 합니다:



    마지막이지만 가장 중요하지 않은 것은 DevTools 컨트롤러를 검사하면 기록된 창고 추적을 볼 수 있습니다. 이것은 스크립트가 실행될 때 창고를 검사하는 데 도움이 됩니다.또한 에이전트를 사용하더라도 글로벌 변수가 제대로 작동하는지 확인할 수 있습니다.


    좋은 웹페이지 즐겨찾기