본문으로 바로가기

파일의 IT 블로그

  1. Home
  2. 웹/Chrome Extension
  3. [크롬 확장프로그램] declarativeNetRequest + manifest v3 이용하여 요청값 리다이렉트, 차단 방법

[크롬 확장프로그램] declarativeNetRequest + manifest v3 이용하여 요청값 리다이렉트, 차단 방법

· 댓글개 · KRFile

서론

저번글에서 요청에 대해 응답값을 마음대로 조작하는 방법을 다뤘습니다. 사실 크롬 확장 프로그램을 개발하면서 처음부터 응답값을 변조하려던건 아니였고, 요청 자체를 차단해서 응답데이터가 오지 않게 하거나 요청을 리다이렉트 시켜서 응답 데이터를 변조하려고 했었습니다.

 

어떻게든 삽질을 통해서 응답값 변조를 포함해, 응답 데이터 차단(Blocking) [각주*요청 자체를 차단하므로 응답은 오지 않고 오류 코드가 리턴되므로 응답 데이터 차단이라고 했지만, 엄밀히 따지면 요청 자체를 차단하는게 맞는 거 같습니다.*], 리다이렉트(Redirect) 하는 방법 전부를 알게 되었습니다. 이번 글에서는 크롬 확장프로그램으로 요청을 차단하거나 리다이렉트 시키는 방법을 다룹니다.

 

만약에 요청을 차단하거나 리다이렉션 해서 응답을 조절하는 방식이 아닌 응답값 자체를 변조시키고 싶으신 분들은 본 글을 참고해주세요.

 

Manifest Version 2?

https://developer.chrome.com/docs/extensions/reference/webRequest/

크롬 확장프로그램이 manifest version 3란 것으로 업데이트 됨에 따라 기존에 사용하던 manifest version 2는 곧 지원이 중단될 예정입니다. (deprecated)

 

원래 manifest version 2에서 웹 요청을 차단하기 위해 사용했던 방법은 webRequest 와 webRequestBlocking 이였지만 webRequestBlocking 권한이 v3부턴 제대로 지원하지 않으므로 declarativeNetRequest 를 적용하라고 나옵니다.

 

이미지 출처 : https://kangjung.tistory.com/24

일단 manifest version 2 로 확장 프로그램을 작업하고 webRequest 와 webRequestBlocking 권한을 사용하여 요청을 차단할 수도 있지만 크롬에서 manifest v2로 확장 프로그램을 만들어 설치하면 이런식으로 2023년에 지원이 종료될 거라고 나옵니다. 

 

계속 확장 프로그램을 만들고 유지할 생각이 있으시다면 웹 요청 차단 (+ 리다이렉션) 구현을 위해 manifest version 3 와 declarativeNetRequest 란 것을 사용해야 합니다.

 

기존에 webRequest 와 webRequestBlocking 를 이용해 요청을 차단하는게 이벤트 리스너와 명령형 프로그래밍을 활용하는 방법이였다면, 이번 새로(?) 도입된 declarativeNetRequest 의 경우엔 선언형 프로그래밍 처럼 웹 요청을 차단하거나 리다이렉션을 할 URL 과 방식을 미리 "선언" 하고 다루는 방식입니다.

 

글로만 써서는 굉장히 어렵게 설명이 됐는데.. 아래에 제가 예제로 방법을 보여드릴것이기 때문에 걱정은 안하셔도 됩니다.

 

쉬운 설명 : 크롬 확장프로그램의 최신 버전은 manifest version 3임. 근데 manifest version3에서 웹 요청을 차단하거나 리다이렉션 시키려면 manifest version 2 에서 사용한 방법이 안됨. declarativeNetRequest 라는 요상한 신기술을 써야한다고 함.

 

시작전에

https://github.com/GoogleChrome/chrome-extensions-samples

 

GitHub - GoogleChrome/chrome-extensions-samples: Chrome Extensions Samples

Chrome Extensions Samples. Contribute to GoogleChrome/chrome-extensions-samples development by creating an account on GitHub.

github.com

크롬 확장 프로그램 개발 중 어려움을 겪는다면 스택 오버플로우의 오래되고 정확하지 않은 글을 찾아보는 것보다 여기 구글 크롬 공식 저장소를 이용하는게 좋습니다. 이 저장소에서는 크롬 확장 프로그램에서 다루는 여러 API에 대한 예제 코드를 제공하고 있습니다.

 

구글링을 열심히 하면서 declarativeNetRequest 에 대한 제대로 된 사용법을 전혀 찾아내지 못했는데 여기 저장소에서 한번에 찾아낼 수 있었습니다.

 

그러므로 본 글에서 다루는 declarativeNetRequest에 대한 사용 예제는 여기 저장소에서 제공하는 예제 파일입니다.

 

 

예제로 알아보기

declarativeNetRequest.zip
0.01MB

시작전에 예제 파일을 미리 받아주세요.

압축을 풀어보면 총 3가지의 예제가 있습니다

1. no-cookies - 쿠키제거

2. url-blocker - 요청 차단

3. url-redirect - 요청 리다이렉트

 

