เหตุใด canvas.toDataURL () จึงมีข้อยกเว้นด้านความปลอดภัย


90

ฉันนอนหลับไม่เพียงพอหรืออะไร? รหัสต่อไปนี้

var frame=document.getElementById("viewer");
frame.width=100;
frame.height=100;

var ctx=frame.getContext("2d");
var img=new Image();
img.src="http://www.ansearch.com/images/interface/item/small/image.png"

img.onload=function() {
    // draw image
    ctx.drawImage(img, 0, 0)

    // Here's where the error happens:
    window.open(frame.toDataURL("image/png"));
}

กำลังแสดงข้อผิดพลาดนี้:

SECURITY_ERR: DOM Exception 18

ไม่มีทางที่จะไม่ได้ผล! ใครช่วยอธิบายเรื่องนี้หน่อยได้ไหม?



2
วิธีนี้ดูเหมือนจะไม่มีประโยชน์สำหรับผู้ที่ไม่ได้ใช้ Adobe AIR
ObscureRobot

คำตอบ:


68

ในรายละเอียดระบุว่า:

เมื่อใดก็ตามที่เมธอด toDataURL () ขององค์ประกอบแคนวาสที่ตั้งค่าแฟล็กการทำความสะอาดต้นทางเป็นเท็จจะถูกเรียกเมธอดนั้นจะต้องเพิ่มข้อยกเว้น SECURITY_ERR

หากอิมเมจมาจากเซิร์ฟเวอร์อื่นฉันไม่คิดว่าคุณจะใช้ toDataURL () ได้


6
หากผู้โจมตีสามารถเดาชื่อรูปภาพที่คุณมีในไซต์ส่วนตัวได้เขาจะสามารถรับสำเนาได้โดยการวาดลงบนผืนผ้าใบและส่งภาพใหม่ไปยังไซต์ของเขา ข้อ จำกัด หลักจากมุมมองของฉันคือการหลีกเลี่ยงการดึงเนื้อหาของไซต์อื่น แต่การรักษาความปลอดภัยนั้นซับซ้อนเกินไปเนื่องจากผู้โจมตีสามารถค้นหาช่องโหว่ในไซต์ที่ไม่คาดคิดได้
AlfonsoML

3
โปรดทราบว่าโดเมนย่อยมีความสำคัญเช่นกัน จากประสบการณ์ของฉันอย่างน้อยที่สุดใน Chrome SECURITY_ERR: DOM Exception 18 จะเพิ่มขึ้นเมื่อทำการโทรที่ถูกมองข้ามโดเมนย่อย: 1. ในexample.com/some/path/index.htmlสำหรับวิดีโอหรือรูปภาพใน foo .example.com 2. เมื่อไปที่หน้าเดียวกับใน 1 แต่ป้อน URL example.com/some/path/index.htmlจากนั้นพยายามเรียก toDataUrl () สำหรับวิดีโอหรือรูปภาพใน www.example.com
Sam Dutton

3
@AlfonsoML ฉันอาจจะคิดผิด แต่ "ถ้าผู้โจมตีสามารถเดาชื่อของรูปภาพที่คุณมีในไซต์ส่วนตัวได้" เขาอาจจะดึงรูปภาพโดยไม่ใช้เบราว์เซอร์แบบม้วนงอ มุมมองของฉัน: เนื้อหาสาธารณะ URL สาธารณะ
kilianc

4
@ pop850 ฉันประสบปัญหานี้แม้ว่าฉันจะใช้ URL ข้อมูลก็ตาม มีวิธีแก้ปัญหานี้สำหรับ URL ข้อมูลหรือไม่
Sujay

