Zwykłe strony internetowe mogą używać obiektu XMLHttpRequest do wysyłania i odbierania danych ze zdalnych danych ale ograniczają je te same zasady dotyczące pochodzenia. Skrypty treści inicjują żądania. w imieniu źródła stron internetowych, w którym został wstrzyknięty skrypt, a tym samym treść skrypty są również objęte tą samą zasadą dotyczącą źródła. (Skrypty treści podlegają zasadom CORB od wersji Chrome 73 i CORS od Chrome 83). Źródła rozszerzeń nie są tak ograniczone – skrypt uruchomione na stronie w tle lub na karcie na pierwszym planie mogą komunikować się z serwerami zdalnymi poza swojego źródła, o ile rozszerzenie żąda uprawnień z innych domen.
Pochodzenie rozszerzenia
Każde uruchomione rozszerzenie istnieje w ramach własnego, oddzielnego źródła zabezpieczeń. Bez wysyłania prośby o dodatkowe
uprawnienia, rozszerzenie może używać XMLHttpRequest do pobierania zasobów w trakcie instalacji. Dla:
Jeśli na przykład rozszerzenie zawiera plik konfiguracji JSON o nazwie config.json
, w tagu
config_resources
, to rozszerzenie może pobrać zawartość pliku w następujący sposób:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();
Jeśli rozszerzenie próbuje użyć innego źródła zabezpieczeń, na przykład https://www.google.com, przeglądarka nie zezwala na to rozszerzenie, chyba że rozszerzenie zażąda odpowiedniego dostępu z innej domeny uprawnień.
Żądanie uprawnień z innych domen
Dodając hosty lub wzorce dopasowania hosta (lub oba te elementy) w sekcji permissions sekcji manifest, rozszerzenie może prosić o dostęp do serwerów zdalnych spoza źródła.
{
"name": "My extension",
...
"permissions": [
"https://www.google.com/"
],
...
}
Wartości uprawnień z innych domen mogą być w pełni kwalifikowanymi nazwami hostów, na przykład:
- "https://www.google.com/"
- "https://www.gmail.com/"
Mogą to być również wzorce dopasowania, na przykład:
- "https://*.google.com/"
- "https://*/"
Wzorzec dopasowania „https://*/” zezwala na dostęp HTTPS do wszystkich osiągalnych domen. Pamiętaj, że tutaj są podobne do wzorców dopasowania skryptu treści, ale wszystkie informacje o ścieżce po jest ignorowany.
Pamiętaj też, że dostęp jest przyznawany zarówno przez hosta, jak i według schematu. Jeśli rozszerzenie potrzebuje zarówno bezpiecznych, niezabezpieczony dostęp HTTP do danego hosta lub zestawu hostów, uprawnienia należy zadeklarować oddzielnie:
"permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
Bezpieczeństwo
Unikanie luk w zabezpieczeniach (cross-site scripting)
Podczas korzystania z zasobów pobranych za pomocą XMLHttpRequest strona w tle powinna uważać, aby nie padają ofiarą ataków typu cross-site scripting. W szczególności należy unikać używania niebezpiecznych interfejsów API, takich jak poniżej:
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be evaluating an evil script!
var resp = eval("(" + xhr.responseText + ")");
...
}
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = xhr.responseText;
...
}
}
xhr.send();
Zamiast tego korzystaj z bezpieczniejszych interfejsów API, które nie uruchamiają skryptów:
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// JSON.parse does not evaluate the attacker's scripts.
var resp = JSON.parse(xhr.responseText);
}
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// innerText does not let the attacker inject HTML elements.
document.getElementById("resp").innerText = xhr.responseText;
}
}
xhr.send();
Ograniczanie dostępu do skryptu treści w przypadku żądań z innych domen
Podczas wykonywania żądań z innych domen w imieniu skryptu treści zwróć uwagę na ochronę przed złośliwe strony internetowe, które mogą próbować podszywać się pod skrypt treści. W szczególności nie zezwalaj na: skryptów treści, które wysyłają żądania dowolnych adresów URL.
Zobacz przykład, w którym rozszerzenie wysyła żądanie z innej domeny, aby zezwolić na skrypt treści poznać cenę danego produktu. Jedną z metod (niezabezpieczonych) jest określenie w skrypcie treści dokładny zasób, który ma być pobierany przez stronę w tle.
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()));
Przy zastosowaniu powyższego rozwiązania skrypt treści może zażądać od rozszerzenia pobrania dowolnego adresu URL, którego rozszerzenie ma dostęp do. Złośliwa strona może być w stanie sfałszować takie wiadomości i skłonić rozszerzenie do przyznawanie dostępu do zasobów z innych domen.
Zamiast tego zaprojektuj moduły obsługi komunikatów, które ograniczają zasoby, które można pobrać. Poniżej tylko
Pole itemId
jest dostarczane przez skrypt treści, a nie pełny adres URL.
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery == 'queryPrice') {
var 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 => ...);
Preferowany protokół HTTPS przed HTTP
Zachowaj szczególną ostrożność także w przypadku zasobów pobieranych przez HTTP. Jeśli Twoje rozszerzenie jest używane na wroga sieć, haker sieci (inaczej "man-in-the-middle") może zmodyfikować odpowiedź i potencjalnie zaatakować rozszerzenie. W miarę możliwości wybieraj HTTPS.
Dostosowywanie polityki bezpieczeństwa treści
Jeśli zmienisz domyślną Content Security Policy dla aplikacji lub rozszerzeń, dodając
content_security_policy
do pliku manifestu, musisz upewnić się, że wszystkie hosty, dla których
które chcesz połączyć. Domyślna zasada nie ogranicza połączeń do hostów,
zachowaj ostrożność podczas dodawania dyrektywy connect-src
lub default-src
.