Warum löst canvas.toDataURL () eine Sicherheitsausnahme aus?

90

Habe ich nicht genug geschlafen oder was? Dieser folgende Code

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

wirft diesen Fehler:

SECURITY_ERR: DOM Exception 18

Das sollte auf keinen Fall funktionieren! Kann das bitte jemand erklären?

pop850
quelle
2
Diese Lösung scheint für Benutzer von Adobe AIR nicht sinnvoll zu sein.
ObscureRobot

Antworten:

67

In den Spezifikationen heißt es:

Immer wenn die toDataURL () -Methode eines Canvas-Elements aufgerufen wird, dessen origin-clean-Flag auf false gesetzt ist, muss die Methode eine SECURITY_ERR-Ausnahme auslösen.

Wenn das Image von einem anderen Server stammt, können Sie toDataURL () nicht verwenden.

Bob
quelle
6
Wenn ein Angreifer in der Lage ist, den Namen eines Bildes zu erraten, das Sie auf einer privaten Site haben, kann er eine Kopie davon erhalten, indem er auf eine Leinwand malt und das neue Bild an seine Site sendet. Aus meiner Sicht besteht die Haupteinschränkung darin, das Zeichnen des Inhalts einer anderen Site zu vermeiden. Die Sicherheit ist jedoch zu komplex, da der Angreifer eine Lücke in einer unerwarteten Site finden kann.
AlfonsoML
3
Beachten Sie, dass auch die Subdomain wichtig ist. Nach meiner Erfahrung wird zumindest in Chrome eine SECURITY_ERR: DOM-Ausnahme 18 ausgelöst, wenn ein Anruf getätigt wird, der als subdomänenübergreifend wahrgenommen wird: 1. in example.com/some/path/index.html für ein Video oder Bild in foo .example.com 2. Wenn Sie zur gleichen Seite wie in 1 gehen, aber die URL example.com/some/path/index.html eingeben und dann versuchen, toDataUrl () für ein Video oder Bild in www.example.com aufzurufen
Sam Dutton
3
@AlfonsoML, vielleicht irre ich mich, aber "Wenn ein Angreifer in der Lage ist, den Namen eines Bildes zu erraten, das Sie auf einer privaten Site haben", wird er das Bild wahrscheinlich mit einer Locke ohne Browser erfassen. Mein Standpunkt: Öffentliche URL Öffentlicher Inhalt.
Kilianc
4
@ pop850 Ich bin mit diesem Problem konfrontiert, auch wenn ich eine Daten-URL verwende. Gibt es eine Möglichkeit, dies für Daten-URLs zu umgehen?
Sujay
5
@kilianc Diese Einschränkung soll verhindern, dass ein Angreifer Ihren Browser (mit Authentifizierungscookies) veranlasst, ein Bild abzurufen und dessen Inhalt an einen Angreifer zu senden. Der Angreifer hat Ihre Cookies nicht, daher kann er Curl nicht verwenden, um dieselben Ressourcen zu erhalten, zu denen Sie berechtigt sind. "Öffentliche URL, öffentlicher Inhalt" ist eine ziemlich fehlerhafte Denkweise: Meine Facebook-Seite enthält öffentlich zugängliche Komponenten, aber es gibt vieles, auf das nur mit dem richtigen Authentifizierungstoken zugegriffen werden kann.
Apsiller
18

Das Festlegen des Cross-Origin-Attributs für die für mich verwendeten Bildobjekte (ich habe FabricJs verwendet)

    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';