6
@kilianc ข้อ จำกัด นี้มีไว้เพื่อป้องกันผู้โจมตีไม่ให้เบราว์เซอร์ของคุณ (ด้วยคุกกี้การตรวจสอบสิทธิ์) ดึงภาพและส่งเนื้อหาไปยังผู้โจมตี ผู้โจมตีไม่มีคุกกี้ของคุณดังนั้นเขาจึงไม่สามารถใช้ curl เพื่อรับทรัพยากรเดียวกับที่คุณได้รับอนุญาต "URL สาธารณะเนื้อหาสาธารณะ" เป็นวิธีคิดที่ค่อนข้างมีข้อบกพร่อง: หน้า Facebook ของฉันมีส่วนประกอบที่เปิดเผยต่อสาธารณะ แต่มีหลายอย่างที่เข้าถึงได้ด้วยโทเค็นการตรวจสอบสิทธิ์ที่ถูกต้องเท่านั้น
apsillers

18

การตั้งค่าแอตทริบิวต์ cross origin บนวัตถุรูปภาพใช้ได้ผลสำหรับฉัน (ฉันใช้ fabricjs)

    var c = document.createElement("img");
    c.onload=function(){
        // add the image to canvas or whatnot
        c=c.onload=null
    };
    c.setAttribute('crossOrigin','anonymous');
    c.src='http://google.com/cat.png';

สำหรับผู้ที่ใช้ fabricjs นี่คือวิธีการแพตช์ Image.fromUrl

// patch fabric for cross domain image jazz
fabric.Image.fromURL=function(d,f,e){
    var c=fabric.document.createElement("img");
    c.onload=function(){
        if(f){f(new fabric.Image(c,e))}
        c=c.onload=null
    };
    c.setAttribute('crossOrigin','anonymous');
    c.src=d;
};

ขอบคุณมันใช้ได้กับฉันใน Chrome ฉันไม่ได้ใช้ fabric.js
Philip007

ฉันใช้ fabricjs แต่ไม่สามารถแก้ได้ คุณจะทำอย่างไรกับรหัสนี้ ฟังก์ชัน wtd_load_bg_image (img_url) {if (img_url) {var bg_img = รูปภาพใหม่ (); bg_img.onload = function () {canvasObj.setBackgroundImage (bg_img.src, canvasObj.renderAll.bind (canvasObj), {originX: 'left', originY: 'top', left: 0, top: 0}); }; bg_img.src = img_url; }}
HOY

ทำงานใน Firefox เช่นกัน
vanowm

16

หากอิมเมจถูกโฮสต์บนโฮสต์ที่ตั้งค่า Access-Control-Allow-Origin หรือ Access-Control-Allow-Credentials คุณสามารถใช้ Cross Origin Resource Sharing (CORS) ได้ ดูที่นี่ (แอตทริบิวต์ crossorigin) สำหรับรายละเอียดเพิ่มเติม