이 3가지 예제에 대해 간단히 해설만 하도록 하겠습니다.

 

1. no cookies

{
  "name": "No Cookies",
  "description": "Removes 'Cookie' headers.",
  "version": "1.0",
  "manifest_version": 3,
  "permissions": ["declarativeNetRequest", "declarativeNetRequestFeedback"],
  "host_permissions": ["<all_urls>"],
  "background": {
    "service_worker": "service-worker.js"
  },
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules_1.json"
      }
    ]
  }
}

먼저 manifest.json 파일입니다. "declarativeNetRequest", "declarativeNetRequestFeedback" 라는 2가지 권한을 주고 있으며, "host_permissions" 에 대해 모든 url에 권한을 주고 있습니다.

 

declarativeNetRequest에 가장 중요한 특징은 manifest.json 에 규칙을 선언(declarative)한다는 점입니다.

 

declarativeNetRequest 라는 key를 json에 등록하고 rule 을 등록합니다.

저 세부 규칙에 관한 정보는 rules_1.json 에 저장할 것이며, 그렇기에 path를 값으로 주었습니다.

id는 그냥 식별하기 위한 값이라 크게 중요한 이름은 아니며 enabled 가 true여야지 저 규칙이 적용됩니다.

 

참고로 declarativeNetRequest 를 제대로 사용하기 위해선 컨텐츠 스크립트가 아니라 반드시 백그라운드 스크립트에 등록해야 합니다. 요청이 일어나는건 컨텐츠가 로딩되기 이전의 일이기 때문에 백그라운드 스크립트로 작동시켜야 합니다!! 필수!!

 

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",
      "requestHeaders": [{ "header": "cookie", "operation": "remove" }]
    },
    "condition": {
      "urlFilter": "|*?no-cookies=1",
      "resourceTypes": ["main_frame"]
    }
  }
]

rules_1.json 에 가보시면 이런식으로 또 규칙이 있는걸 확인 가능합니다. rules_1.json 안에 저런식으로 id를 1부터 N까지 늘리고, 우선순위(priority) 도 1부터 N까지 늘려 여러 규칙 추가가 가능합니다.

 

지금 보시면 요청 헤더에서 쿠키를 제거하도록 조작하고 있습니다. 단 condition 의 조건이 맞는 요청의 경우에만 쿠키를 지웁니다. 보다 싶이 요청 URL에 no-cookies=1 이라는 문자가 포함되어야 쿠키가 지워지나 보네요.

 

// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

// Simple extension to remove the 'Cookie' request header.

chrome.declarativeNetRequest.onRuleMatchedDebug.addListener((e) => {
  const msg = `Cookies removed in request to ${e.request.url} on tab ${e.request.tabId}.`;
  console.log(msg);
});

console.log('Service worker started.');

마지막으로 백그라운드 스크립트인 service_worker.js 에 보시면 chrome.declarativeNetRequest 란 것을 이용하여 이벤트 리스너로 요청값을 컨트롤 하고 있는걸 보실 수 있습니다.

오늘 예제 3개에서 service_worker.js 안에 있는 내용은 전부 동일하고 메세지 내용만 다릅니다. 그래서 아래 예제 2개는 service_worker.js 에 대한 설명을 생략하도록 하겠습니다.

 

2. url-blocker

이번에는 특정 url이 요청으로 들어오면 요청을 차단시키는 예제를 보겠습니다. 사실 앞의 쿠키 예제보다 지금 나올 url blocking 하고 redirection 이 훨씬 더 유용합니다.

 

{
  "name": "URL Blocker",
  "version": "0.1",
  "manifest_version": 3,
  "description": "Blocks all main frame requests to example.com.",
  "background": {
    "service_worker": "service_worker.js"
  },
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules_1.json"
      }
    ]
  },
  "permissions": ["declarativeNetRequest", "declarativeNetRequestFeedback"]
}

manifest.json 에 아까처럼 사용할 rule 을 명시해줍니다. 백그라운드 스크립트로 service_worker도 동일하게 실행시켜 줍니다.

 

[
  {
    "id": 1,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": "||example.com",
      "resourceTypes": ["main_frame"]
    }
  }
]

이제 rule 이 중요한데 example.com 이라는 url이 들어오면 웹 요청이 차단되게끔 선언함을 보실 수 있습니다.

실제로 해당 크롬 확장 프로그램을 설치하고 example.com 을 들어가면 웹 요청이 차단됩니다.

 

https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/

다만 아까 설명하지 않았지만 urlfilter 의 동작이 상당히 특이합니다. url filter의 동작에 관해선 위 표를 참고하시면 좋을 거 같습니다. 매칭은 기본적으로 문자열에 별다른 기호를 적지 않아도 전역적(global) 으로 매칭하며 * 은 일반적으로 모든 문자를 대표하는 wildcard를 의미하는 거 같습니다. 근데 다른건 솔직히 잘 모르겠네요 ㅈㅅ 

걍 주소만 잘 적어주면 될 듯..?

 

[
  {
    "id": 1,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": "github.com",
      "resourceTypes": ["main_frame"]
    }
  }
]