Wenn Sie FabricJs verwenden, finden Sie hier Informationen zum Patchen von 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
quelle
Danke, es funktioniert bei mir in Chrome. Ich benutze allerdings keine Fabric.js
Philip007
Ich benutze Fabricjs, konnte es aber nicht lösen. Wie würden Sie das mit diesem Code machen? Funktion wtd_load_bg_image (img_url) {if (img_url) {var bg_img = neues Bild (); 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
Funktioniert auch in Firefox.
Vanowm
16

Wenn das Image auf einem Host gehostet wird, auf dem entweder Access-Control-Allow-Origin oder Access-Control-Allow-Credentials festgelegt sind, können Sie Cross Origin Resource Sharing (CORS) verwenden. Weitere Informationen finden Sie hier (das Crossorigin-Attribut).

Sie können auch festlegen, dass Ihr Server über einen Endpunkt verfügt, der ein Image abruft und bereitstellt. (zB http: // your_host / endpoint? url = URL ) Der Nachteil dieses Ansatzes ist die Latenz und das theoretisch unnötige Abrufen.

Wenn es mehr alternative Lösungen gibt, wäre ich daran interessiert, davon zu hören.

James
quelle
Hallo, ich habe genau das getan, ich habe Access-Control-Allow-Origin: * auf den Bildern, ich habe crossorigin = 'anonym', dann male ich das Bild auf Leinwand, rufe dann DataUrl auf und erhalte immer noch SECURITY_ERR : DOM Ausnahme 18
skrat
13

Es scheint eine Möglichkeit zu geben, dies zu verhindern, wenn das Image-Hosting die folgenden HTTP-Header für die Bildressourcen bereitstellen kann und der Browser CORS unterstützt:

Zugriffskontrolle-Zulassungsursprung: *
Zugangskontrolle-Zulassungsanmeldeinformationen: true

Es wird hier angegeben: http://www.w3.org/TR/cors/#use-cases

DitherSky
quelle
1
Zugangskontrolle-Zulassungsanmeldeinformationen: true funktioniert nicht mit Zugriffskontrolle-Zulassungsursprung: *. Wenn Ersteres festgelegt ist, sollte Letzteres einen Ursprungswert haben, keinen Platzhalterwert von developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
Silver Gonzales
4

Endlich habe ich die Lösung gefunden. Nur muss der hinzufügen crossOriginals dritte param in fromURLfunc

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

Ich hatte das gleiche Problem und alle Bilder werden in derselben Domain gehostet ... Wenn also jemand das gleiche Problem hat, habe ich Folgendes gelöst:

Ich hatte zwei Schaltflächen: eine zum Generieren der Leinwand und eine zum Generieren des Bildes aus der Leinwand. Es hat nur bei mir funktioniert, und es tut mir leid, dass ich nicht weiß warum, als ich den gesamten Code auf den ersten Button geschrieben habe. Wenn ich darauf klicke, werden gleichzeitig die Leinwand und das Bild generiert ...

Ich habe immer dieses Sicherheitsproblem, wenn die Codes unterschiedliche Funktionen hatten ... = /

CarinaPilar
quelle
2

Ich konnte es damit zum Laufen bringen:

Schreiben Sie dies in die erste Zeile Ihres .htaccessauf Ihrem Quellserver

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

Gehen Sie dann beim Erstellen eines <img>Elements wie folgt vor:

// 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
quelle
1

Sie können keine Leerzeichen in Ihre ID einfügen

Aktualisieren

Ich vermute, dass sich das Image auf einem anderen Server befindet als auf dem Sie das Skript ausführen. Ich konnte Ihren Fehler duplizieren, als ich ihn auf meiner eigenen Seite ausführte, aber er funktionierte einwandfrei, als ich ein Bild verwendete, das auf derselben Domain gehostet wurde. Es ist also sicherheitsrelevant - platzieren Sie das Bild auf Ihrer Website. Weiß jemand warum das so ist?

Mike Robinson
quelle
0

Wenn Sie einfach einige Bilder auf eine Leinwand zeichnen, stellen Sie sicher, dass Sie die Bilder aus derselben Domäne laden.

www.example.com unterscheidet sich von example.com

Stellen Sie also sicher, dass Ihre Bilder und die URL in Ihrer Adressleiste identisch sind, www oder nicht.

JonathanC
quelle
0

Ich verwende Fabric.js und könnte dies beheben, indem ich toDatalessJSON anstelle von toDataURL verwende:

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

Edit: Nevermind. Dies führt dazu, dass nur das Hintergrundbild in JPG exportiert wird, ohne dass die Zeichnung oben angezeigt wird, sodass es schließlich nicht ganz nützlich war.

horstwilhelm
quelle