2011-07-08 2 views
15

私はチームメイトと画像ギャラリーのユーザーアップロード画像の場所について議論しました。私は、私たちが提案する方法についてより広範な洞察をしたいと思います。アップロードした画像をパブリックフォルダに配置するのは安全ですか?

私のチームメイトは、パブリックブラウジングでは利用できないフォルダ(つまり、サーバー上のpublic_html)に置かれた画像ファイルにfile_get_contentsという名前のコントローラとアクションを書き込み、ヘッダーを介してエコーします。これは安全ですが、Zend Frameworkを使用しているので、画像コントローラの呼び出しごとにブートストラップのクエリが実行されるため、約500msの遅延が発生します。画像ギャラリービューは同時に20枚以上の画像を表示するので迷惑です。要するに

、関連するコードは次のようになります。

class ImageController extends Zend_Controller_Action { 
    public function showAction() { 
     $filename = addslashes($this->_getParam('filename')); 
     if(!is_file($filename)) { 
      $filename = APPLICATION_PATH.'/../public/img/nopicture.jpg'; 
     } 
     $this->_helper->viewRenderer->setNoRender(true); 
     $this->view->layout()->disableLayout(); 
     $img = file_get_contents($filename); 
     header('Content-Type: image/jpeg'); 
     $modified = new Zend_Date(filemtime($filename)); 
     $this->getResponse() 
      ->setHeader('Last-Modified',$modified->toString(Zend_Date::RFC_1123)) 
      ->setHeader('Content-Type', 'image/jpeg') 
      ->setHeader('Expires', '', true) 
      ->setHeader('Cache-Control', 'public', true) 
      ->setHeader('Cache-Control', 'max-age=3800') 
      ->setHeader('Pragma', '', true); 
     echo $img; 
    } 
} 

次に、ビューで、私たちは呼ん:

<img src="<?php echo $this->url(array('controller' => 'image', 'action' => 'show', 'filename' => PATH_TO_HIDDEN_LOCATION.'/filename.jpg')); ?>" /> 

私は別のアプローチを持っている:私は、元の画像を維持することを好みます隠された場所にありますが、要求されるとすぐにそれらを公共の場所にコピーし、リンクを提供します(cronによって実行される特別な仕組みで、毎回公共画像のディレクトリを拭き取り、その後無駄にならないようにします)。スペースと、robots.txtがGoogleにディレクトリのインデックスを作成しないように指示します)。解決策は、公的にアクセス可能なディレクトリ内のファイル(すべての与えられた瞬間で数)(提供される一つのファイル名を知っている)を置き、だけでなく、このように、ブートストラップ起動しない、唯一のビューヘルパーが必要ですの助けを借りて

class Zend_View_Helper_ShowImage extends Zend_View_Helper_Abstract { 
    public function showImage ($filename) { 
     if (!file_exists(PUBLIC_PATH."/img/{$filename}")) { 
      if (!copy(PATH_TO_HIDDEN_FILES."/{$filename}",PUBLIC_PATH."/img/{$filename}")) 
       $url = PUBLIC_PATH.'/img/nopicture.jpg'; 
      else 
       $url = PUBLIC_PATH."/img/{$filename}"; 
     } else { 
      $url = PUBLIC_PATH."/img/{$filename}" 
     } 
     return "{$url}"; 
    } 
} 

をこのヘルパーは、コールは、ビューに非常に簡単です:

<img src="<?php echo $this->showImage('filename.jpg'); ?>" /> 

質問:は私のcoleague状態として、私のアプローチは、セキュリティ上の脅威をもたらすしていますか?これの潜在的なリスクは何ですか?そして、最も重要なのは、もしあれば、セキュリティ上の脅威がページ負荷の10秒間の利益を上回るのか?

重要な場合:私たちは約15,000人の登録ユーザーを持つコミュニティポータルに取り組んでおり、ギャラリーは非常に頻繁に使用されています。

