일반 웹페이지는 fetch() 또는 XMLHttpRequest API를 사용하여 원격
서버에서 데이터를 주고받을 수 있지만 동일한 출처 정책에 따라 제한됩니다. 콘텐츠 스크립트는 콘텐츠 스크립트가 삽입된 웹 출처를 대신하여 요청을 시작하므로 콘텐츠
스크립트에도 동일한 출처 정책이 적용됩니다. 확장 프로그램 출처는 제한이 없습니다. 확장 프로그램 서비스 워커 또는 포그라운드 탭에서 실행되는 스크립트는 확장 프로그램이 호스트 권한을 요청하는 한 출처 외부의 원격 서버와 통신할 수 있습니다.
확장 프로그램 출처
실행 중인 각 확장 프로그램은 자체 보안 출처 내에 있습니다. 추가 권한을 요청하지 않고도 확장 프로그램은 fetch()를 호출하여 설치 내에서 리소스를 가져올 수 있습니다. 예를 들어 확장 프로그램에 config_resources/ 폴더에 config.json이라는 JSON 구성 파일이 포함되어 있는 경우 확장 프로그램은 다음과 같이 파일의 콘텐츠를 가져올 수 있습니다.
const response = await fetch('/config_resources/config.json');
const jsonData = await response.json();
확장 프로그램이 자체 보안 출처가 아닌 다른 보안 출처(예: https://www.google.com)에서 콘텐츠를 요청하려고 하면 확장 프로그램에 호스트 권한이 없는 한 교차 출처 요청으로 처리됩니다. 확장 프로그램에 호스트 권한이 있더라도 콘텐츠 스크립트에서 교차 출처 요청은 항상 교차 출처 요청으로 처리됩니다.
교차 출처 권한 요청
확장 프로그램 출처 외부의 원격 서버에 대한 액세스를 요청하려면 매니페스트 파일의 host_permissions 섹션에 호스트, 일치 패턴 또는 둘 다 추가합니다.
{
"name": "My extension",
...
"host_permissions": [
"https://www.google.com/"
],
...
}
교차 출처 권한 값은 다음과 같이 정규화된 호스트 이름일 수 있습니다.
- "https://www.google.com/"
- "https://www.gmail.com/"
또는 다음과 같이 일치 패턴일 수 있습니다.
- "https://*.google.com/"
- "https://*/"
'https://*/'의 일치 패턴을 사용하면 연결 가능한 모든 도메인에 대한 HTTPS 액세스가 허용됩니다. 여기서 일치 패턴은 콘텐츠 스크립트 일치 패턴과 비슷하지만 호스트 뒤에 오는 모든 경로 정보는 무시됩니다.
액세스 권한은 호스트와 스키마 모두에 의해 부여됩니다. 확장 프로그램이 특정 호스트 또는 호스트 집합에 대한 보안 및 비보안 HTTP 액세스를 모두 원하는 경우 권한을 별도로 선언해야 합니다.
"host_permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
Fetch()와 XMLHttpRequest()
fetch()는 서비스 워커를 위해 특별히 만들어졌으며 동기 작업에서 벗어나는 더 광범위한 웹 트렌드를 따릅니다. XMLHttpRequest() API는 서비스 워커 외부의 확장 프로그램에서 지원되며 이를 호출하면 확장 프로그램 서비스 워커의 가져오기 핸들러가 트리거됩니다. 새 작업은 가능한 경우 fetch()를 선호해야 합니다.
보안 고려사항
교차 사이트 스크립팅 취약점 방지
fetch()를 통해 가져온 리소스를 사용할 때는 오프스크린 문서, 사이드 패널 또는 팝업이
교차 사이트 스크립팅의 피해자가 되지 않도록 주의해야 합니다. 특히 innerHTML과 같은 위험한 API는 사용하지 마세요. 예를 들면 다음과 같습니다.
const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = jsonData;
...
대신 스크립트를 실행하지 않는 더 안전한 API를 사용하는 것이 좋습니다.
const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// JSON.parse does not evaluate the attacker's scripts.
let resp = JSON.parse(jsonData);
const response = await fetch("https://api.example.com/data.json");
const jsonData = response.json();
// textContent does not let the attacker inject HTML elements.
document.getElementById("resp").textContent = jsonData;
교차 출처 요청에 대한 콘텐츠 스크립트 액세스 제한
콘텐츠 스크립트를 대신하여 교차 출처 요청을 실행할 때는 콘텐츠 스크립트를 가장하려고 할 수 있는 악성 웹페이지를 방지하는 데 주의해야 합니다. 특히 콘텐츠 스크립트가 임의의 URL을 요청하도록 허용하지 마세요.
확장 프로그램이 교차 출처 요청을 실행하여 콘텐츠 스크립트가 항목의 가격을 검색할 수 있도록 하는 예를 살펴보겠습니다. 보안이 좋지 않은 방법 중 하나는 콘텐츠 스크립트가 백그라운드 페이지에서 가져올 정확한 리소스를 지정하도록 하는 것입니다.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery == 'fetchUrl') {
// WARNING: SECURITY PROBLEM - a malicious web page may abuse
// the message handler to get access to arbitrary cross-origin
// resources.
fetch(request.url)
.then(response => response.text())
.then(text => sendResponse(text))
.catch(error => ...)
return true; // Will respond asynchronously.
}
}
);
chrome.runtime.sendMessage(
{
contentScriptQuery: 'fetchUrl',
url: `https://another-site.com/price-query?itemId=${encodeURIComponent(request.itemId)}`
},
response => parsePrice(response.text())
);
위의 방법에서 콘텐츠 스크립트는 확장 프로그램에 액세스할 수 있는 모든 URL을 가져오도록 확장 프로그램에 요청할 수 있습니다. 악성 웹페이지는 이러한 메시지를 위조하고 확장 프로그램을 속여 교차 출처 리소스에 대한 액세스 권한을 부여할 수 있습니다.
대신 가져올 수 있는 리소스를 제한하는 메시지 핸들러를 설계하세요. 아래에서는 전체 URL이 아닌 itemId만 콘텐츠 스크립트에서 제공됩니다.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery == 'queryPrice') {
const url = `https://another-site.com/price-query?itemId=${encodeURIComponent(request.itemId)}`
fetch(url)
.then(response => response.text())
.then(text => parsePrice(text))
.then(price => sendResponse(price))
.catch(error => ...)
return true; // Will respond asynchronously.
}
}
);
chrome.runtime.sendMessage(
{contentScriptQuery: 'queryPrice', itemId: 12345},
price => ...
);
HTTP보다 HTTPS 선호
또한 HTTP를 통해 가져온 리소스에 특히 주의하세요. 확장 프로그램이 적대적인 네트워크에서 사용되는 경우 네트워크 공격자 (일명 "man-in-the-middle")가 응답을 수정하고 확장 프로그램을 공격할 수 있습니다. 대신 가능한 경우 HTTPS를 사용하는 것이 좋습니다.
콘텐츠 보안 정책 조정
매니페스트에
content_security_policy 속성을 추가하여 확장 프로그램의 기본 콘텐츠 보안 정책을 수정하는 경우 연결하려는 모든 호스트가 허용되는지 확인해야 합니다. 기본 정책은 호스트에 대한 연결을 제한하지 않지만 connect-src 또는 default-src 지시어를 명시적으로 추가할 때는 주의하세요.