質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

3回答

6270閲覧

PHP なぜbasenameでパスの最後にある名前の部分を取得しないといけないのでしょうか?

marimokomokmOk

総合スコア52

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

1グッド

1クリップ

投稿2017/05/29 07:00

編集2022/01/12 10:55

なぜ
$file = 'upload/' . basename ( $_FILES ['upfile'] ['name'] );
basenameでパスの最後にある名前の部分を取得しないといけないのでしょうか?
basename使わなくてもいいような気がしますが。。

あと、なぜif (move_uploaded_file ( $_FILES ['upfile'] ['tmp_name'], $file ))
のように
if文を使うのでしょうか。
もし$_FILES ['upfile'] ['tmp_name']が$fileに移動したらという意味なのに何故if文でも動作するのでしょうか。
if文じゃなくて単純にやりたいことやりたいんなら
if文付けないで
move_uploaded_file ( $_FILES ['upfile'] ['tmp_name'], $file )
これだけでいいような気がします。

php

1<?php 2$file = 'upload/'. basename($_FILES['apple']['name']); 3move_uploaded_file($_FILES['apple']['tmp_name'], $file); 4echo '<p><img src="',$file,'"></p>'; 5?>


これでも一応機能するのに。。(´・ω・`;A) アセアセ

php

1<p>アップロードするファイルを指定してください。</p> 2<form action="upload-output.php" method="post" enctype="multipart/form-data"> 3<p><input type="file" name="upfile"></p> 4<p><input type="submit" value="アップロード"></p> 5</form> 6<p> 7 <a href="main.php">一覧へ戻る</a> 8</p>

php

1<?php 2if (is_uploaded_file ( $_FILES ['upfile'] ['tmp_name'] )) { 3 if (! file_exists ( 'upload' )) { 4 mkdir ( 'upload' ); 5 } 6 $file = 'upload/' . basename ( $_FILES ['upfile'] ['name'] ); 7 if (move_uploaded_file ( $_FILES ['upfile'] ['tmp_name'], $file )) { 8 echo $file, 'のアップロードに成功しました。'; 9 echo '<p><img src="', $file, '"></p>'; 10 } else { 11 echo 'アップロードに失敗しました。'; 12 } 13} else { 14 echo 'ファイルを選択してください。'; 15} 16echo $file; 17?> 18<p> 19 <a href="main.php">一覧へ戻る</a> 20</p>
fjaiofjawiefjaw👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

basename関数が必要な理由ですが、例えばこのスクリプトを /var/www/html というディレクトリにおくとすると、アップロード先のディレクトリは /var/www/html/upload となります。
この場合、外部から指定するアップロードファイル名を ../index.php とすると、アップロード先のファイル名は /var/www/html/upload/../index.php となりますが、../ は親ディレクトリを示すので、結局 /var/www/html/index.php に書き込まれることになります。書き込む内容がPHPスクリプトだとすると、外部から任意のスクリプトを書き込まれることになりますし、そのファイルを参照することで実行もできます。このような攻撃をディレクトリトラバーサル攻撃といいます。

ファイル名を ../index.php なんかにできるのかと思われるかもしれませんが、たしかに通常のブラウザではできませんが、ブラウザからの通信を横取りして書き換えるツール(Burp Suiteなど)を使うとできます。

なので、basename()関数を使います。basename()関数を通すと、../index.phpというファイル名から ../ をとって、index.php というファイル名になるので、別のディレクトリを指定する攻撃はできなくなります。

以上が、basename()関数を用いる理由ですが、実は話はそう単純ではありません(今までも十分複雑かな?)。

まず、basename()関数を通せば安全かというと、/var/www/html/upload/index.php でも通常はPHPスクリプトを実行することになるので、単にbasenameを通せば安全というわけではありません。

一方で、$_FILES自体が、あらかじめbasename()関数相当の処理がされています。この改良はPHP 4.3.7でなされました。従って、../index.php を用いた攻撃を試すためには PHP 4.3.6 以前という化石のようなバージョンを用いなければなりません。PHP5.6とかPHP7のような現在のバージョンでは、basename($_FILES ...)という処理は二重にbasename関数を通していることになります。

では、多くの$_FILESのサンプルでbasename()関数を用いている理由はなぜかといいますと、すべてのプログラマが$_FILESの挙動を詳細に知っているわけではないので、処理内容を見た時に 「あれ、ディレクトリトラバーサル攻撃されてしまうぞ」と思ってしまうからです。プログラムは作りっぱなしで終わりではなく、後々メンテナンスし続けるものですし、その際に担当者が変わることもあるので、明示的に basename関数を読んでおいたほうが、「あれっ?」と思う機会と時間ロスがなくなるのですね。
また、「外部から得られたファイル名は例外なくbasename関数を通してから使用する」というルールで統一しておければ、ディレクトリトラバーサル攻撃の被害にあう可能性を少なくできます。$_FILESの場合はたまたまbasenameを呼ぶ必要はありませんが、他の場合は呼ぶ必要があります。ケースバイケースでプログラマがその判断をしてしまうと、万一判断ミスがあった場合にディレクトリトラバーサル攻撃にあいます。ディレクトリトラバーサル攻撃は非常に危険なので、万が一にもそのようなミスをなくす必要があります。

このような理由からbasename関数を通しておいたほうがよいとは思いますが、それだけでは不十分だ、というのが結論です。

投稿2017/05/29 22:11

ockeghem

総合スコア11701

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

marimokomokmOk

2017/05/30 07:15

ありがとうございます ご教示くださった文章が難しいのでよく読んでみます
guest

0

質問の前提をひっくり返してしまうような回答になって申し訳ないのですが、$_FILES以下の['name']は、クライアント側で生成するものですので、「長すぎる名前」「ファイル名として書き出せない文字が入った名前」など、攻撃者の手にかかれば何を送られてくるかわかりません

こんな信用ならない名前は使わずに、連番なりランダムなり、コントロールの効く名前を生成したほうが適切だと考えます。

投稿2017/05/29 07:12

maisumakun

総合スコア145121

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

marimokomokmOk

2017/05/29 08:32

ありがとうございます。 先程のプログラムだとすでに同じファイル名があると上書きされてしまいますので、新しいファイル名をアップロード前のファイル名から付けるのではなく、プログラム側で決めるように一工夫します><
guest

0

仰るとおり「一応機能はする」けれど、必ず成功するとは限りません。
そのため関数は親切に成否(true/false)を返しています。
成否を確認することで処理が確実に成功したという結果を受け取ることで続く処理に進めます。
※もちろんmaisumakunさんが仰るように「安全な情報が来た」という根拠にはならないので別途安全な情報であるかどうかはチェックが必要です。

「正常系・異常系」を調べると理解が進むと思いますよ。

投稿2017/05/29 08:11

m.ts10806

総合スコア80765

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問