일반 웹페이지는 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)를 사용하려고 하면 확장 프로그램이 적절한 교차 출처 권한을 요청하지 않는 한 브라우저에서 이를 허용하지 않습니다.
교차 출처 권한 요청
확장 프로그램의 출처 외부의 원격 서버에 대한 액세스를 요청하려면 manifest 파일의 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 액세스와 비보안 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
지시어를 명시적으로 추가할 때는 주의하세요.