web-dev-qa-db-ja.com

ハッカーは画像のアップロードを使用してPHPコードをサイトに取得しました

私はウェブサイトに取り組んでいます—現在、それはテストの初期段階にあり、まだローンチされておらず、テストデータしかありません-よろしくお願いします。

まず、ハッカーはウェブサイトの「管理」ページにログオンするためのパスワードを見つけました*。友人のコンピュータにあるキーロガーを使ってサイトにログインし、フィードバックをくれたと思います。

次に、画像アップロードボックスを使用してPHPファイルをアップロードしました。jpgと.pngファイルのみが受け入れられるように厳格なチェックを行いました。それ以外はすべて拒否されているはずです。確かにそこにあります。 .jpgファイルをアップロードし、ファイルが保存されたら拡張子を変更する方法はありませんか?

ありがたいことに、ファイルが送信されたときに新しいファイル名も生成するので、コードを実行するためにファイルを見つけることができなかったと思います。

WebサイトがPHPファイルを通過させる方法を理解できていないようです。セキュリティの何が問題になっていますか?検証関数コードは以下のとおりです。

function ValidateChange_Logo(theForm)
{
   var regexp;
   if (theForm.FileUpload1.value == "")
   {
      alert("You have not chosen a new logo file, or your file is not supported.");
      theForm.FileUpload1.focus();
      return false;
   }
   var extension = theForm.FileUpload1.value.substr(theForm.FileUpload1.value.lastIndexOf('.'));
   if ((extension.toLowerCase() != ".jpg") &&
       (extension.toLowerCase() != ".png"))
   {
      alert("You have not chosen a new logo file, or your file is not supported.");
      theForm.FileUpload1.focus();
      return false;
   }
   return true;
}

ファイルがサーバーに到達したら、次のコードを使用して拡張子を保持し、新しいランダムな名前を生成します。少し面倒ですが、うまく機能します。

// Process and Retain File Extension
$fileExt = $_FILES[logo][name];
$reversed = strrev($fileExt);
$extension0 = substr($reversed, 0, 1);
$extension1 = substr($reversed, 1, 1);
$extension2 = substr($reversed, 2, 1);
$fileExtension = ".".$extension2.$extension1.$extension0;
$newName = Rand(1000000, 9999999) . $fileExtension;

私はlogo.php;.jpgなどの名前でテストしたところ、画像をWebサイトで開くことができませんが、名前が123456.jpgに正しく変更されました。 logo.php/.jpgに関しては、Windowsはそのようなファイル名を許可しません。


*ウェブサイトの保護されたページ。シンプルな機能を許可します。たとえば、写真をアップロードすると、ウェブサイトの新しいロゴになります。 FTPの詳細は、Webサイトの保護されたページへのログオンに使用されるパスワードとは完全に異なります。データベースとcPanelの資格情報も同様です。私は、サイトのフォルダとファイルの構造を人々が見ることさえできないようにしました。 FTPの詳細がない場合、このサイトで.jpgまたは.png拡張子を.phpに名前変更する方法は、文字通りありません。

117
Williamz902

クライアント側の検証

指定した検証コードはJavaScriptです。これは、クライアントで検証を行うために使用するコードであることを示唆しています。

安全なウェブアプリのルール番号1は、クライアントを信頼しないことです。クライアントはユーザー(この場合は攻撃者)の完全な制御下にあります。クライアントに送信するコードが何にでも使用されていることを確認することはできません。また、クライアントに配置したブロックがこれまでにないセキュリティ上の価値を持つことはありません。クライアントでの検証は、スムーズなユーザーエクスペリエンスを提供するためだけのものであり、実際にセキュリティ関連の制約を強制するものではありません。

攻撃者は、ブラウザでそのJavaScriptの一部を変更するか、スクリプトを完全にオフにするか、ブラウザからファイルを送信せず、代わりにcurlのようなツールを使用して独自のPOSTリクエストを作成します。

サーバー上のすべてを再検証する必要があります。つまり、PHPは、ファイルが正しいことを確認する必要がありますタイプ、現在のコードではできないもの。

