{
    "componentChunkName": "component---src-templates-blog-post-jsx",
    "path": "/post/how-to-disable-limitation-of-cors-in-html5-canvas/",
    "result": {"data":{"site":{"siteMetadata":{"title":"WEB EGG","author":"Leko - CTO at Yuimedi"}},"markdownRemark":{"id":"5e95b21a-9819-56c3-a0dd-af669e7dbc3e","excerpt":"こんにちは。 画像をくっつけるツールというjsで簡単な画像処理を行うSPAを作った時に、 URLを指定して画像を読み込んで結合する、という要件があり、 この要件とcanvas周りでハマったので対象方法を残します。 何が起きたか、なぜ起きるか URLをimgタグのsrc属性にセット 画像の読込が完了したらcanvas…","html":"<p>こんにちは。<br>\n<a href=\"http://img-concater.herokuapp.com\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">画像をくっつけるツール</a>というjsで簡単な画像処理を行うSPAを作った時に、</p>\n<p>URLを指定して画像を読み込んで結合する、という要件があり、<br>\nこの要件とcanvas周りでハマったので対象方法を残します。</p>\n<!--more-->\n<h2 id=\"何が起きたかなぜ起きるか\" style=\"position:relative;\"><a href=\"#%E4%BD%95%E3%81%8C%E8%B5%B7%E3%81%8D%E3%81%9F%E3%81%8B%E3%81%AA%E3%81%9C%E8%B5%B7%E3%81%8D%E3%82%8B%E3%81%8B\" aria-label=\"何が起きたかなぜ起きるか permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>何が起きたか、なぜ起きるか</h2>\n<ol>\n<li>URLをimgタグのsrc属性にセット</li>\n<li>画像の読込が完了したらcanvasに描画</li>\n<li>canvas.toDataURL()でdata uriに変換</li>\n</ol>\n<p>という流れで処理をしようと目論んでいたのですが、3でエラーが起きました。<br>\n<code>Unable to get image data from canvas because the canvas has been tainted by cross-origin data.</code></p>\n<blockquote>\n<p><a href=\"https://developer.mozilla.org/ja/docs/Web/HTML/CORS_enabled_image\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">CORS Enabled Image</a></p>\n</blockquote>\n<p>上記の記事に解説がありますが、外部ドメインのデータによってcanvasが汚染されている、というエラーでした。</p>\n<blockquote>\n<p><a href=\"http://tech-sketch.jp/2013/05/s3corscanvas.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">S3上の画像をCORSを利用してCanvasで使う</a></p>\n</blockquote>\n<p>上記の記事のように、画像サーバでCORSをわざわざ有効にしてくれるパターンはごく稀で、大体の場合この制限に引っかります。</p>\n<h2 id=\"どう対処するか\" style=\"position:relative;\"><a href=\"#%E3%81%A9%E3%81%86%E5%AF%BE%E5%87%A6%E3%81%99%E3%82%8B%E3%81%8B\" aria-label=\"どう対処するか permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>どう対処するか</h2>\n<ul>\n<li>返ってくるのはjsではなく画像のバイナリなのでjsonpは使えない</li>\n<li>imgタグに入れても外部URLならこの制限に引っかかる</li>\n</ul>\n<p>ということで、自分のサーバをプロキシとして扱い、</p>\n<ul>\n<li>jsからUrlを送信</li>\n<li>サーバ(Go)でそのURLの画像を取得しバイナリをレスポンス</li>\n<li>jsでバイナリを画像に変換</li>\n</ul>\n<p>として、自分のサーバから返されたバイナリなので制限を突破する、という作戦で行きます。</p>\n<p>もちろんセキュリティ上の制限を無理やり超えるので、ブラウザの脆弱性を突かれる可能性があります。ご留意の上ご使用下さい。<br>\n以下に具体的な手段を載せます。</p>\n<blockquote>\n<p>今回の場合、セッションを持っておらず、URLパラメータに対しても反応しないので、悪意のあるURLを誰かに踏ませることは出来ないので自分で悪意のある入力をして自分に攻撃をすることしかできない。iframe埋め込みもさせないので、他人のPCへ攻撃はできない。という仮定で作っています。<br>\nもしレスポンスを受け取ってechoするだけの処理に脆弱性があるならサーバ側がやられます。が仮にやられてもデータを保持しておらず、ソースコードは公開しているし、怖いのは改竄くらいですが、定期的にコンテナの破棄+gitで管理されたソースでのコンテナ再構築 + 再起動がかかるので、まぁ大丈夫だろうという推定で動いています。</p>\n</blockquote>\n<h2 id=\"生xhrを使用する-or-jqueryにオプションを足す\" style=\"position:relative;\"><a href=\"#%E7%94%9Fxhr%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B-or-jquery%E3%81%AB%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%B6%B3%E3%81%99\" aria-label=\"生xhrを使用する or jqueryにオプションを足す permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>生XHRを使用する or jQueryにオプションを足す</h2>\n<p>バイナリのレスポンスを受け取るには、XHRのresponseTypeに<code>arraybuffer</code>を使用する必要があります。<br>\n出ないと文字化けした良く分からないテキスト、がレスポンスになってしまいます。</p>\n<p>なおAjaxのライブラリ(jQueryや<a href=\"https://github.com/visionmedia/superagent\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">superagent</a>あたり)を使用している場合注意が必要です。</p>\n<blockquote>\n<p><a href=\"http://qiita.com/tom_konda/items/484955b8332e0305ebc4\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">バイナリファイルをAjaxで取得する際に注意する点</a></p>\n</blockquote>\n<p>なおソース読んでみた結果、superagentでarraybugferの指定は現状使用できないです。</p>\n<h2 id=\"画像urlをgoに投げ画像を取得しバイナリをレスポンス\" style=\"position:relative;\"><a href=\"#%E7%94%BB%E5%83%8Furl%E3%82%92go%E3%81%AB%E6%8A%95%E3%81%92%E7%94%BB%E5%83%8F%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%82%92%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B9\" aria-label=\"画像urlをgoに投げ画像を取得しバイナリをレスポンス permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>画像URLをGoに投げ画像を取得しバイナリをレスポンス</h2>\n<p>Goで実装してますが、サーバ側は何の言語でも同じです。URLに対応する画像の中身を取得し、バイナリのレスポンスを吐きます。</p>\n<h2 id=\"jsでバイナリを受け取ってblob化\" style=\"position:relative;\"><a href=\"#js%E3%81%A7%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%81%A3%E3%81%A6blob%E5%8C%96\" aria-label=\"jsでバイナリを受け取ってblob化 permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>jsでバイナリを受け取ってBlob化</h2>\n<p>responseType: arraybufferで受け取ったレスポンスをバイナリに変換します。</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">xhr<span class=\"token punctuation\">.</span><span class=\"token function-variable function\">onreadystatechange</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// 判定略</span>\n  <span class=\"token keyword\">var</span> buff <span class=\"token operator\">=</span> xhr<span class=\"token punctuation\">.</span>response<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">var</span> blob <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Blob</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">[</span>buff<span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> type<span class=\"token operator\">:</span> <span class=\"token operator\">...</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>引数はArraybufferの配列なのでご注意下さい。<br>\n配列になっているのは複数のArrayBufferを指定できるためだそうです。</p>\n<h2 id=\"blobをimgタグのsrcに指定する\" style=\"position:relative;\"><a href=\"#blob%E3%82%92img%E3%82%BF%E3%82%B0%E3%81%AEsrc%E3%81%AB%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B\" aria-label=\"blobをimgタグのsrcに指定する permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Blobをimgタグのsrcに指定する</h2>\n<p><a href=\"https://developer.mozilla.org/ja/docs/Web/API/URL/createObjectURL\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">window.URL.createObjectURL()</a>を使用して、バイナリをsrc属性に指定可能な形式に変換します。<br>\nこのURLはいつでも使えるパーマリンクではなく、そのページを開いている間のみ有効なものなので、リロードすると使えなくなります。</p>\n<h2 id=\"drawimage\" style=\"position:relative;\"><a href=\"#drawimage\" aria-label=\"drawimage permalink\" class=\"autolink-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>drawImage</h2>\n<p>Imageオブジェクトに自分のサーバから返されたバイナリ(画像)がセットされたので、あとは普通にcanvasで扱うだけです。</p>\n<blockquote>\n<p><a href=\"http://www.html5.jp/canvas/ref/method/drawImage.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">drawImage() メソッド – Canvasリファレンス – HTML5.JP</a></p>\n</blockquote>\n<p>CORS制限を回避したので、canvas内で加工した画像を書き出すことができるようになっています。</p>","timeToRead":5,"frontmatter":{"title":"canvasのCORS制限を突破する","tags":["HTML5 Canvas","JavaScript","security"],"date":"December 15, 2015","featuredImage":null}}},"pageContext":{"slug":"/how-to-disable-limitation-of-cors-in-html5-canvas/","previous":{"fields":{"slug":"/refactored-ansible-settings-to-setup-golang/"},"frontmatter":{"title":"Goの環境を作るAnsibleの設定をリファクタした","tags":["Ansible","Go"]}},"next":{"fields":{"slug":"/how-to-download-with-a-tag-without-file-server/"},"frontmatter":{"title":"aタグのdownload属性でサーバを介さずにファイルダウンロードする","tags":["HTML5 Canvas"]}}}},
    "staticQueryHashes": ["2585454260","2954598359"]}