主にプログラミングに関して。Python, .NET Framework(C#), JavaScript, その他いくらか。
記事にあるサンプルやコードは要検証。使用に際しては責任を負いかねます

スポンサーサイト

                
tags:
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

HTML5, JavaScript: Base64エンコードされた画像をアップロードする上で

                
 HTMLでimgタグを使って画像を表示したい場合、srcに画像のURLを入れる。これが近年ではcanvasタグやFile APIの導入にともなって、Base64エンコードされた文字列を画像データとしてsrcに入れるケースもブログなどで紹介されている。もしこれをユーザに任意の画像をアップロードさせるWebアプリに使うならXSSに気を払わなければならないという話。たとえばcanvasに画像を描いてもらって、それをtoDataURLメソッドで文字列化してアップロードして使うとか。

 実例として簡単なPythonサーバスクリプトを用意してみる。/postにアクセスするとフォームが用意されており、ここから画像ファイルをアップロードできる。アップロードしたファイルは成功していれば/profileで確認できる。SNSで自分のアイコン画像をアップロードすることをすごく簡単に模擬してみた。画像はDataURL形式で文字列化して使われている。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import wsgiref.simple_server

import tornado.web
import tornado.wsgi
import pymongo


class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hell!")

class IconPostHandler(tornado.web.RequestHandler):
def get(self):
html = """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
</head>
<body>
<input type="file" id="files" />
<button onclick="post();">post</button>
<script>
function post() {
var f = document.getElementById("files").files[0]; // FileList object

// Only process image files.
if (!f.type.match('image.*')) {
return;
}

var reader = new FileReader();

// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
$.post("/post", {imageData:e.target.result});
};
})(f);

// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
</script>
</body>
</html>"""
self.write(html)

def post(self):
image_data = self.get_argument("imageData")
db.user.save({"_id":"user1", "icon":image_data})

class ProfileHandler(tornado.web.RequestHandler):
def get(self):
user = db.user.find_one()
html = """<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
user ID: {userID}<br>
icon: <img width="50" src="{icon}" />
</body>
</html>""".format(userID=user["_id"], icon=user["icon"])
self.write(html)

db = pymongo.MongoClient("localhost", 27017).test_db

if __name__ == "__main__":
settings = dict(
debug=True,
)

application = tornado.wsgi.WSGIApplication([
(r"/", MainHandler),
(r"/post", IconPostHandler),
(r"/profile", ProfileHandler),
], **settings)

server = wsgiref.simple_server.make_server('', 8888, application)
server.serve_forever()


 /postにアクセスして画像をアップロードして/profileでそれを表示する。これは悪意のない優しい使いかた。ここでブラウザの開発者メニューからコンソールを開いて、下記の命令を入れてみる。
$.post("/post", {imageData:'" /><script>alert("foo");</script>'});
そんで/profileにアクセスしてみる。画像は表示されずに、アラートでfooという文字列が表示される。なにも考えずにDataURL形式で画像を取りまわしてHTMLに埋め込んだりしようとすると、任意のスクリプトが埋め込めてしまうということ。テンプレートエンジンのオートエスケープなどで防がれることもあるだろうが、うっかり画像ファイルデータならエスケープしなくてもいいだろうとオートエスケープを外してしまうかもしれない。
 対策としてエスケープしておけばいいが、そもそもDataURL形式で画像を扱うのはそれほどメリットになることでもない。データ量がバイナリに比べておおよそ30%増しだし、埋め込んでしまうことでキャッシュも効かなくなる。画像はDataURL形式で取りまわすことなく、バイト列で旧来どおりにやることがおそらく無難。どうしてもDataURL形式で取りまわしたいならXSS対策は打っておく必要がある。
            

コメントの投稿

非公開コメント

プロフィール

hMatoba

Author:hMatoba
Github

最新記事
リンク
作ったものなど
月別アーカイブ
カテゴリ
タグリスト

検索フォーム
Amazon
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。