それを行う方法は、あなたの質問の範囲外であると私が思う広い問題ですが、 この質問 は、読み始めるのに適した場所です。あなたがしたいかもしれない 見て あなたの.htaccessファイルも同様です。

拡張機能を取得する

セキュリティの問題ではないかもしれませんが、 this の方がより良い方法です。

$ext = pathinfo($filename, PATHINFO_EXTENSION);

マジックPHP関数

編集ボックスからデータを保存するとき、PHPの3つの関数すべてを使用してデータをクリーンアップします。

$cleanedName = strip_tags($_POST[name]); // Remove HTML tags
$cleanedName = htmlspecialchars($cleanedName); // Allow special chars, but store them safely. 
$cleanedName = mysqli_real_escape_string($connectionName, $cleanedName);

これは良い戦略ではありません。ファイル拡張子の検証のコンテキストでこれを言及したという事実は、問題が解決することを期待して、セキュリティ関連の関数をいくつか入力に投げているだけだと心配します。

たとえば、SQLiから保護するために、エスケープせずに準備済みステートメントを使用する必要があります。データベースからデータが取得された後にXSSを防ぐためのHTMLタグと特殊文字のエスケープは、そのデータを挿入する場所によって異なります。などなど。

結論

これは意地悪だとは言いませんが、あなたはここで多くの間違いをしているようです。他の脆弱性があっても私は驚かないでしょう。アプリが機密情報を処理する場合は、セキュリティ経験のある人にそれを実稼働に移す前に見てもらうことを強くお勧めします。

313
Anders

まず、誰かがファイルアップロード機能を悪用した場合は、両方のクライアントでファイルを確認していることを確認してくださいおよびserver。クライアント側のJavaScript保護をバイパスするのは簡単です。

第2に、必ず OWASPからのこれらのアイデア を実行して実装してください。

これでほとんどの問題がカバーされます。また、サーバーにパッチが適用されていない他の欠陥がないことを確認してください(たとえば、古いプラグイン、悪用可能なサーバーなど)。

36
thel3l

PHPは他のコンテンツに埋め込まれるように設計されていますであることに注意してください。具体的には、HTMLページ内に埋め込まれるように設計されており、動的に更新されるデータのマイナービットで大部分が静的なページを補完します。

そして、非常に簡単に言えば、それが何に埋め込まれているかは問題ではありません。 PHPインタープリターは、任意のファイルタイプをスキャンして、スクリプトタグで適切にマークされている任意のPHPコンテンツを実行します。

これが引き起こした歴史的な問題は、画像のアップロードにPHPが含まれる可能性があることです。そして、WebサーバーがPHPインタープリターを介してこれらのファイルをフィルターすることをいとわない場合、そのコードが実行されます。Wordpress "Tim Thumb"プラグインの大失敗を参照してください。

適切な解決策は、Webサーバーが信頼できないコンテンツをPHP経由で実行する必要があるものとして決して処理しないようにすることです。入力をフィルタリングすることは、それを回避する1つの方法ですが、ご存じのように、制限があり、エラーが発生しやすくなります。