*私が貼り付けたコードは、両方のアプローチの仕組みを示すために、私たち一人ひとりが何を思いついたかを編集して簡略化したものです。

+1

+1明確な方法で複雑な質問を書く。 :)今、私はあなたのために良い答えを持っていた場合... – nickf

答えて

6

私は別のアプローチを持っている:私は

を隠した場所に、元の画像を維持することを好む、しかし、すぐに彼らが要求されているとして、公共の場所にコピーし、それへのリンクを提供+1は創造性です。

私の同僚は、セキュリティ上の脅威に晒されていますか?これの潜在的なリスクは何ですか?そして、最も重要なのは、もしあれば、セキュリティ上の脅威がページ負荷の10秒間の利益を上回るのか?

はい、あなたが見ることを許可されている画像しかない場合は、それらを公開してアクセス可能なディレクトリに置いています。他の人がその画像を見ることができない変化があります。私はまた、(かなり間違っているかもしれない)と考えると、ページの読み込みに10秒かかることになります。イメージをコピーする必要があります。これはかなり集中的な操作で、file_get_contentsやreadfile()を使うよりも使いやすいです。

これは安全ですが、Zend Frameworkを使用しているため、画像コントローラの呼び出しごとに、ブートストラップのクエリが実行されるため、約500msの遅延が発生します。

この特定のケースのための核心のZend Framework。かなり大きなウェブサイトでもZend Frameworkを使用していますので、ブートストラップが望む時間より長くかかることがあります。 Zend Frameworkを迂回してvanilla PHPを選択すると、パフォーマンスが大幅に向上します。

また、file_get_contents()ではなくreadfile()を使用してください。 file_get_contentsが出力する前にファイル全体をメモリにロードするという点で大きな違いがあります。readfileはこれをより効率的に行います。

+5

+1はリードファイルを推奨します。しかし、そこには、fopen/fread/echoというより良い解決策がありますが、チャンクされています。言って、2kbのチャンク。パフォーマンスはreadfileよりも約50%優れています。 –

+0

@Michael J. V. Really?それは素晴らしいです。私はベンチマークを行い、それをチェックします。もしそうなら、私は^ _^ –

+0

を再訪するいくつかのプロジェクトを持っているので、それは既知の事実です。私が間違っていない場合、php.netのドキュメントで数年間アップされています。私はこれを数年前から使ってきましたが、readfile()/ file_get_contents + echoよりもずっと速いです。 –

2

ディレクトリリストを持たないように設定し、データベースで追跡される非常に長くランダムなファイル名(例えばidと塩からmd5で生成されたもの)を使用して、各アクセス後にファイル名が変更されました。

データベースを保持している...ファイル名: 'myfile.jpg'、TEMPNAME: 'uysdfnasdufhansvdufgnvasoeuvncas.jpg' を

は、アクセス機能は、ファイル名が同じまま、データベースにTEMPNAMEを検索し、それに応じて

アクセスした後、URLを挿入します、tempnameが変更され、ファイル名が新しいテンポラリ名に変更されました

+0

悪くないが、私はこれにデータベースに頼るのが嫌です。それでも、チップのおかげで。 – mingos

1

セキュリティに関する考慮事項はありますか?あなたの脅威モデルは何ですか?これらのソリューションはどのようにこれに対処していますか

いずれのソリューションもリーチングに対応していません。いずれのソリューションもマルウェアの再配布に対応していません。いずれの解決策もコンテンツアイテムへのアクセスを制限しません。

チームメイトのソリューションには、イメージ全体が不必要にPHPのメモリにロードされるという欠点があります。

あなたのソリューションは、1回の読み取りの代わりに2回の読み取りと書き込み操作を必要とするため、あまり効率的ではありません。コンテンツに無制限にアクセスできる機会を減らすだけです。

+0

私の同僚は、(一部の)画像に一般にアクセスすることによって悪意のあるコードを公開する人を恐れています。私はそれがどのように可能なのか分からないので、私の質問です。 Leechingは我々が検討している問題ではない。マルウェアの流通は。 – mingos

