アップロードされるファイルのファイル名の注意点

アップロードされたファイルを簡単に保存する方法 では、 PostedFile の FileName プロパティからファイル名を取って、それにファイルの保存ディレクトリを足し合わせることでパスを作りました。

以前のコードのパスを作るところはここです。

string path = @"C:\temp";
string fileName = FileUpload1.PostedFile.FileName;
string filePath = Path.Combine(path, fileName);

今回は、このコードは危ないですよ、というお話です。

「大丈夫、こんなコード書かないよ」 っていう人は、このページは役に立たないので、他のページをお読みください(笑)

さて、上記のコードがどうして危ないかというと、ファイル名が設定されない、あるいは不正に設定される可能性があるからです。

ファイル名はどこでセットされるか

そもそも、ファイル名はどうやってブラウザからサーバーに渡されるのか、おさらいしてみましょう。

HTML フォームからポストされるファイルは、次のようなポストデータの中で渡されます。

フォームベースアップロード

(注: この図は フォームベース・ファイルアップロードの仕組み の図です。 図中に IE はフルパスをセットする、とありますが最近の IE はファイル名だけのようです)

具体的にいえば、今回の場合は次のような POST リクエストがサーバーに送られます。

POST /vwd/FileUploadTest1.aspx HTTP/1.1
Content-Type: multipart/form-data, boundary=AAA
Host: localhost
Accept: */*
Content-Length: ...
Connection: Keep-Alive

--AAA
Content-Disposition: form-data; name="__VIEWSTATE"

/wEPKIjqEPyDsnr ... (省略)... VdefVbuuAKOSvuIe9b0=
--AAA
Content-Disposition: form-data; name="__EVENTVALIDATION"

/wEWAgK ... (省略) ... T8Nb+AwK
--AAA
Content-Disposition: form-data; name="FileUpload1"; filename="Test1.txt"
Content-Type: text/plain

Hello, world!
--AAA
Content-Disposition: form-data; name="Button1"

Upload
--AAA--

値がセットされない可能性も無いわけではない

__VIEWSTATE と __EVENTVALIDATION は、ASP.NET が内部的に自動的にセットするものです。 ファイルのデータは、その次の FileUpload1 のパートです。 ここでは、"Hello, world!" という文字の書いたテキストファイルをアップロードしています。 そのパートのヘッダ部分に、filename としてファイル名がセットされています。

実はこの値はブラウザが、オリジナルのファイル名を基にして自動的にセットしているのです。 全てのブラウザがちゃんと値をセットしてくれたらいいですが、ブラウザ(一般に Web クライアント)によってはファイル名をセットしない可能性もあるわけです。

PostedFile の FileName プロパティはここで指定された filename 属性を素直に受取りますから、 クライアントから空っぽのデータが filename としてセットされていれば空になるわけです。

これが危ないですよ、という根拠のひとつめです。メジャーなブラウザをユーザが普通に使っている限り、 値がセットされないことは無いと思いますが、必ずセットされると思っていてはダメだ、ということです。

意図したディレクトリ以外に保存する可能性もある

さらに、上のコードは潜在的なセキュリティの問題もあります。

親のディレクトリをたぐれるようなコードは、しばしばセキュリティホールとなります。 上のように FileName (すなわちユーザーの入力) をそのままパス名に結合すると、親パスに到達できてしまいます。

例えば、filename 属性に "..\a.txt" という値をセットするとどうなるでしょうか。

私が実験したところ、次のように FileName 属性には素直に "..\a.txt" がセットされ、 それを C:\temp というディレクトリ名と結合した結果、C:\a.txt として書き込みを試みました。

ASP.NET のファイルアップロード

C:\ には幸い書き込みが許可されていなかったために、上では例外が発生しましたが、設定によっては書き込めてしまうことになります。

このように、ブラウザが裏でセットする値は意外とテストも面倒くさくて確認を怠りがちですが、 十分気をつけてください。

一番いいのは、前にも書きましたがサーバー側で安全なファイル名を振りなおすことです。FileName プロパティは参考程度にみるべきです。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 ASP.NET 入門