หน้าเว็บปกติสามารถใช้ออบเจ็กต์ XMLHttpRequest เพื่อส่งและรับข้อมูลจากระยะไกล เซิร์ฟเวอร์ แต่ถูกจำกัดโดยนโยบายต้นทางเดียวกัน สคริปต์เนื้อหาเริ่มต้นคำขอ ในนามของต้นทางเว็บที่มีการแทรกสคริปต์เนื้อหาลงไปในเนื้อหา สคริปต์ยังอยู่ภายใต้นโยบายต้นทางเดียวกันด้วย (สคริปต์เนื้อหาอยู่ภายใต้ CORB ตั้งแต่ Chrome 73 และ CORS ตั้งแต่ Chrome 83) ต้นทางของส่วนขยายไม่จำกัด - สคริปต์ การดำเนินการในหน้าพื้นหลังหรือแท็บเบื้องหน้าของส่วนขยายสามารถสื่อสารกับเซิร์ฟเวอร์ระยะไกลนอก ต้นทาง ตราบใดที่ส่วนขยายขอสิทธิ์แบบข้ามต้นทาง
ที่มาของส่วนขยาย
ส่วนขยายที่ทำงานอยู่แต่ละรายการมีอยู่ในต้นทางการรักษาความปลอดภัยของตัวเอง โดยไม่ต้องส่งคำขอเพิ่มเติม
ส่วนขยายสามารถใช้ XMLHttpRequest เพื่อรับทรัพยากรภายในการติดตั้ง สำหรับ
เช่น หากส่วนขยายมีไฟล์การกำหนดค่า JSON ชื่อ config.json
ในไฟล์
config_resources
โฟลเดอร์ ส่วนขยายสามารถเรียกเนื้อหาของไฟล์ได้ดังนี้
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();
หากส่วนขยายพยายามใช้ต้นทางการรักษาความปลอดภัยอื่นที่ไม่ใช่ตัวมันเอง เช่น https://www.google.com เบราว์เซอร์ไม่อนุญาต เว้นแต่ส่วนขยายจะขอแบบข้ามต้นทางที่เหมาะสม สิทธิ์
การขอสิทธิ์ข้ามต้นทาง
เพิ่มโฮสต์หรือรูปแบบการจับคู่โฮสต์ (หรือทั้ง 2 อย่าง) ลงในส่วนสิทธิ์ของส่วน ไฟล์ Manifest ส่วนขยายดังกล่าวสามารถขอสิทธิ์เข้าถึงเซิร์ฟเวอร์ระยะไกลนอกต้นทางได้
{
"name": "My extension",
...
"permissions": [
"https://www.google.com/"
],
...
}
ค่าสิทธิ์ข้ามต้นทางอาจเป็นชื่อโฮสต์แบบเต็ม เช่น
- "https://www.google.com/"
- "https://www.gmail.com/"
หรืออาจเป็นการจับคู่รูปแบบ เช่น
- "https://*.google.com/"
- "https://*/"
รูปแบบการจับคู่ของ "https://*/" ทำให้สามารถเข้าถึง HTTPS กับโดเมนที่ติดต่อได้ทั้งหมด โปรดทราบว่าตรงนี้ ให้จับคู่ จะคล้ายกับรูปแบบการจับคู่สคริปต์เนื้อหา แต่ข้อมูลเส้นทางที่อยู่หลัง ไม่พิจารณาโฮสต์
และโปรดทราบว่าระบบจะให้สิทธิ์การเข้าถึงทั้งโดยโฮสต์และตามรูปแบบ หากส่วนขยายต้องการทั้งปลอดภัยและ การเข้าถึง HTTP ที่ไม่ปลอดภัยสำหรับโฮสต์หรือชุดโฮสต์หนึ่งๆ โฮสต์ดังกล่าวจะต้องประกาศสิทธิ์แยกต่างหาก
"permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
ข้อควรพิจารณาด้านความปลอดภัย
การหลีกเลี่ยงช่องโหว่ในการเขียนสคริปต์ข้ามเว็บไซต์
เมื่อใช้ทรัพยากรที่เรียกผ่าน XMLHttpRequest หน้าพื้นหลังของคุณควรระวังอย่า ตกเป็นเหยื่อของการเขียนสคริปต์ข้ามเว็บไซต์ ขอให้หลีกเลี่ยงการใช้ API ที่เป็นอันตราย เช่น ด้านล่าง
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();
แต่ให้ใช้ API ที่ปลอดภัยกว่าซึ่งไม่เรียกใช้สคริปต์แทน ดังนี้
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();
การจำกัดการเข้าถึงสคริปต์เนื้อหาสำหรับคำขอแบบข้ามต้นทาง
เมื่อส่งคำขอข้ามต้นทางในนามของสคริปต์เนื้อหา โปรดป้องกัน หน้าเว็บที่เป็นอันตรายซึ่งอาจพยายามแอบอ้างเป็นสคริปต์เนื้อหา โดยเฉพาะอย่างยิ่ง ไม่อนุญาต สคริปต์เนื้อหาเพื่อขอ 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 ใดก็ตามที่ส่วนขยายนั้นๆ มีสิทธิ์เข้าถึง หน้าเว็บที่เป็นอันตรายอาจปลอมแปลงข้อความดังกล่าวและหลอกให้ส่วนขยาย ให้สิทธิ์เข้าถึงทรัพยากรแบบข้ามต้นทาง
แต่ให้ออกแบบเครื่องจัดการข้อความที่จำกัดทรัพยากรที่ดึงข้อมูลได้แทน ด้านล่างมีเพียง
itemId
ได้มาจากสคริปต์เนื้อหา ไม่ใช่ 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 => ...);
ต้องการใช้ HTTPS แทน HTTP
นอกจากนี้ โปรดระวังทรัพยากรที่ดึงมาผ่าน HTTP เป็นพิเศษ หากมีการใช้ส่วนขยายของคุณใน เครือข่ายที่ไม่เป็นมิตร ผู้โจมตีเครือข่าย (หรือที่เรียกว่า "man-in-the-middle") อาจแก้ไขการตอบสนองได้ และอาจโจมตีส่วนขยายของคุณ แนะนำให้ใช้ HTTPS แทนเมื่อเป็นไปได้
การปรับนโยบายความปลอดภัยของเนื้อหา
หากคุณแก้ไขนโยบายรักษาความปลอดภัยเนื้อหาเริ่มต้นสำหรับแอปหรือส่วนขยายโดยเพิ่ม
content_security_policy
ไปยังไฟล์ Manifest ของคุณ คุณต้องตรวจสอบว่าโฮสต์ที่
ที่คุณต้องการเชื่อมต่อ แม้ว่านโยบายเริ่มต้นจะไม่จำกัดการเชื่อมต่อกับโฮสต์
โปรดใช้ความระมัดระวังเมื่อเพิ่มคำสั่ง connect-src
หรือ default-src
อย่างชัดแจ้ง