💡 ตัวอย่างการทำงานเต็มที่มีบน GitHub:
skip-external-resources-when-signing-dotnet
บทนำ
เอกสาร Word สามารถมีรูปภาพที่ไม่ได้อยู่ในไฟล์ได้ เอกสารจะเก็บที่อยู่ไว้และโปรแกรมใดที่เปิดมันก็จะดึงที่อยู่นั้นมา ในเครื่องเดสก์ท็อปนี่เป็นฟีเจอร์ – รูปภาพจะอัปเดตเมื่อแหล่งที่มามีการเปลี่ยนแปลง แต่บนเซิร์ฟเวอร์ที่รับอัปโหลด หมายความว่าผู้ที่ส่งไฟล์ให้คุณจะกำหนด URL ที่โครงสร้างพื้นฐานของคุณต้องร้องขอ
การโหลดเอกสารอย่างปลอดภัยเป็นพฤติกรรมของ GroupDocs.Signature สำหรับ .NET ที่ปฏิเสธการทำคำขอเหล่านั้น ตั้งแต่เวอร์ชัน 26.9, LoadOptions.SkipExternalResources มีค่าเริ่มต้นเป็น true บทความนี้เปรียบเทียบโหมดการโหลดสามแบบกับเอกสารเดียวกัน แสดงวิธีการอนุญาตโฮสต์หนึ่งโดยไม่อนุญาตทั้งหมด และอธิบายว่าทำไมการลงนามไฟล์ที่ไม่เชื่อถือจึงไม่ต้องการการเข้าถึงเครือข่ายเลย
ทำไมเรื่องนี้ถึงสำคัญกว่าที่คิด
การโจมตีนี้มีชื่อ – server‑side request forgery (SSRF) – และมีรูปแบบที่เป็นรูปธรรมสามแบบ
- ที่อยู่ภายในที่ไม่สามารถเข้าถึงจากอินเทอร์เน็ตได้ แต่สามารถเข้าถึงจากเซิร์ฟเวอร์ของคุณได้ ดังนั้นเอกสารที่จัดทำขึ้นอย่างประณีตอาจทำให้บริการของคุณดึง
http://169.254.169.254/หรือ endpoint ของผู้ดูแลระบบบน localhost และขึ้นอยู่กับว่าคุณทำอะไรกับผลลัพธ์นั้น อาจทำให้ข้อมูลรั่วไหล - เส้นทาง UNC ในเอกสารอาจทำให้โฮสต์ Windows ทำการยืนยันตัวตนออกไป ส่งข้อมูลประจำตัวให้กับเซิร์ฟเวอร์ที่ผู้โจมตีควบคุม
- ลิงก์ไปยังโฮสต์ที่ไม่ตอบสนองเลยทำให้เธรดการโหลดค้างจนหมดเวลา ซึ่งเป็นวิธีราคาถูกในการทำให้พูลของ worker หมด
ฉันเคยคิดว่านี่เป็นความกังวลเชิงทฤษฎีจนกระทั่งได้เห็นเอกสารทดสอบดึงรูปภาพผ่านบริการที่ไม่มีหน้าที่ทำการร้องขอออกไปเลย สิ่งเหล่านี้ไม่ต้องอาศัยบั๊กในไลบรารีเอกสาร การตามลิงก์เป็นสิ่งที่ฟอร์แมตต้องการ; คำถามคือเซิร์ฟเวอร์ของคุณควรทำตามหรือไม่
วิธีที่ 1 – ค่าเริ่มต้นใหม่
ไม่มี LoadOptions เลย:
using var signature = new Signature(sourcePath);
return SavePagePreview(signature, previewPath);
ไม่มีอะไรถูกดึง ตัวอย่างภาพตัวอย่างจะแสดงที่ว่างเปล่าที่รูปภาพที่เชื่อมโยงไว้ และไฟล์ PNG จะเล็กกว่าที่เป็นปกติ ความแตกต่างของขนาดนี้เป็นหลักฐานที่สะดวกที่สุดว่ามีคำขอใดออกจากเครื่องหรือไม่
ฟีเจอร์ใดบ้างที่ถือเป็นภายนอก? รูปภาพที่เชื่อมโยงแทนที่ฝังไว้, ฟิลด์ INCLUDEPICTURE, รูปภาพที่เชื่อมโยงในงานนำเสนอและสเปรดชีต, รวมถึงรูปภาพและสไตล์ชีตที่ SVG อ้างอิง เนื้อหาที่ฝังไว้จะไม่ถูกกระทบ – มันอยู่แล้วในไฟล์
วิธีที่ 2 – รายการขาวที่อยู่หนึ่ง
เอกสารจำนวนมากเชื่อมโยงไปยังแหล่งที่เชื่อถือได้: CDN ของบริษัท, เซิร์ฟเวอร์รูปภาพภายใน, ร้านเทมเพลต ให้อนุญาตเฉพาะนั้นและไม่มีอื่น:
var loadOptions = new LoadOptions
{
WhitelistedResources = new List<string> { trustedAddress }
};
using var signature = new Signature(sourcePath, loadOptions);
กฎการจับคู่ต้องให้ความสนใจ มันเป็นการทดสอบ substring ที่ไม่สนใจตัวพิมพ์ใหญ่‑เล็กต่อที่อยู่ทรัพยากร ซึ่งหมายความว่า fragment สั้น ๆ อาจเป็นอันตราย: github จะตรงกับ github.attacker.example/payload.png เช่นเดียวกับโฮสต์ที่คุณตั้งใจใช้ ใช้ scheme, host และ path – ตัวอย่าง whitelist คือ raw.githubusercontent.com/groupdocs-signature/
วิธีที่ 3 – อนุญาตทั้งหมด
พฤติกรรมก่อนเวอร์ชัน 26.9 ที่ยังคงใช้ได้:
var loadOptions = new LoadOptions { SkipExternalResources = false };
เหมาะสมกับเอกสารที่แอปของคุณเองสร้างขึ้น มีกับดักหนึ่งที่ควรระบุ: property LoadExternalResources ที่ล้าสมัยมี polarity ตรงกันข้าม ดังนั้น SkipExternalResources = false คือสิ่งที่แทนที่ LoadExternalResources = true หากคัดลอกค่าจาก property เก่าไปโดยไม่ระวัง คุณจะสลับท่าทีความปลอดภัยของคุณโดยไม่มีข้อผิดพลาดบอกเตือน
การเปรียบเทียบสามวิธี: ควรใช้เมื่อใด
| โหมด | เหมาะสำหรับ | ข้อได้เปรียบหลัก | ข้อจำกัด |
|---|---|---|---|
| ค่าเริ่มต้น (skip) | การอัปโหลดโดยผู้ใช้, อีเมล, ไฟล์จากพันธมิตร | ไม่สามารถทำคำขอออกไปได้ | รูปภาพที่เชื่อมโยงจะแสดงเป็นที่ว่างเปล่า |
| Whitelist | เอกสารที่เชื่อมโยงไปยังโฮสต์ของคุณเอง | ทำให้ลิงก์ที่ถูกต้องทำงานได้ | การจับคู่ substring ต้องการ fragment ที่ยาวและเฉพาะเจาะจง |
| Allow all | ไฟล์ที่ระบบของคุณสร้างขึ้น | ตัวอย่างภาพแสดงผลเหมือนเดิม | คืนสภาพการเปิดเผย SSRF ที่ค่าเริ่มต้นได้ลบออก |
การลงนามต้องการทรัพยากรหรือไม่?
ไม่ต้อง และนี่คือผลตอบแทนเชิงปฏิบัติ การลงนามด้วย QR‑code ทำด้วยการตั้งค่าโหลดเริ่มต้นและไม่มีการร้องขอทรัพยากรภายนอกขณะโหลด, ลงนาม หรือบันทึกเอกสาร:
var options = new QrCodeSignOptions("Approved by GroupDocs.Signature")
{
EncodeType = QrCodeTypes.QR,
Left = 400,
Top = 50,
Width = 120,
Height = 120
};
SignResult result = signature.Sign(outputPath, options);
ผลลัพธ์ที่ลงนามยังคงเก็บลิงก์ไว้ ดังนั้นผู้ใช้ที่เปิดเอกสารในภายหลังจะยังเห็นรูปภาพที่แก้ไขบนเครื่องของตนเอง การข้ามเป็นนโยบายระดับเซิร์ฟเวอร์ ไม่ได้แก้ไขเอกสาร – ซึ่งทำให้ปลอดภัยต่อการประมวลผลไฟล์ในนามของผู้อื่น
สิ่งที่เปลี่ยนเมื่อคุณอัปเกรด
สำหรับบริการส่วนใหญ่ จะไม่มีอะไรที่มองเห็นได้ในครั้งแรก และนั่นเป็นสิ่งที่ควรบอกอย่างชัดเจน เพราะค่าเริ่มต้นด้านความปลอดภัยที่เปลี่ยนพฤติกรรมทั่วทุกที่จะไม่ผ่านการตรวจสอบอัปเกรดได้ ยกเว้นกรณีที่ตัวอย่างหรือ thumbnail เคยแสดงรูปภาพที่เชื่อมโยงและตอนนี้แสดงที่ว่างเปล่า; นั่นคือการเปลี่ยนแปลงที่ทำหน้าที่ของมัน และการแก้คือเพิ่มรายการ whitelist หากโฮสต์เป็นของคุณ, หรือยอมรับหากเอกสารมาจากภายนอก
วิธีตรวจสอบที่ตรงไปตรงมาคือวิธีที่ตัวอย่างใช้: แสดงเอกสารเดียวกันในสามโหมดและเปรียบเทียบขนาดผลลัพธ์ หากขนาดของ preview ค่าเริ่มต้นและ whitelist เหมือนกัน แสดงว่าไม่มีการดึงข้อมูลในกรณีใดกรณีหนึ่ง – ซึ่งมักหมายความว่าโฮสต์ไม่สามารถเข้าถึงจากเครื่องนั้น แทนที่ whitelist ล้มเหลว และตัวอย่างจะแสดงข้อความบอกอย่างนั้น
ตัวช่วยสร้าง Preview, เนื่องจากไม่ชัดเจน
สองในสามโหมดข้างต้นเรียกตัวช่วยขนาดเล็ก และคุ้มค่าที่จะแสดงเพราะ PreviewOptions ไม่รับพาธ:
var previewOptions = new PreviewOptions(
pageData => File.Create(previewPath),
(pageData, pageStream) => pageStream.Dispose())
{
PreviewFormat = PreviewOptions.PreviewFormats.PNG
};
signature.GeneratePreview(previewOptions);
มันรับ factory สองตัว – หนึ่งสร้างสตรีมต่อหน้า, อีกหนึ่งปล่อยสตรีม ตัวอย่างเอกสารมีหน้าเดียว ดังนั้นไฟล์เดียวจะถูกเขียน; หากเป็นอินพุตหลายหน้า ให้ใส่หมายเลขหน้าในชื่อไฟล์ มิฉะนั้นทุกหน้าจะเขียนทับหน้าเดิม
แนวปฏิบัติที่ดีที่สุด
- ถือทุกอย่างที่คุณไม่ได้สร้างเองว่าไม่เชื่อถือ, รวมถึงไฟล์จากพันธมิตรที่มีท่าทีด้านความปลอดภัยดี
- ทำให้ fragment ของ whitelist ยาวพอที่จะไม่มีความคลุมเครือ, และตรวจสอบเมื่อ CDN มีการเปลี่ยนแปลง
- อย่าตั้งค่า
SkipExternalResourcesจากค่าที่เคยกำหนดให้LoadExternalResources - ตรวจสอบด้วยขนาดผลลัพธ์แทนการตรวจสอบค่าการตั้งค่า; การตั้งค่าที่ดูถูกต้องและคำขอที่ไม่ได้เกิดขึ้นเป็นข้ออ้างที่ต่างกัน
สิ่งที่เกิดขึ้นกับ SVG
ควรอธิบายแยกต่างหาก เพราะ SVG เป็นฟอร์แมตอัปโหลดที่พบบ่อยและเป็นเวกเตอร์ SSRF ที่พบบ่อย SVG สามารถอ้างอิงรูปภาพและสไตล์ชีตด้วย URL, และการอ้างอิงเหล่านั้นเป็นทรัพยากรภายนอกตามกฎเดียวกัน – ถูกข้ามโดยค่าเริ่มต้น, สามารถ whitelist ได้, สามารถกู้คืนได้ บริการที่รับ SVG avatar หรือโลโก้และเรนเดอร์บนเซิร์ฟเวอร์เป็นระบบที่การเปลี่ยนแปลงนี้คุ้มครองโดยตรง
หาก pipeline ของคุณรับ SVG จากผู้ใช้, ค่าเริ่มต้นคือการตั้งค่าที่คุณต้องการ, และ whitelist ใช้ในกรณีที่เทมเพลตของคุณดึงสไตล์ชีตที่แชร์จากโฮสต์ที่คุณดูแล
สรุป
ค่าเริ่มต้นได้ถูกสลับให้พฤติกรรมที่เสี่ยงต้องการการตัดสินใจอย่างชัดเจนและพฤติกรรมที่ปลอดภัยไม่ต้องการอะไรเลย ให้ใช้ค่าเริ่มต้นสำหรับข้อมูลที่ไม่เชื่อถือ, whitelist อย่างแคบเมื่อโฮสต์ของคุณเกี่ยวข้อง, และจำไว้ว่า การลงนามเองไม่เคยต้องการเครือข่าย การรันตัวอย่างกับเอกสารของคุณเองใช้เวลาเพียงนาทีและบอกคุณในสามขนาดไฟล์ว่าเซอร์วิสของคุณดึงอะไรบ้าง