あるファイル(フォルダ)が指定されたフォルダ内にあるかの判定
公開日:
更新日:
あるファイルもしくは, フォルダが指定されたフォルダ以下にあるかどうかの簡単な判定方法について考える.
問題
以下のディレクトリがあるとする.
- fileR
- root
- folder
- fileA
- fileB
- fileC
- folder
この場合, folder, fileA, fileB, fileCはroot内にあるが, fileRはroot外にある. これの判定をしたい.
解決
上の問題の場合, 各ファイルパスは以下のようになる.
| fileR | ./fileR |
|---|---|
| root | ./root |
| folder | ./root/folder |
| fileA | ./root/folder/fileA |
| fileB | ./root/fileB |
| fileC | ./root/fileC |
ここで, root内にあるファイルフォルダは./rootで始まっている. つまり, パスが./rootで始まっているものがそのフォルダ内にあり, それ以外は, 外部である.
スクリプトで書くとこうなるだろう.
/*
* $fileNameが$root以下にあるか.
*
* @return: あるときは, true, それ以外はfalse
*/
function IsFileOwner($fileName, $root){
// パス正規化.
// この時, そのパス先にファイルがなくても動作したいため,
// realpath()は使わない.
$fileName = NormalizePath($fileName);
if(StartsWith($fileName, $root)){
return true;
}
return false;
}
/* パス正規化
* ファイルが存在しなくても, パスを正規化する.
* ex:
* ./files/../fileA -> ./fileA
* /home/user/folder/../../ -> /home
*/
function NormalizePath (string $str) {
$fn = explode("/", $str);
$stack = [];
$index = 0;
foreach ($fn as $path) {
if ($path === "..") {
if (count($stack))
array_pop($stack);
}
// 最初の'.'は無視しない.
else if ($index != 0 && $path === ".") {
// 無視
}
else if ($path === "") {
// 無視
}
else {
array_push($stack, $path);
}
$index++;
}
return implode("/", $stack);
}
/*
* $strが$searchで始まっているか.
*
*/
function StartsWith($str, $search){
if (substr($str, 0, strlen($search)) === $search) {
// Match
return true;
}
return false;
}
ディレクトリトラバーサルの対策になるかもしれない.
ディレクトリトラバーサル
ディレクトリトラバーサル (英語: directory traversal) とは, 利用者が供給した入力ファイル名のセキュリティ検証/無害化が不十分であるため, ファイルAPIに対して「親ディレクトリへの横断 (traverse)」を示すような文字がすり抜けて渡されてしまうような攻撃手法のことである[1].