ความปลอดภัย
โมเดลความปลอดภัยของเราในประโยคเดียว: ไม่มีอะไรบนเซิร์ฟเวอร์ที่จะโจมตีได้ ทุกอย่างด้านล่างนี้สามารถตรวจสอบได้จาก DevTools ของคุณ
สถาปัตยกรรม
เครื่องมือสร้างเป็นแอปพลิเคชันหน้าเดียวแบบสถิตที่ให้บริการจาก Cloudflare Pages: ไม่มีบัญชีผู้ใช้ ไม่มีการยืนยันตัวตน และไม่มีเส้นทางโค้ดแบ็กเอนด์ใดๆ ในขั้นตอนการสร้าง QR ทั้งสิ้น การสร้าง QR การเข้ารหัส การสแกน และการเรนเดอร์ทุกอย่างทำงานในเบราว์เซอร์ของคุณอย่างสมบูรณ์ มีจุดปลายแบบ serverless ขนาดเล็กหนึ่งจุดอยู่ข้างๆ ซึ่งเราแจ้งไว้ตรงนี้แทนที่จะซ่อนไว้ ได้แก่ /api/contact (แบบฟอร์มติดต่อ ซึ่งจัดเก็บสิ่งที่คุณส่งไว้ใน Cloudflare D1 และส่งอีเมลมาหาเรา) จุดปลายนี้ไม่ถูกเรียกใช้ระหว่างที่คุณสร้าง QR code ตัวตรวจสอบความปลอดภัยของ URL เป็นผลิตภัณฑ์แยกต่างหากที่ check.qr.abundera.ai ตรวจสอบว่า QR code ปลอดภัยหรือไม่
โมเดลภัยคุกคาม
เนื่องจากเครื่องมือสร้างไม่ได้เก็บรวบรวม จัดเก็บ หรือส่งข้อมูลผู้ใช้ (จุดปลายทั้งสองข้างต้นจัดการเฉพาะสิ่งที่คุณส่งให้อย่างชัดเจนเท่านั้น) ภัยคุกคามเว็บแอปที่พบบ่อยที่สุด, การขโมยข้อมูลประจำตัว การละเมิดฐานข้อมูล การแย่งชิงเซสชัน การฉีดโค้ดฝั่งเซิร์ฟเวอร์, ไม่มีผลใช้บังคับกับมัน พื้นที่โจมตีที่เหลืออยู่คือชุดทรัพย์สินแบบสถิต (HTML, CSS, JavaScript) ที่ให้บริการจากแหล่งต้นทางของเรา เราออกแบบโดยสมมติว่า:
- การโจมตีแหล่งต้นทาง: ผู้โจมตีแทนที่ทรัพย์สินที่รวมอยู่ของเราด้วยทรัพย์สินที่เป็นอันตราย CSP ด้านล่างจำกัดรัศมีความเสียหาย token cache-buster ทำให้การย้อนกลับไปยังเวอร์ชันที่ดีที่รู้จักทำได้ง่าย
- ข้อมูลป้อนเข้าจากผู้ใช้: payload ของ QR ชื่อ vCard รหัสผ่าน WiFi แถว CSV แบบชุด เนื้อหาภาพที่สแกน ทุกเส้นทางป้อนข้อมูลถูกถือว่าไม่น่าเชื่อถือเมื่อเรนเดอร์กลับสู่ DOM
- ภาพที่ผู้ใช้ให้มา: URL รูปภาพ vCard และการอัปโหลดโลโก้ ภาพถูกเรนเดอร์ลงใน canvas ไม่เคยถูกฝังลงใน DOM ในรูปแบบ markup ดิบ
- ส่วนขยายเบราว์เซอร์ของบุคคลที่สาม: อยู่นอกขอบเขต หากส่วนขยายในเบราว์เซอร์ของคุณมีสิทธิ์แก้ไขทุกหน้า ก็สามารถแก้ไขหน้าของเราได้เช่นกัน การรับประกันของเรามีผลบังคับใช้เมื่อหน้าถูกโหลดโดยไม่มีการแก้ไข
นโยบายความปลอดภัยของเนื้อหา, ตามคำสั่ง
นโยบายด้านล่างถูกอ่านจากไฟล์ _headers ที่ใช้งานจริงเมื่อสร้างหน้านี้ จึงไม่มีทางคลาดเคลื่อนไปจากสิ่งที่ edge ส่งจริง ตรวจสอบได้ในส่วนหัว response ของคำขอใดๆ
Content-Security-Policy:
default-src 'self';
script-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com;
worker-src 'self' blob:;
style-src 'self' 'unsafe-inline' https://abundera.ai;
font-src 'self';
img-src 'self' data: blob: https:;
connect-src 'self' https: https://challenges.cloudflare.com;
frame-src 'self' https://challenges.cloudflare.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self'สิ่งที่แต่ละคำสั่งอนุญาตให้เราทำและจุดที่มีการประนีประนอม:
default-src 'self', พื้นฐานที่แข็งแกร่ง สิ่งที่เราไม่ผ่อนคลายอย่างชัดเจนจะยังคงเป็น same-originscript-src 'self' https://abundera.ai 'wasm-unsafe-eval' https://challenges.cloudflare.com, ไม่มี<script>แบบ inline ไม่มีeval()abundera.aiคือโดเมนแม่ของเราเองและให้บริการสคริปต์ที่ใช้ร่วมกันหนึ่งตัวคือตัวเลือกภาษาchallenges.cloudflare.comคือวิดเจ็ต Turnstile ที่ใช้เฉพาะบน /contact/ เท่านั้น'wasm-unsafe-eval'จำเป็นสำหรับเส้นทางการคอมไพล์ WebAssembly ของไลบรารี encoder QR ของเรา มันไม่อนุญาตให้ใช้eval()แบบดั้งเดิม การตรวจสอบก่อนการ deploy ของเราสแกนทุกหน้า HTML หา inline script และทำให้การ build ล้มเหลวstyle-src 'self' 'unsafe-inline', การประนีประนอมที่แท้จริง ตัวอย่าง QR ของเราคำนวณสีระดับพิกเซลแบบ inline (คุณสมบัติ style บนโมดูลแต่ละอัน) รายการอนุญาตที่ใช้ hash จะทำงานได้แต่จะล้มเหลวในการอัปเดต style ใดๆ โดยไม่มีการ deploy การประนีประนอม: เรายอมรับนโยบาย style ที่อ่อนแอกว่าเล็กน้อย การจัดรูปแบบไม่สามารถขโมยข้อมูลได้อยู่แล้ว (CSS ไม่มีการเข้าถึงconnect-src)img-src 'self' data: blob: https:,data:สำหรับ QR render แบบ inlineblob:สำหรับ URL ดาวน์โหลดการส่งออกhttps:สำหรับ URL รูปภาพ vCard และอวตารที่ผู้ใช้ให้มา URL ที่ผู้ใช้ให้มาไม่เคยถูกเรียกใช้งาน เพียงแค่เรนเดอร์เท่านั้น โปรดทราบว่าคำสั่งนี้ยังเป็นช่องทางขาออกด้วย สคริปต์ใดๆ ที่ทำงานอยู่บนหน้าเว็บสามารถขอรูปภาพจากโฮสต์ HTTPS ใดก็ได้และใส่ข้อมูลลงใน URL เรายอมรับสิ่งนี้เพราะฟีเจอร์นี้จำเป็นต้องใช้ และเราขอบอกไว้ตรงนี้แทนที่จะอธิบายว่านโยบายนี้ปิดสนิททุกช่องทางconnect-src 'self' https: https://challenges.cloudflare.com, ข้อนี้ต้องอ่านอย่างละเอียดhttps:เป็น scheme source กล่าวคือมันอนุญาตให้fetch(), XMLHttpRequest, WebSocket ผ่าน TLS, EventSource และsendBeacon()เข้าถึงแหล่งต้นทาง HTTPS ใดก็ได้ ไม่ใช่แค่ที่คุณพิมพ์ไว้ มันมีไว้เพื่อให้ช่องกรอกรูปภาพ vCard และการตรวจสอบความปลอดภัยของ URL สามารถดึงที่อยู่ที่คุณวางลงไปได้ มันไม่ได้ป้องกันการขโมยข้อมูลโดยสคริปต์ที่เป็นอันตราย ไม่มีคำสั่ง CSP ใดทำได้เมื่อสคริปต์นั้นทำงานในฐานะเราอยู่แล้ว สิ่งที่ป้องกันการขโมยข้อมูลคือโค้ดที่เราส่งมอบไม่มีการเรียกขอแบบนั้น ซึ่งคุณสามารถดูได้ในแท็บ Networkframe-src 'self' https://challenges.cloudflare.com, มีเพียงวิดเจ็ต Turnstile เท่านั้นที่สามารถถูกฝังในเฟรมได้frame-ancestors 'none', ไม่มีเว็บไซต์อื่นสามารถฝังเราได้ ป้องกัน clickjackingbase-uri 'self', แท็ก<base>ที่ถูกฉีดเข้ามาอย่างเป็นอันตรายไม่สามารถเปลี่ยนเส้นทาง URL สัมพัทธ์ไปยังแหล่งต้นทางของผู้โจมตีได้form-action 'self', ฟอร์มที่ถูกฉีดเข้ามาสามารถส่งข้อมูลได้เฉพาะกลับไปยังแหล่งต้นทางของเรา แบบฟอร์มติดต่อเป็นฟอร์มเดียวที่ส่งข้อมูลไปที่ใดก็ตาม
สิ่งที่ CSP ทำไม่ได้
CSP จำกัดว่าเบราว์เซอร์จะโหลดโค้ดจากที่ใด แต่ไม่ได้ตรวจสอบว่าโค้ดที่โหลดมาทำอะไร หากแหล่งต้นทางของเราถูกบุกรุก ซึ่งเป็นข้อแรกในโมเดลภัยคุกคาม สคริปต์ของผู้โจมตีจะทำงานด้วยสิทธิ์เดียวกับของเรา รวมถึงสิทธิ์ connect-src และ img-src ข้างต้น การป้องกันของเราต่อกรณีนี้อยู่เหนือชั้นของ CSP: การ deploy ออกผ่านบัญชี Cloudflare บัญชีเดียวที่มี token จำกัดขอบเขต ชื่อไฟล์ทรัพย์สินถูกแฮชตามเนื้อหาเพื่อให้ไฟล์ที่ถูกสลับเปลี่ยน URL ที่อ้างอิงถึงทุกแห่ง และ service worker แคชเฉพาะ response GET แบบ same-origin เท่านั้น manifesto อธิบายเว็บไซต์ตามที่ถูกส่งมอบจริง ไม่ใช่คำกล่าวอ้างว่าไม่มีหน้าเว็บใดจะส่งข้อมูลออกไปได้เลย และเราอยากให้คุณยึดเราไว้กับข้อความที่แคบกว่าและตรวจสอบได้นี้แทน
นโยบาย CSP ที่แตกต่างกันมีผลใช้กับ /bio/* (ผ่อนคลาย img-src สำหรับอวตารที่ผู้ใช้ให้มา) และ /embed/* (ผ่อนคลาย frame-ancestors สำหรับการฝังโดยตั้งใจ) ทั้งสองมีเอกสารใน site/_headers
ส่วนหัวการขนส่งและการจัดกรอบ
- HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload, 1 ปี ทุกโดเมนย่อย มีสิทธิ์สำหรับรายการ preload HSTS การลดระดับเป็น HTTP จะถูกปฏิเสธโดยเบราว์เซอร์ที่ปฏิบัติตามมาตรฐาน - X-Frame-Options: DENY, ซ้ำซ้อนกับ CSP
frame-ancestorsเก็บไว้เพื่อรองรับเบราว์เซอร์รุ่นเก่า - X-Content-Type-Options: nosniff, ป้องกันการโจมตีแบบ MIME confusion
- Referrer-Policy: strict-origin-when-cross-origin, การคลิกลิงก์ขาออกจะรั่วไหลแหล่งต้นทางแต่ไม่รั่วไหลเส้นทาง
- Permissions-Policy:
camera=(self), microphone=(), geolocation=(), กล้องได้รับอนุญาตเฉพาะสำหรับเครื่องสแกนของเรา ไมโครโฟนและตำแหน่งถูกปฏิเสธอย่างชัดเจนแม้จะถูกฝังอยู่ก็ตาม
Service worker
Service worker ของเรา (site/sw.js) แคชเฉพาะทรัพย์สิน same-origin เท่านั้น ตัวจัดการ fetch ปฏิเสธคำขอ cross-origin และวิธีการที่ไม่ใช่ GET อย่างชัดเจน, คุณสามารถอ่านตรรกะได้ที่ /sw.js (ซอร์สโค้ดที่ส่งมอบไม่ได้ย่อขนาดจนอ่านไม่ออก) การเขียนแคชถูกห่อด้วย event.waitUntil() เพื่อไม่ให้ถูกละทิ้งระหว่างการนำทาง
การทำความสะอาดข้อมูลป้อนเข้า
ทุกเส้นทางการเรนเดอร์ที่รับข้อมูลป้อนเข้าของผู้ใช้จะถือว่ามันเป็นข้อความที่ไม่น่าเชื่อถือ:
- ตัวอย่าง payload QR ใช้
textContentไม่ใช่innerHTML - เป้าหมายการแชร์ (clipboard,
navigator.share()) ส่งผ่านข้อความผู้ใช้เป็นสตริง ไม่ใช่ markup - การส่งออก SVG ถูกสร้างจากไลบรารี encoder ของเรา เนื้อหาผู้ใช้ถูกเข้ารหัส base64 ลงใน
<image>xlink:hrefไม่ใช่การฉีดเป็นองค์ประกอบ SVG - ตัวอย่างการพิมพ์ใช้ blob URL ไม่ใช่
document.write() - การแยกวิเคราะห์ localStorage ถูกห่อด้วย
try/catch, รายการที่เสียหายจะให้ค่าเริ่มต้นว่างใหม่ ไม่ใช่ข้อยกเว้นที่อาจคลี่คลายไปสู่เส้นทางโค้ดที่ใช้งานอยู่ - URL ที่ผู้ใช้ให้มาซึ่งแสดงเป็นปุ่ม "เปิดลิงก์" ถูกจำกัดไว้ที่
http(s)://, รูปแบบjavascript:และdata:จะถูกปฏิเสธ
การดึงภาพ cross-origin
เมื่อผู้ใช้วาง URL https: เป็นรูปภาพ vCard หรือโลโก้ เบราว์เซอร์จะดึงข้อมูลโดยขึ้นอยู่กับ CORS และรายการอนุญาต img-src ของ CSP ของเรา ภาพถูกเรนเดอร์ลงใน canvas มันไม่เคยกลายเป็น DOM ที่ใช้งานอยู่ ไม่เคยทำงานเป็นโค้ด และไม่เคยไปถึงแหล่งต้นทางของเรา, การดึงข้อมูลคือเบราว์เซอร์ → ภาพระยะไกล และผลลัพธ์จะถูกวาดฝั่ง client ผู้โจมตีที่ควบคุม URL ภาพระยะไกลสามารถติดตามว่า URL ถูกโหลด (บรรทัด log บนเซิร์ฟเวอร์ของตนเอง) แต่ไม่สามารถขโมยข้อมูลใดๆ จากหน้าของเราได้
Subresource Integrity (SRI)
JavaScript และ CSS ทั้งหมดที่เราส่งมอบเป็น same-origin เราไม่โหลด script หรือ stylesheet ของบุคคลที่สาม ดังนั้น hash SRI จึงไม่มีผลบังคับใช้ หากเราโหลดทรัพย์สินของบุคคลที่สาม เราจะเพิ่มคุณสมบัติ SRI integrity ลงไปและบันทึกกระบวนการอัปเดต hash ในหน้านี้
การรายงานช่องโหว่
หากคุณค้นพบปัญหาด้านความปลอดภัยที่ส่งผลกระทบต่อ Abundera QR, ไม่ว่าจะในโค้ด การปรับใช้ หรือในการพึ่งพาที่เราจัดส่ง, โปรดรายงานอย่างเป็นส่วนตัวไปที่ security@abundera.ai เรามุ่งหมายที่จะจัดลำดับความสำคัญภายใน 72 ชั่วโมง คุณยังสามารถติดต่อเราผ่านรายละเอียดการติดต่อในไฟล์ /.well-known/security.txt ของเรา
ยังไม่มีโปรแกรมรางวัลบัก
เราไม่ได้เสนอรางวัลที่จ่ายเงินในปัจจุบัน แต่รายงานที่ถูกต้องทุกฉบับที่ได้รับการยืนยันจะได้รับเครดิตในบันทึกการเปลี่ยนแปลงและคำขอบคุณสาธารณะจากเรา
ตรวจสอบสิ่งที่กล่าวมาข้างต้น
ทุกคำอ้างในหน้านี้สามารถตรวจสอบได้จาก DevTools ของเบราว์เซอร์โดยไม่ต้องวางใจเรา:
- CSP: DevTools → Network →
/→ Headers อ่านส่วนหัว responseContent-Security-Policy - HSTS + ส่วนหัวความปลอดภัย: ที่เดียวกัน,
Strict-Transport-Security,X-Frame-Optionsฯลฯ ทั้งหมดมองเห็นได้ - ไม่มีการเรียกออกไปข้างนอก: DevTools → Network → Fetch/XHR สร้าง QR ดูจำนวนคงที่ที่ศูนย์
- ขอบเขต service worker: DevTools → Application → Service Workers ตรวจสอบแหล่งที่มาของ script และรายการทรัพย์สินที่แคชไว้
- ไม่มี cookie: DevTools → Application → Cookies ว่างเปล่า
- คำแนะนำฉบับสมบูรณ์: ส่วนการตรวจสอบของ manifesto
ติดต่อ
การเปิดเผยด้านความปลอดภัย: security@abundera.ai