만약에 url 필터링 조건을 이렇게 바꾸면 github.com 이 포함된 모든 url에 대한 요청을 차단합니다.

 

위 예제에서 rule만 github.com 으로 바꾸고 크롬 확장프로그램 설치한 다음 들어가면 이런식으로 차단됐다고 나옵니다.

 

3. url-redirect

마지막으로 특정 요청에 대해 리다이렉트를 시켜버리는 예제를 보겠습니다. 리다이렉션 대상을 잘만 지정해주면 요청 url이 바뀌는 거니깐 결론적으로 응답값도 변조해낼 수 있을 거 같습니다.

다만 아무곳이나 리다이렉트 시켜버리면 CORS..가 걸리니깐 이 점은 주의하셔야 합니다.

 

{
  "name": "URL Redirect",
  "version": "0.1",
  "manifest_version": 3,
  "description": "Redirects MV2 documents to equivalent MV3 documents on developer.chrome.com.",
  "background": {
    "service_worker": "service_worker.js"
  },
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules_1.json"
      }
    ]
  },
  "permissions": [
    "declarativeNetRequest",
    "declarativeNetRequestFeedback",
    "declarativeNetRequestWithHostAccess"
  ],
  "host_permissions": ["https://developer.chrome.com/"]
}

manifest.json 에 똑같이 declarative_net_request 선언을 해주고, 백그라운드 스크립트 실행, 권한으론 저 3가지를 줍니다. 그리고 가장 중요한 것으로 host_permission 에 리다이렉션 시 사용할 url을 명시해줍니다. 저기에 url을 명시해주지 않으면 리다이렉션을 declarativeNetRequest 를 이용해서 시켜도 아무 동작이 되지 않습니다. 권한이 없기 때문이죠.

 

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "url": "https://developer.chrome.com/docs/extensions/mv3/intro/"
      }
    },
    "condition": {
      "urlFilter": "https://developer.chrome.com/docs/extensions/mv2/",
      "resourceTypes": ["main_frame"]
    }
  },
  {
    "id": 2,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "url": "https://developer.chrome.com/docs/extensions/reference/action/"
      }
    },
    "condition": {
      "urlFilter": "https://developer.chrome.com/docs/extensions/reference/browserAction/",
      "resourceTypes": ["main_frame"]
    }
  },
  {
    "id": 3,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "url": "https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/"
      }
    },
    "condition": {
      "urlFilter": "https://developer.chrome.com/docs/extensions/reference/declarativeWebRequest/",
      "resourceTypes": ["main_frame"]
    }
  },
  {
    "id": 4,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "url": "https://developer.chrome.com/docs/extensions/reference/action/"
      }
    },
    "condition": {
      "urlFilter": "https://developer.chrome.com/docs/extensions/reference/pageAction/",
      "resourceTypes": ["main_frame"]
    }
  },
  {
    "id": 5,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "url": "https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/"
      }
    },
    "condition": {
      "urlFilter": "https://developer.chrome.com/docs/extensions/reference/webRequest/",
      "resourceTypes": ["main_frame"]
    }
  }
]

rule 은 이런식으로 5가지가 들어 있습니다. 모두 우선 순위는 1개이고 id만 다르게 설정 되어 있네요. 우선 순위가 다르면 

아마 적용 순서가 달라질 듯 한데 이렇게 동일하면 어떤 순서로 작동할 진 잘 모르겠습니다.

 

{
    "id": 1,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": {
        "url": "https://developer.chrome.com/docs/extensions/mv3/intro/"
      }
    },
    "condition": {
      "urlFilter": "https://developer.chrome.com/docs/extensions/mv2/",
      "resourceTypes": ["main_frame"]
    }
  }
}

일단 맨 위 id 1번의 규칙만 해석해보면 웹 요청이 https://developer.chrome.com/docs/extensions/mv3/intro/https://developer.chrome.com/docs/extensions/mv2/ 로 리다이렉트 시키라는 의미입니다.

 

그리고 "resourceTypes" 이 "main_frame" 인데 이건 확인해보니 크롬 창에서 저희가 직접 열고 들어간 URL을 의미하는 거 같습니다.

 

예를 들어서 저희가 리다이렉션 시킬 웹 요청이 저희가 직접 열고 들어간 사이트가 아니라 직접 열고 들어간 사이트 안의 Axios, Fetch 같은 라이브러리가 내부적으로 요청을 수행하는 것이다 하면  "resourceTypes" : "main_frame" 부분을 제거해야 합니다.

 

좀 더 쉽게 이야기 해서 웹 패킷 보고 리다이렉션을 시켰는데도 리다이렉션이 안걸리면  "resourceTypes" : "main_frame" 을 빼면 됩니다.

 

공식문서도 그렇고.. 제 설명도 그렇고.. 마음에 들고 쉬운 설명이 하나도 없긴 한데 아마 예제를 하나씩 실행해보시면서 삽질하다보면 동작 원리에 대해 깨달으실거라 믿습니다 ㅎㅎ;

 

SNS 공유하기
💬 댓글 개
이모티콘창 닫기
울음
안녕
감사해요
당황
피폐

이모티콘을 클릭하면 댓글창에 입력됩니다.