はじめに
前の記事で、Sophos Antivirus for Linux Free Edition を CentOS にインストールしました。折角なので、今度はアップロードしたファイルを、PHP から Sophos でウィルススキャンする方法をご紹介します。
PHP から Sophos でウィルススキャン
Sophos でウィルススキャンした結果を受け取るには、CentOS のコマンドラインで以下のように2行コマンドを実行する必要があります。
savscan filepath echo $?
PHP から CentOS のコマンドを実行するには、system 関数と exec 関数がありますが、実行結果を受け取りたいので、exec 関数を使用します。また、2行コマンドを実行するので、; でつないで1行にする必要があります。
具体的には、以下のようになります。
exec("savscan $tmpfile; echo $?", $output, $result);
$output に Sophos のウィルススキャン実行結果が格納されます。
ですが、2行コマンドを実行していることで、2行分の実行結果が $output に格納されてしまうので、end 関数を使用して、最後の結果のみ取得します。これで、ウィルススキャンの実行結果を判断するようにします。
なお、実行結果は以下のようになります。
0 | エラー、ウィルスの検出ともになし |
1 | ユーザーが「Ctrl + C」を押して検索を中断した |
2 | エラーが発生したため検索が中断した |
3 | ウィルスが検出された |
PHP でファイルのアップロード
PHP でファイルのアップロードを行うには、注意点が多いのですが、詳しくは参考書籍、参考サイトをご確認ください。一応、ソースコードにコメントをつけてあります。
なお、ウィルススキャンをサーバー上の一時ファイルに対して行っているのは、格納後にウィルススキャンをしたくなかったこと、exec コマンドからの実行では Sophos はウィルスの駆除ができないこと、一時ファイルは処理を抜けると自動的に削除されることが理由です。
ソースコード
■upload.php
<?php session_start(); header("Content-type: text/html; charset=utf-8"); ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset = "utf-8"> </head> <body> <form action="accept.php" method="post" enctype="multipart/form-data"> アップロードするファイルを選択してください(png, jpg, gif 形式のみ)。<br><br> <input type="file" name="filename"><br> <input type="submit" value="アップロード"> </form> </body> </html>
■accept.php
<?php session_start(); define('UPLOADPATH', '/var/www/upload'); $tmpfile = $_FILES["filename"]["tmp_name"]; //一時ファイル, $orgfile = $_FILES["filename"]["name"]; //元ファイル名 try { //ファイルがアップロードされているかチェック if (! is_uploaded_file($tmpfile)) { throw new RuntimeException("ファイルがアップロードされていません。"); } //ファイルサイズ上限チェック // (必要時のみ) if ($_FILES["filename"]["size"] > 1000000 ) { throw new RuntimeException("ファイルサイズが大きすぎます"); } //元ファイルの拡張子を取得 $info = pathinfo($orgfile); $ext = strtolower($info['extension']); if ($ext != 'png' && $ext != 'jpg' && $ext != 'gif') { throw new RuntimeException("拡張子はpng, jpg, gif で指定してください。"); } //ファイル形式を取得 $imginfo = getimagesize($tmpfile); $type = $imginfo[2]; //ファイル形式判定 if (!($ext == 'gif' && $type == IMAGETYPE_GIF || $ext == 'jpg' && $type == IMAGETYPE_JPEG || $ext == 'png' && $type == IMAGETYPE_PNG)) { throw new RuntimeException("拡張子と形式が一致しません。"); } //ウィルススキャン exec("savscan $tmpfile; echo $?", $output, $result); //結果判定 // 0:ウィルスなし・正常終了 // 1:ユーザーによる中断 // 2:エラー発生 // 3:ウィルス検出 if (end($output) == 3) { throw new RuntimeException("ウィルスが検出されました。"); } else if (end($output) == 1 || end($output) == 2) { throw new RuntimeException("ウィルススキャンが異常終了しました。"); } //ユニークなファイル名を取得 $info = pathinfo($orgfile); $ext = strtolower($info['extension']); $count = 0; do { $tofile = sprintf('%s/%08x.%s', UPLOADPATH, mt_rand(), $ext); $fp = @fopen($tofile, 'x'); //排他でファイルを開く } while ($fp === FALSE && ++$count < 10); //10回までファイル作成を試行 if ($fp === FALSE) { throw new RuntimeException('ファイルが作成できません。'); } fclose($fp); //アップロードファイル格納 if (! move_uploaded_file($tmpfile, $tofile)) { throw new RuntimeException("ファイルをアップロードできませんでした。"); } } catch(RuntimeException $e) { $error = 1; $_SESSION['msg'] = $e->getMessage(); } //正常終了 if ($error != 1) { $_SESSION['msg'] = "ファイルをアップロードしました。"; } header("HTTP/1.1 301 Moved Permanently"); header("Location: result.php"); ?>
■result.php
<?php session_start(); header("Content-type: text/html; charset=utf-8"); ?> <!DOCTYPE html> <html lang="ja"> <head> <meta charset = "utf-8"> </head> <body> <?php echo htmlspecialchars($_SESSION['msg'] , ENT_QUOTES, "UTF-8") ?> <?php $_SESSION = array(); ?> </body> </html>
おわりに
PHP から Sophos でアップロードファイルをウィルススキャンする方法を見てきましたが、いかがでしたでしょうか。
やり方が分かってしまえば、さほど難しくないと思います。うまくいくまで多少手こずりましたが。
それよりも、ファイルアップロード処理そのものの方が難しいのではないでしょうか。
特に、画像のマジックバイトの処理は注意が必要ですね。最近は、Internet Explorer 7 をサポート対象外にするサービスも増えているから対応しないという選択肢もあるかとは思いますが、脆弱性があるのを分かっていてサービスを提供するのもどうだろうかと思います。
この記事が参考になれば幸いです。
コメント