ตัวเลือกอื่นของคุณคือให้เซิร์ฟเวอร์ของคุณมีปลายทางที่ดึงข้อมูลและแสดงภาพ (เช่นhttp: // your_host / endpoint? url = URL ) ข้อเสียของวิธีการนั้นคือเวลาแฝงและการดึงข้อมูลที่ไม่จำเป็นในทางทฤษฎี

หากมีทางเลือกอื่นเพิ่มเติมฉันสนใจที่จะรับฟัง


สวัสดีฉันทำอย่างนั้นฉันมี Access-Control-Allow-Origin: * ในรูปภาพฉันมี crossorigin = 'anonymous' จากนั้นฉันวาดภาพบนผ้าใบจากนั้นฉันโทรไปที่ DataUrl และฉันยังคงได้รับ SECURITY_ERR : DOM Exception 18
skrat

13

ดูเหมือนจะมีวิธีป้องกันว่าหากโฮสต์รูปภาพสามารถให้ส่วนหัว HTTP ต่อไปนี้สำหรับทรัพยากรรูปภาพและเบราว์เซอร์รองรับ CORS:

การควบคุมการเข้าถึงอนุญาตที่มา: *
access-control-allow-credentials: true

มีระบุไว้ที่นี่: http://www.w3.org/TR/cors/#use-cases


1
access-control-allow-credentials: true จะไม่ทำงานกับ access-control-allow-origin: * เมื่ออดีตถูกตั้งค่าหลังควรมีค่าต้นทางไม่ใช่ค่าตัวแทนจากdeveloper.mozilla.org/en-US/docs/HTTP/Access_control_CORS
Silver Gonzales

4

ในที่สุดฉันก็พบวิธีแก้ปัญหา เพียงแค่ต้องเพิ่มcrossOriginพารามิเตอร์เป็นตัวที่สามในfromURLfunc

fabric.Image.fromURL(imageUrl, function (image) {
            //your logic
    }, { crossOrigin: "Anonymous" });

2

ฉันมีปัญหาเดียวกันและรูปภาพทั้งหมดโฮสต์อยู่ในโดเมนเดียวกัน ... ดังนั้นหากมีคนประสบปัญหาเดียวกันนี่คือวิธีที่ฉันแก้ไข:

ฉันมีสองปุ่ม: ปุ่มหนึ่งสำหรับสร้างผืนผ้าใบและอีกปุ่มหนึ่งสำหรับสร้างภาพจากผืนผ้าใบ มันใช้ได้ผลสำหรับฉันเท่านั้นและขออภัยที่ฉันไม่รู้ว่าทำไมเมื่อฉันเขียนโค้ดทั้งหมดบนปุ่มแรก ดังนั้นเมื่อฉันคลิกมันจะสร้างผืนผ้าใบและภาพในเวลาเดียวกัน ...

ฉันมักจะมีปัญหาด้านความปลอดภัยนี้เสมอเมื่อรหัสอยู่ในฟังก์ชันต่างๆ ... = /


2

ฉันสามารถทำให้มันใช้งานได้โดยใช้สิ่งนี้:

เขียนสิ่งนี้ในบรรทัดแรกของคุณ.htaccessบนเซิร์ฟเวอร์ต้นทางของคุณ

Header add Access-Control-Allow-Origin "*"

จากนั้นเมื่อสร้าง<img>องค์ประกอบให้ทำดังนี้:

// jQuery
var img = $('<img src="http://your_server/img.png" crossOrigin="anonymous">')[0]

// or pure
var img = document.createElement('img');
img.src='http://your_server/img.png';
img.setAttribute('crossOrigin','anonymous');

1

คุณไม่สามารถเว้นวรรคใน ID ของคุณ

อัปเดต

ฉันเดาว่ารูปภาพนั้นอยู่บนเซิร์ฟเวอร์อื่นที่ไม่ใช่ที่ที่คุณเรียกใช้สคริปต์ ฉันสามารถทำซ้ำข้อผิดพลาดของคุณได้เมื่อเรียกใช้บนเพจของฉันเอง แต่มันก็ใช้ได้ดีเมื่อฉันใช้รูปภาพที่โฮสต์บนโดเมนเดียวกัน ดังนั้นจึงเกี่ยวข้องกับความปลอดภัย - วางภาพบนไซต์ของคุณ ใครทราบว่าทำไมถึงเป็นเช่นนี้?


0

หากคุณกำลังวาดภาพบางภาพบนผืนผ้าใบตรวจสอบให้แน่ใจว่าคุณกำลังโหลดภาพจากโดเมนเดียวกัน

www.example.comแตกต่างจากexample.com

ดังนั้นตรวจสอบให้แน่ใจว่ารูปภาพและ URL ที่คุณมีในแถบที่อยู่นั้นเหมือนกัน www หรือไม่


0

ฉันใช้ fabric.js และสามารถแก้ไขปัญหานี้ได้โดยใช้ toDatalessJSON แทน toDataURL:

canvas.toDatalessJSON({ format: 'jpeg' }).objects[0].src

แก้ไข: ไม่เป็นไร สิ่งนี้ส่งผลให้มีเพียงภาพพื้นหลังที่ถูกส่งออกไปยัง JPG โดยไม่มีภาพวาดด้านบนดังนั้นจึงไม่มีประโยชน์เลย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.