Mengapa canvas.toDataURL () memunculkan pengecualian keamanan?

90

Apakah saya tidak cukup tidur atau apa? Kode berikut ini

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"));
}

melempar kesalahan ini:

SECURITY_ERR: DOM Exception 18

Tidak mungkin ini tidak berhasil! Adakah yang bisa menjelaskan ini?

pop850
sumber
2
Solusi itu tampaknya tidak berguna bagi orang yang tidak menggunakan Adobe AIR.
ObscureRobot

Jawaban:

68

Dalam spesifikasi tertulis:

Kapan pun metode toDataURL () dari elemen kanvas yang flag origin-clean-nya disetel ke false dipanggil, metode tersebut harus memunculkan pengecualian SECURITY_ERR.

Jika gambar tersebut berasal dari server lain, saya rasa Anda tidak dapat menggunakan toDataURL ()

Bob
sumber
6
Jika penyerang dapat menebak nama gambar yang Anda miliki di situs pribadi, dia bisa mendapatkan salinannya dengan mengecat di kanvas dan mengirim gambar baru ke situsnya. Pembatasan utama dari sudut pandang saya adalah untuk menghindari penggambaran konten dari situs lain, tetapi keamanan terlalu rumit karena penyerang dapat menemukan lubang di situs yang tidak terduga.
AlfonsoML
3
Perhatikan bahwa subdomain juga penting. Menurut pengalaman saya, di Chrome setidaknya, SECURITY_ERR: DOM Exception 18 muncul saat melakukan panggilan yang dianggap melintasi subdomain: 1. di example.com/some/path/index.html untuk video atau gambar di foo .example.com 2. saat membuka halaman yang sama seperti di 1 tetapi dengan memasukkan URL example.com/some/path/index.html dan kemudian mencoba memanggil toDataUrl () untuk video atau gambar di www.example.com
Sam Dutton
3
@AlfonsoML, mungkin saya salah, tetapi "Jika penyerang dapat menebak nama gambar yang Anda miliki di situs pribadi" mungkin dia akan mengambil gambar tersebut dengan format non-ikal dengan browser. Sudut pandang saya: Public URL Public Content.
kilianc
4
@ pop850 Saya menghadapi masalah ini bahkan ketika saya menggunakan URL data. Adakah cara saya dapat mengatasinya untuk URL data?
Sujay
6
@kilianc Pembatasan ini ada untuk mencegah penyerang menyebabkan browser Anda (dengan kuki otentikasi) untuk mengambil gambar dan mengirim isinya ke penyerang; penyerang tidak memiliki cookie Anda, jadi dia tidak dapat menggunakan curl untuk mendapatkan sumber daya yang sama dengan yang Anda dapatkan. "URL Publik, Konten Publik" adalah cara berpikir yang agak cacat: halaman Facebook saya memiliki komponen yang berhubungan dengan publik, tetapi banyak yang hanya dapat diakses dengan token otentikasi yang benar.
apsillers
18

Mengatur atribut lintas asal pada objek gambar berfungsi untuk saya (saya menggunakan 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';

Bagi mereka yang menggunakan fabricjs, berikut cara menambal 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;
};
Philip Nuzhnyy
sumber
Terima kasih, ini berfungsi untuk saya di Chrome. Saya tidak menggunakan fabric.js sekalipun
Philip007
Saya menggunakan fabricjs tetapi tidak bisa menyelesaikannya. Bagaimana Anda melakukan ini dengan kode ini? function wtd_load_bg_image (img_url) {if (img_url) {var bg_img = new Image (); 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
Bekerja di Firefox juga.
vanowm
16

Jika gambar dihosting di host yang menyetel Access-Control-Allow-Origin atau Access-Control-Allow-Credentials, Anda dapat menggunakan Cross Origin Resource Sharing (CORS). Lihat di sini (atribut crossorigin) untuk lebih jelasnya.

Pilihan Anda yang lain adalah server Anda memiliki titik akhir yang mengambil dan menyajikan gambar. (mis. http: // your_host / endpoint? url = URL ) Kelemahan dari pendekatan itu adalah latensi dan secara teoritis pengambilan tidak perlu.

Jika ada lebih banyak solusi alternatif, saya tertarik untuk mendengarnya.

James
sumber
Hai, saya baru saja melakukannya, saya memiliki Access-Control-Allow-Origin: * pada gambar, saya memiliki crossorigin = 'anonymous', lalu saya melukis gambar di atas kanvas, lalu saya memanggil toDataUrl dan saya masih mendapatkan SECURITY_ERR : Pengecualian DOM 18
skrat
13

Tampaknya ada cara untuk mencegahnya jika hosting gambar dapat menyediakan header HTTP berikut untuk sumber daya gambar dan browser mendukung CORS:

access-control-allow-origin: *
access-control-allow-credentials: true

Dinyatakan di sini: http://www.w3.org/TR/cors/#use-cases

DitherSky
sumber
1
access-control-allow-credentials: true tidak akan berfungsi dengan access-control-allow-origin: *. Jika yang pertama disetel, yang terakhir harus memiliki nilai asal, bukan nilai karakter pengganti dari developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
Silver Gonzales
4

Akhirnya saya menemukan solusinya. Hanya perlu menambahkan crossOriginsebagai parameter ketiga di fromURLfunc

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

Saya memiliki masalah yang sama dan semua gambar di-host di domain yang sama ... Jadi, jika seseorang mengalami masalah yang sama, berikut adalah cara saya mengatasinya:

Saya memiliki dua tombol: satu untuk menghasilkan kanvas dan satu lagi untuk menghasilkan gambar dari kanvas. Itu hanya bekerja untuk saya, dan maaf saya tidak tahu mengapa, ketika saya menulis semua kode pada tombol pertama. Jadi ketika saya mengkliknya menghasilkan kanvas dan gambar pada saat yang bersamaan ...

Saya selalu mengalami masalah keamanan ini ketika kode-kode tersebut memiliki fungsi yang berbeda ... = /

CarinaPilar
sumber
2

Saya bisa membuatnya bekerja menggunakan ini:

Tulis ini di baris pertama Anda .htaccessdi server sumber Anda

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

Kemudian saat membuat <img>elemen, lakukan sebagai berikut:

// 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');
Qwerty
sumber
1

Anda tidak dapat menempatkan spasi di ID Anda

Memperbarui

Dugaan saya adalah gambar itu ada di server yang berbeda dari tempat Anda menjalankan skrip. Saya dapat menduplikasi kesalahan Anda saat menjalankannya di halaman saya sendiri, tetapi berfungsi dengan baik saat saya menggunakan gambar yang dihosting di domain yang sama. Jadi ini terkait keamanan - letakkan gambar di situs Anda. Adakah yang tahu mengapa ini terjadi?

Mike Robinson
sumber
0

Jika Anda hanya menggambar beberapa gambar di kanvas, pastikan Anda memuat gambar dari domain yang sama.

www.example.com berbeda dengan example.com

Jadi pastikan gambar dan url yang Anda miliki di bilah alamat sama, www atau tidak.

JonathanC
sumber
0

Saya menggunakan fabric.js dan bisa menyelesaikannya dengan menggunakan toDatalessJSON daripada toDataURL:

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

Edit: Tidak apa-apa. Ini hanya menghasilkan gambar latar yang diekspor ke JPG, tanpa gambar di atasnya sehingga tidak sepenuhnya berguna.

horstwilhelm.dll
sumber