ファイルの場所とファイル拡張子の両方を使用してPHPが処理するものを定義し、アップロードやその他の信頼できないコンテンツを処理されない場所や拡張子に分離するために使用されていることを確認する方がはるかに優れています。(方法これはサーバーごとに異なり、「通常、物事を機能させることは、気づかない方法でセキュリティを緩めることを意味します」という問題に悩まされます。

26
gowenfawr

アップロードディレクトリのPHPを.htaccessで無効にすることができます。これにより、サーバーはディレクトリおよびそのサブディレクトリでPHPコードを実行しなくなります。

次のルールを使用して、アップロードディレクトリ内に.htaccessファイルを作成します。

php_flag engine off

.htaccessルールは、PHPサーバーがmod_phpモードで実行されている場合にのみ機能します。

編集:もちろん、.htaccessはApacheでのみ機能することを忘れていました。

23
medskill

拡張子のみをチェックしています。これは必ずしも実際のファイルタイプとは相関していません。サーバー側では、exif_imagetypeなどを使用して、ファイルがイメージであることを確認できます。 exif画像タイプの使用法: http://php.net/manual/en/function.exif-imagetype.php

11
iainpb

受け入れられた回答には完全に同意しますが、ファイル名をいじるだけではなく、もう少し多くのことを行うことをお勧めします。 元のファイル/アップロードしたファイルをPHP GdまたはImagickを使用して再圧縮し、新しい画像を使用するを使用して再圧縮します。この方法で、挿入されたコードを破棄します(正直に言うと、90%の時間、コードが圧縮を存続させる方法がありますが、それは多くの作業です)。

また、se .htaccessファイルを使用して、アップロードディレクトリが実行されないようにするPHPコード(IISおそらく同等の.webconfigがあります)。

<FilesMatch \.php$>
    SetHandler application/x-httpd-php
</FilesMatch>

この方法では、アップロードされたファイルは「最終拡張」がPHPの場合にのみ実行されるため、image.php.jpgは実行されません。

いくつかのリソース:

6
akman

ここでのソリューションの考えられる部分の1つは、イメージを保持することですが、イメージを確実に取得し、元のイメージのサイズを縮小できるように、 PHPでサイズ変更したコピーを作成 することもできます。これにはいくつかのプラスの効果があります。

  • 元の画像を表示に使用しないで、その安全性を高めます
  • サーバーのスペースを節約します(これが問題になる場合とそうでない場合があります)
  • サイト訪問者は、約10 MBの画像が読み込まれるのを待たないため、読み込み時間が短縮されます。代わりに、適切なサイズのロードで300 KBの画像を持っています

重要な点は、ユーザーデータを信頼することはできません。すべて、ファイルのアップロード、入力、Cookieを検証して、データが問題を引き起こさないことを確認します。

6
LostBoy

触れられていない小さなことを1つ追加するだけです。ホワイトリスト戦略を使用することです。

他の人がすでに指摘したように、クライアント側の検証のみに依存することは確かに良いことではありません。また、ブラックリスト戦略を採用しています。つまり、自分が知っている/悪いと思っていることをチェックし、他のすべてを許可しているということです。より堅牢な戦略は、問題がないことを確認し、それ以外はすべて拒否することです。

これは、経験の浅いユーザーに写真を正常にアップロードするために何をする必要があるかを知らせるなど、ユーザーフィードバックとのトレードオフになる場合があります。

私の経験では、2つの戦略は、ユーザーフィードバック用のクライアント側のブラックリスト検証とセキュリティ用のサーバー側のホワイトリスト検証という異なる目的で使用されている限り、必ずしも相互に排他的ではありません。

2
Daniel

私は画像のアップロードを許可していますが、すべてにトロイの木馬コードがロードされていると想定しています。アップロード時に、アップロードした画像を一時的にWebルートの上に配置し、ImageMagickを使用して画像をBMPに変換し、次にJPGに変換して、Webアプリが使用できる場所に移動します。これにより、埋め込まれたPHPコードはすべて効果的に削除されます。

下のコメントで@Sneftelが指摘しているように、JPGでこの変換を実行すると、画像がある程度劣化します。私の目的では、これは許容できるトレードオフです。

2
a coder

画像のEXIFタグを確認するか、strpos関数を使用して画像コンテンツ内のPHPコードを検索します。例:

$imageFile = file_get_contents($your_image_temp_path); // ATTENTION! Temporary path for images is really needed for security reasons!
$dangerousSyntax = ['<?', '<?php', '?>'];
$error = '';
foreach($dangerousSyntax as $value) {
  $find = strpos($value, $imageFile);
  if( $find !== true ) {
    unlink($your_image_temp_path);
    $error = 'We found a dangerous code in your image';
  }
}

if(!empty($error)) { 
  die($error);
}
1

受け入れられた回答に加えて、PHP組み込み getImageSize 関数を使用してMIMEタイプを取得し、PHPファイルは画像ファイル拡張子の下に隠れていません。

0
Grey