2

[OK]を、どのように別の方法について(これは仕方によって、あまりにも、少しハックです)あなたはギャラリーページに行くとき、あなたがユーザーに送信する必要がありますどの画像を知っているだろう

。私はそれらが識別子(ファイル名/ id /何でも)を持っていると仮定します。この要求に必要なすべてのファイルのリストを生成し、スクリプト(たとえば、公開されていない)上のテキストファイルなどに簡単にアクセスできる場所に格納します。そのファイルにIDを与えます。

ここで、完全なZendフレームワークをロードしない別のPHPファイルを用意してください。 URLからいくつかのパラメータを取得する必要があります:要求されているイメージファイルと、リストのID。

はそうのようなimgタグにURLを書く:

<img src="image.php?file=myFile.jpg&list=12345" /> 

image.phpファイルは、そのIDでリストを開き、myFile.jpgがそれに存在するかどうかを確認する必要があります。その場合は、404を発行しない場合は表示してください。

古いリストを定期的に整理することを忘れないでください。

4

限り、あなたは魔法のバイトとprehapsもあなたは問題ないはずGDライブラリを使用してイメージをロードしようとすると、ファイルが本当にMIMEをチェックして、画像ファイルであることを確認してくださいと。

ブートストラップ全体を実行しない特別なブートストラップを使用することで、初期のアプローチがスピードアップする可能性があります。あなたのイメージを読み込むための基本的なものです。

またGDまたは類似を使用して、非パブリックフォルダから画像を開き、出力スクリプトが含まれているパブリックフォルダで「ノーマル」.PHPファイルを置くことができます。ただ、

<a href="image.php?image=foobar.jpg&width=320&height=240" /> 

のようなもので、それを呼び出すここではまた、あなたが盲目的にイメージ名を信用していないことを確認する必要がありますが、実際には、これはあなたのイメージディレクトリにある画像やない厄介なものであることを確認します。

私はまた、あなたのイメージは、Webサーバがへ 書き込みアクセスを持っている 非パブリックフォルダに保存された、その後、これに パブリックフォルダにをリスト書き込みアクセスとディレクトリのない シンボリックリンクを作成することはお勧めでき

イメージフォルダを開き、そこからイメージをロードします。あなたのWebサーバーはそれらを直接提供でき、コピーする必要はありません。

ほんの少しの提案。

+0

これは安全です(ファイルは一般公開されていません)と高速です(Zend Frameworkは完全に回避されています)。私はそれを撃つだろう。 – mingos

2

私はこのような問題を数週間前に考えていましたが、同じようにThumbnail ViewHelperを作成しました。オリジナルの画像は非公開のディレクトリに保存されます。だから私はやるたび

echo $this->thumb(array('url' => HIDDEN_DIR . 'foo/bar.jpg')); 
ような呼び出し

なViewHelperイメージ公共アクセス可能キャッシュディレクトリをcopieとcaced画像へのURLを返します。

/_cache/thumbs/3858f62230ac3c915f300c664312c63f.jpg 

追加は、スクリプトは、トリミングとサイズを変更することができますイメージ、必要ならば。また、私は50の呼び出しごとにキャッシュから1000画像をクリーンアップする機能を追加しました...

覚えておくべきことは、アクセスできない場合であっても元の画像でディレクトリへのパスを決して表示しないことです公衆から。それから、私は安全だと言います。

このスクリプトでは、キャッシュを有効にして、目に見えない時間がないため、ユーザーエクスペリエンスが向上します。私はあなたが私の考えのいくつかを使用できることを願っています;)

+0

ニースのアプローチ!ファイル名は元のファイル名とは何の関係もないので、それを見る権利がない人は推測できません。定期的なクリーンアップでは、ほぼ完全に安全です。ありがとう、仲間、私はあなたの方法が好きです。 – mingos