tl; dr - มีบทสรุปในตอนท้ายและส่วนหัวในคำตอบเพื่อให้ง่ายต่อการค้นหาส่วนที่เกี่ยวข้อง ขอแนะนำให้อ่านทุกอย่างแม้ว่าจะมีพื้นฐานที่เป็นประโยชน์ในการทำความเข้าใจว่าเหตุใดจึงทำให้เห็นว่าการนำไปใช้ในสถานการณ์ต่างๆง่ายขึ้นอย่างไร
เกี่ยวกับนโยบายแหล่งกำเนิดเดียวกัน
นี่คือเดียวกันนโยบายแหล่งกำเนิดสินค้า เป็นคุณลักษณะด้านความปลอดภัยที่ใช้งานโดยเบราว์เซอร์
กรณีเฉพาะของคุณกำลังแสดงวิธีการใช้งาน XMLHttpRequest (และคุณจะได้รับผลลัพธ์ที่เหมือนกันหากคุณใช้การดึงข้อมูล) แต่ยังใช้กับสิ่งอื่น ๆ ด้วย (เช่นรูปภาพที่โหลดลงใน a <canvas>
หรือเอกสารที่โหลดลงใน a <iframe>
) ด้วย การใช้งานที่แตกต่างกันเล็กน้อย
(น่าแปลกที่ยังใช้กับแบบอักษร CSS ด้วย แต่นั่นเป็นเพราะผู้ก่อตั้งที่พบยืนยันใน DRM และไม่ใช่สำหรับปัญหาด้านความปลอดภัยที่โดยทั่วไปนโยบาย Origin Origin จะครอบคลุม)
สถานการณ์มาตรฐานที่แสดงให้เห็นถึงความต้องการ SOP สามารถแสดงได้ด้วยอักขระสามตัว :
- อลิซเป็นคนที่มีเว็บเบราว์เซอร์
- Bob ทำงานเว็บไซต์ (
https://www.[website].com/
ในตัวอย่างของคุณ)
- Mallory ทำงานเว็บไซต์ (
http://localhost:4300
ในตัวอย่างของคุณ)
อลิซลงชื่อเข้าใช้ไซต์ของ Bob และมีข้อมูลที่เป็นความลับอยู่ที่นั่น บางทีอาจเป็นอินทราเน็ตของ บริษัท (เข้าถึงได้เฉพาะเบราว์เซอร์บน LAN) หรือธนาคารออนไลน์ของเธอ (เข้าถึงได้ด้วยคุกกี้ที่คุณได้รับหลังจากป้อนชื่อผู้ใช้และรหัสผ่านเท่านั้น)
Alice เข้าชมเว็บไซต์ของ Mallory ซึ่งมี JavaScript บางส่วนที่ทำให้เบราว์เซอร์ของ Alice ส่งคำขอ HTTP ไปยังเว็บไซต์ของ Bob (จากที่อยู่ IP ของเธอพร้อมคุกกี้ของเธอเป็นต้น) ซึ่งอาจทำได้ง่ายเพียงแค่ใช้XMLHttpRequest
และอ่านไฟล์responseText
.
นโยบายแหล่งกำเนิดเดียวกันของเบราว์เซอร์ป้องกันไม่ให้ JavaScript อ่านข้อมูลที่ส่งคืนโดยเว็บไซต์ของ Bob (ซึ่ง Bob และ Alice ไม่ต้องการให้ Mallory เข้าถึง) (โปรดทราบว่าคุณสามารถแสดงภาพโดยใช้<img>
องค์ประกอบข้ามจุดเริ่มต้นได้เนื่องจากเนื้อหาของภาพไม่เปิดเผยต่อ JavaScript (หรือ Mallory) ... เว้นแต่คุณจะโยนผ้าใบลงในส่วนผสมซึ่งในกรณีนี้คุณจะสร้างแหล่งที่มาเดียวกัน ข้อผิดพลาดในการละเมิด)
เหตุใดจึงใช้นโยบายแหล่งกำเนิดเดียวกันเมื่อคุณไม่คิดว่าควร
สำหรับ URL ที่ระบุอาจเป็นไปได้ว่าไม่จำเป็นต้องใช้ SOP สถานการณ์ทั่วไปสองสามกรณีที่เป็นกรณีนี้:
- Alice, Bob และ Mallory เป็นคน ๆ เดียวกัน
- Bob กำลังให้ข้อมูลสาธารณะทั้งหมด
… แต่เบราว์เซอร์ไม่มีทางรู้ได้ว่าข้อใดข้อหนึ่งข้างต้นเป็นจริงดังนั้นความไว้วางใจจึงไม่เป็นไปโดยอัตโนมัติและมีการใช้ SOP ต้องได้รับอนุญาตอย่างชัดเจนก่อนที่เบราว์เซอร์จะให้ข้อมูลที่ได้รับไปยังเว็บไซต์อื่น
เหตุใดนโยบายแหล่งกำเนิดเดียวกันจึงใช้กับ JavaScript ในหน้าเว็บเท่านั้น
ส่วนขยายเบราว์เซอร์*
แท็บเครือข่ายในเครื่องมือสำหรับนักพัฒนาเบราว์เซอร์และแอปพลิเคชันเช่น Postman ได้รับการติดตั้งซอฟต์แวร์ พวกเขาไม่ได้ส่งผ่านข้อมูลจากเว็บไซต์หนึ่งไปยัง JavaScript ที่เป็นของเว็บไซต์อื่นเพียงเพราะคุณเข้าชมเว็บไซต์อื่นนั้น การติดตั้งซอฟต์แวร์มักจะเป็นทางเลือกที่ใส่ใจมากกว่า
ไม่มีบุคคลที่สาม (Mallory) ที่ถือเป็นความเสี่ยง
*
จำเป็นต้องเขียนส่วนขยายของเบราว์เซอร์อย่างระมัดระวังเพื่อหลีกเลี่ยงปัญหาข้ามแหล่งกำเนิด ดูเอกสาร Chrome สำหรับตัวอย่างเช่น
เหตุใดคุณจึงสามารถแสดงข้อมูลในเพจโดยไม่ต้องอ่านด้วย JS
มีหลายสถานการณ์ที่ไซต์ของ Mallory อาจทำให้เบราว์เซอร์ดึงข้อมูลจากบุคคลที่สามและแสดงข้อมูลนั้น (เช่นโดยการเพิ่ม<img>
องค์ประกอบเพื่อแสดงภาพ) เป็นไปไม่ได้ที่ JavaScript ของ Mallory จะอ่านข้อมูลในทรัพยากรนั้นมีเพียงเบราว์เซอร์ของ Alice และเซิร์ฟเวอร์ของ Bob เท่านั้นที่สามารถทำได้ดังนั้นจึงยังปลอดภัย
CORS
Access-Control-Allow-Origin
HTTP ตอบสนองส่วนหัวที่อ้างถึงในข้อผิดพลาดเป็นส่วนหนึ่งของล ธมาตรฐานซึ่งจะช่วยให้บ๊อบที่จะกำหนดให้ชัดเจนได้รับอนุญาตไปยังเว็บไซต์ของมัลลอที่จะเข้าถึงข้อมูลผ่านทางเบราว์เซอร์ของอลิซ
การใช้งานขั้นพื้นฐานจะรวมถึง:
Access-Control-Allow-Origin: *
…ในส่วนหัวของคำตอบเพื่ออนุญาตให้เว็บไซต์ใด ๆ อ่านข้อมูล
Access-Control-Allow-Origin: http://example.com/
…จะอนุญาตให้เฉพาะไซต์ใดไซต์หนึ่งเท่านั้นที่สามารถเข้าถึงได้และ Bob สามารถสร้างสิ่งนั้นแบบไดนามิกตามส่วนหัวของOrigin
คำขอเพื่ออนุญาตให้หลายไซต์เข้าถึงได้ แต่ไม่ใช่ทั้งหมด
ข้อมูลเฉพาะของวิธีที่ Bob ตั้งค่าส่วนหัวการตอบกลับนั้นขึ้นอยู่กับเซิร์ฟเวอร์ HTTP ของ Bob และ / หรือภาษาโปรแกรมฝั่งเซิร์ฟเวอร์ มีชุดคำแนะนำสำหรับการกำหนดค่าทั่วไปต่างๆที่อาจช่วยได้
หมายเหตุ: การร้องขอบางคนมีความซับซ้อนและส่งpreflight OPTIONS ขอให้เซิร์ฟเวอร์จะมีการตอบสนองต่อการเบราว์เซอร์ก่อนที่จะส่ง GET / POST / นำ / คำขอสิ่งที่ JS ต้องการที่จะทำให้ การใช้งาน CORS ที่เพิ่มAccess-Control-Allow-Origin
เฉพาะ URL ที่เฉพาะเจาะจงมักจะสะดุดด้วยสิ่งนี้
เห็นได้ชัดว่าการอนุญาตผ่าน CORS เป็นสิ่งที่ Bob จะทำได้ก็ต่อเมื่อ:
- ข้อมูลไม่เป็นส่วนตัวหรือ
- มัลลอรีได้รับความไว้วางใจ
แต่ฉันไม่ใช่บ็อบ!
ไม่มีกลไกมาตรฐานสำหรับMalloryในการเพิ่มส่วนหัวนี้เนื่องจากต้องมาจากเว็บไซต์ของ Bob ซึ่งเธอไม่ได้ควบคุม
หาก Bob กำลังเรียกใช้ API สาธารณะอาจมีกลไกในการเปิด CORS (อาจจะโดยการจัดรูปแบบคำขอด้วยวิธีใดวิธีหนึ่งหรือตัวเลือกการกำหนดค่าหลังจากเข้าสู่เว็บไซต์ Developer Portal สำหรับไซต์ของ Bob) สิ่งนี้จะต้องเป็นกลไกที่ Bob ดำเนินการ Mallory สามารถอ่านเอกสารในไซต์ของ Bob เพื่อดูว่ามีบางอย่างหรือไม่หรือเธอสามารถพูดคุยกับ Bob และขอให้เขาใช้ CORS
ข้อความแสดงข้อผิดพลาดที่กล่าวถึง "การตอบสนองสำหรับไฟล่วงหน้า"
ร้องขอบางแหล่งกำเนิดต่างประเทศจะถูกpreflighted
สิ่งนี้เกิดขึ้นเมื่อ (พูดโดยประมาณ) คุณพยายามร้องขอข้ามแหล่งที่มาที่:
- รวมข้อมูลประจำตัวเช่นคุกกี้
- ไม่สามารถสร้างด้วยรูปแบบ HTML ทั่วไป (เช่นมีส่วนหัวที่กำหนดเองหรือประเภทเนื้อหาที่คุณไม่สามารถใช้ในแบบฟอร์มได้
enctype
)
หากคุณกำลังทำสิ่งที่ต้องการแสงล่วงหน้าอย่างถูกต้อง
ในกรณีเหล่านี้แล้วส่วนที่เหลือของคำตอบนี้ยังคงใช้แต่คุณยังต้องให้แน่ใจว่าเซิร์ฟเวอร์สามารถฟังสำหรับการร้องขอ preflight (ซึ่งจะเป็นOPTIONS
(และไม่ได้GET
, POST
หรือสิ่งที่คุณกำลังพยายามที่จะส่ง) และตอบสนองต่อมันกับขวาAccess-Control-Allow-Origin
ส่วนหัว แต่ยังAccess-Control-Allow-Methods
และAccess-Control-Allow-Headers
เพื่ออนุญาตวิธีการหรือส่วนหัว HTTP เฉพาะของคุณ
หากคุณเรียกใช้ไฟล่วงหน้าโดยไม่ได้ตั้งใจ
บางครั้งผู้คนก็ทำผิดพลาดเมื่อพยายามสร้างคำขอของ Ajax และบางครั้งสิ่งเหล่านี้ทำให้เกิดความจำเป็นในการใช้ไฟล่วงหน้า หาก API ได้รับการออกแบบมาเพื่ออนุญาตคำขอข้ามแหล่งที่มา แต่ไม่ต้องการสิ่งใดที่จำเป็นต้องมีไฟล่วงหน้าสิ่งนี้จะทำลายการเข้าถึงได้
ข้อผิดพลาดทั่วไปที่ทำให้เกิดสิ่งนี้ ได้แก่ :
- พยายามใส่
Access-Control-Allow-Origin
และส่วนหัวการตอบสนอง CORS อื่น ๆ ตามคำขอ สิ่งเหล่านี้ไม่ได้อยู่ในคำขอไม่ทำอะไรที่เป็นประโยชน์ (ระบบการอนุญาตที่คุณสามารถให้สิทธิ์ตัวเองเป็นอย่างไร) และจะต้องปรากฏในการตอบกลับเท่านั้น
- พยายามใส่
Content-Type: application/json
ส่วนหัวในคำขอ GET ที่ไม่มีเนื้อหาของคำขอเพื่ออธิบายเนื้อหาของ (โดยทั่วไปเมื่อผู้เขียนสับสนContent-Type
และAccept
)
ในทั้งสองกรณีนี้การลบส่วนหัวคำขอพิเศษมักจะเพียงพอที่จะหลีกเลี่ยงความจำเป็นในการใช้ไฟล่วงหน้า (ซึ่งจะช่วยแก้ปัญหาได้เมื่อสื่อสารกับ API ที่รองรับคำขอธรรมดา แต่ไม่ใช่คำขอที่มีการระบุไว้ล่วงหน้า)
การตอบสนองแบบทึบ
บางครั้งคุณต้องส่งคำขอ HTTP แต่คุณไม่จำเป็นต้องอ่านคำตอบ เช่นหากคุณโพสต์ข้อความบันทึกไปยังเซิร์ฟเวอร์เพื่อบันทึก
หากคุณกำลังใช้API (มากกว่า) แล้วคุณสามารถกำหนดค่าให้ไม่ได้พยายามที่จะใช้ล ธfetch
XMLHttpRequest
โปรดทราบว่าสิ่งนี้จะไม่อนุญาตให้คุณทำอะไรที่คุณต้องการให้ CORS ทำ คุณจะไม่สามารถอ่านคำตอบได้ คุณจะไม่สามารถส่งคำขอที่ต้องใช้ไฟล่วงหน้าได้
จะช่วยให้คุณสามารถส่งคำขอแบบธรรมดาไม่เห็นการตอบกลับและไม่กรอกข้อความแสดงข้อผิดพลาดใน Developer Console
จะอธิบายได้อย่างไรโดยข้อความแสดงข้อผิดพลาดของ Chrome ที่ระบุเมื่อคุณส่งคำขอโดยใช้fetch
และไม่ได้รับอนุญาตให้ดูการตอบกลับกับ CORS:
การเข้าถึงเพื่อดึงข้อมูลที่ ' https://example.com/
' จากต้นทาง ' https://example.net
' ถูกบล็อกโดยนโยบาย CORS: ไม่ ' Access-Control-Allow-Origin
' แสดงอยู่ในทรัพยากรที่ร้องขอ หากการตอบกลับแบบทึบตอบสนองความต้องการของคุณให้ตั้งค่าโหมดของคำขอเป็น "no-cors" เพื่อดึงทรัพยากรโดยปิดใช้งาน CORS
ดังนั้น:
fetch("http://example.com", { mode: "no-cors" });
รายการทางเลือกสำหรับ CORS
JSONP
Bob ยังสามารถให้ข้อมูลโดยใช้การแฮ็กเช่นJSONPซึ่งเป็นวิธีที่ผู้คนทำ Ajax ข้ามแหล่งกำเนิดก่อนที่ CORS จะเข้ามา
ทำงานโดยการนำเสนอข้อมูลในรูปแบบของโปรแกรม JavaScript ที่ฉีดข้อมูลลงในหน้าของ Mallory
ต้องให้ Mallory ไว้วางใจ Bob ไม่ให้ส่งโค้ดที่เป็นอันตราย
หมายเหตุธีมทั่วไป: ไซต์ที่ให้ข้อมูลจะต้องแจ้งให้เบราว์เซอร์ทราบว่าไซต์ของบุคคลที่สามสามารถเข้าถึงข้อมูลที่ส่งไปยังเบราว์เซอร์ได้
เนื่องจาก JSONP ทำงานโดยการต่อท้าย<script>
องค์ประกอบเพื่อโหลดข้อมูลในรูปแบบของโปรแกรม JavaScript ซึ่งเรียกใช้ฟังก์ชันที่มีอยู่แล้วในเพจการพยายามใช้เทคนิค JSONP กับ URL ที่ส่งคืน JSON จะล้มเหลวโดยทั่วไปจะมีข้อผิดพลาด CORB เนื่องจาก JSON ไม่ใช่ JavaScript
ย้ายทรัพยากรทั้งสองไปยังแหล่งกำเนิดเดียว
หากเอกสาร HTML ที่ JS ทำงานอยู่และ URL ที่ร้องขออยู่ในต้นทางเดียวกัน (ใช้โครงร่างชื่อโฮสต์และพอร์ตเดียวกันร่วมกัน) นโยบายแหล่งกำเนิดเดียวกันจะให้สิทธิ์ตามค่าเริ่มต้น ไม่จำเป็นต้องใช้ CORS
พร็อกซี
Mallory สามารถใช้รหัสฝั่งเซิร์ฟเวอร์เพื่อดึงข้อมูล (ซึ่งเธอสามารถส่งจากเซิร์ฟเวอร์ของเธอไปยังเบราว์เซอร์ของ Alice ผ่าน HTTP ได้ตามปกติ)
มันจะ:
- เพิ่มส่วนหัว CORS
- แปลงการตอบสนองเป็น JSONP
- มีอยู่ในแหล่งกำเนิดเดียวกันกับเอกสาร HTML
โค้ดฝั่งเซิร์ฟเวอร์นั้นสามารถเขียนและโฮสต์โดยบุคคลที่สามได้ (เช่น CORS Anywhere) สังเกตผลกระทบของความเป็นส่วนตัว: บุคคลที่สามสามารถตรวจสอบได้ว่าใครเป็นผู้รับมอบฉันทะในเซิร์ฟเวอร์ของตน
Bob ไม่จำเป็นต้องให้สิทธิ์ใด ๆ เพื่อให้สิ่งนั้นเกิดขึ้น
ไม่มีผลกระทบด้านความปลอดภัยที่นี่เนื่องจากเป็นเพียงระหว่างมัลลอรีและบ็อบ ไม่มีทางที่บ็อบจะคิดว่ามัลลอรีเป็นอลิซและมอบข้อมูลที่ควรเก็บเป็นความลับระหว่างอลิซและบ็อบให้แก่มัลลอรี
ดังนั้น Mallory สามารถใช้เทคนิคนี้เพื่ออ่านข้อมูลสาธารณะเท่านั้น
อย่างไรก็ตามโปรดทราบว่าการนำเนื้อหาจากเว็บไซต์ของผู้อื่นมาแสดงและแสดงด้วยตนเองอาจเป็นการละเมิดลิขสิทธิ์และเปิดโอกาสให้คุณดำเนินการทางกฎหมายได้
การเขียนสิ่งอื่นที่ไม่ใช่เว็บแอป
ดังที่ระบุไว้ในส่วน "เหตุใดนโยบายแหล่งกำเนิดเดียวกันจึงใช้ได้กับ JavaScript ในหน้าเว็บเท่านั้น" คุณสามารถหลีกเลี่ยง SOP ได้โดยไม่เขียน JavaScript ในหน้าเว็บ
ไม่ได้หมายความว่าคุณจะใช้ JavaScript และ HTML ต่อไปไม่ได้ แต่คุณสามารถแจกจ่ายโดยใช้กลไกอื่น ๆ เช่น Node-WebKit หรือ PhoneGap
ส่วนขยายของเบราว์เซอร์
เป็นไปได้ที่ส่วนขยายของเบราว์เซอร์จะฉีดส่วนหัว CORS ในการตอบสนองก่อนที่จะใช้นโยบายแหล่งกำเนิดเดียวกัน
สิ่งเหล่านี้มีประโยชน์สำหรับการพัฒนา แต่ไม่สามารถใช้งานได้จริงสำหรับไซต์ที่ใช้งานจริง (การขอให้ผู้ใช้ไซต์ของคุณทุกคนติดตั้งส่วนขยายเบราว์เซอร์ที่ปิดใช้งานคุณลักษณะด้านความปลอดภัยของเบราว์เซอร์นั้นไม่สมเหตุสมผล)
นอกจากนี้ยังมีแนวโน้มที่จะทำงานเฉพาะกับคำขอธรรมดา ๆ เท่านั้น (ล้มเหลวเมื่อจัดการคำขอของ OPTIONS ล่วงหน้า)
การมีสภาพแวดล้อมการพัฒนาที่เหมาะสมกับเซิร์ฟเวอร์การพัฒนา
เฉพาะที่เป็นแนวทางที่ดีกว่า
ความเสี่ยงด้านความปลอดภัยอื่น ๆ
โปรดทราบว่า SOP / CORS ไม่ลดการโจมตีXSS , CSRFหรือSQL Injectionซึ่งจำเป็นต้องจัดการอย่างอิสระ
สรุป
- ไม่มีอะไรที่คุณสามารถทำได้ในโค้ดฝั่งไคลเอ็นต์ของคุณที่จะทำให้ CORS เข้าถึงเซิร์ฟเวอร์ของคนอื่นได้
- หากคุณควบคุมเซิร์ฟเวอร์จะมีการร้องขอ: เพิ่มสิทธิ์ CORS ให้กับเซิร์ฟเวอร์
- หากคุณเป็นมิตรกับบุคคลที่ควบคุม: ให้พวกเขาเพิ่มสิทธิ์ CORS ให้กับมัน
- หากเป็นบริการสาธารณะ:
- อ่านเอกสาร API เพื่อดูว่าพวกเขาพูดอย่างไรเกี่ยวกับการเข้าถึงด้วย JavaScript ฝั่งไคลเอ็นต์:
- พวกเขาอาจบอกให้คุณใช้ URL เฉพาะ
- อาจรองรับ JSONP
- อาจไม่รองรับการเข้าถึงข้ามแหล่งที่มาจากโค้ดฝั่งไคลเอ็นต์เลย (อาจเป็นการตัดสินใจโดยเจตนาเกี่ยวกับเหตุผลด้านความปลอดภัยโดยเฉพาะอย่างยิ่งหากคุณต้องส่งคีย์ API ส่วนบุคคลในแต่ละคำขอ)
- ตรวจสอบว่าคุณไม่ได้เรียกใช้คำขอไฟล่วงหน้าที่คุณไม่ต้องการ API อาจให้สิทธิ์สำหรับคำขอธรรมดา แต่ไม่ใช่คำขอที่เน้นไว้ล่วงหน้า
- หากไม่ตรงตามข้อใดข้างต้นให้ใช้เบราว์เซอร์เพื่อคุยกับเซิร์ฟเวอร์ของคุณแทนจากนั้นให้เซิร์ฟเวอร์ของคุณดึงข้อมูลจากเซิร์ฟเวอร์อื่นและส่งต่อไป (นอกจากนี้ยังมีบริการโฮสต์ของบุคคลที่สามซึ่งแนบส่วนหัว CORS เข้ากับทรัพยากรที่เข้าถึงได้แบบสาธารณะซึ่งคุณสามารถใช้ได้)