修复已知安全问题,新增文件删除功能

This commit is contained in:
Chenx221 2024-02-12 12:49:18 +08:00
parent 78c9fe01dd
commit b4b45b3c6b
Signed by: chenx221
GPG Key ID: D7A9EC07024C3021

View File

@ -4,7 +4,6 @@ namespace app\controllers;
use app\models\RenameForm;
use app\utils\FileTypeDetector;
use InvalidArgumentException;
use Yii;
use yii\filters\VerbFilter;
use yii\helpers\ArrayHelper;
@ -48,8 +47,11 @@ class HomeController extends Controller
$rootDataDirectory = Yii::getAlias(Yii::$app->params['dataDirectory']);
$userId = Yii::$app->user->id;
if ($directory == null | $directory == '.') {
if ($directory === '.' ||$directory == null) {
$directory = null;
$parentDirectory = null;
} elseif ($directory === '..' || str_contains($directory, '../')) {
throw new NotFoundHttpException('Invalid directory.');
} else {
$parentDirectory = dirname($directory);
}
@ -83,7 +85,20 @@ class HomeController extends Controller
// 获取路径下的所有文件和文件夹
$directoryContents = scandir($path);
return array_diff($directoryContents, ['.', '..']);
// 移除 '.' 和 '..'
$directoryContents = array_diff($directoryContents, ['.', '..']);
// 使用 usort 对目录内容进行排序,使文件夹始终在文件之前
usort($directoryContents, function ($a, $b) use ($path) {
$aIsDir = is_dir($path . '/' . $a);
$bIsDir = is_dir($path . '/' . $b);
if ($aIsDir === $bIsDir) {
return strnatcasecmp($a, $b); // 如果两者都是文件夹或都是文件,按名称排序
}
return $aIsDir ? -1 : 1; // 文件夹始终在文件之前
});
return $directoryContents;
}
/**
@ -99,7 +114,7 @@ class HomeController extends Controller
$relativePath = rawurldecode($relativePath);
// 检查相对路径是否只包含允许的字符
if (!preg_match('/^[\w\-.\/]+$/u', $relativePath)) {
if (!preg_match('/^[\w\-.\/\s]+$/u', $relativePath)) {
throw new NotFoundHttpException('Invalid file path.');
}
@ -136,7 +151,7 @@ class HomeController extends Controller
$relativePath = rawurldecode($relativePath);
// 检查相对路径是否只包含允许的字符
if (!preg_match('/^[\w\-.\/]+$/u', $relativePath)) {
if (!preg_match('/^[\w\-.\/\s]+$/u', $relativePath)) {
throw new NotFoundHttpException('Invalid file path.');
}
@ -178,56 +193,63 @@ class HomeController extends Controller
*/
public function actionDelete()
{
// 从 POST 请求中获取 relativePath 参数
$relativePath = Yii::$app->request->post('relativePath');
// 对相对路径进行解码
$relativePath = rawurldecode($relativePath);
// 检查相对路径是否只包含允许的字符
if (!preg_match('/^[\w\-.\/]+$/u', $relativePath)) {
if (!preg_match('/^[\w\-.\/\s]+$/u', $relativePath)) {
throw new NotFoundHttpException('Invalid file path.');
}
// 确定文件的绝对路径
$absolutePath = Yii::getAlias(Yii::$app->params['dataDirectory']) . '/' . Yii::$app->user->id . '/' . $relativePath;
// 检查文件或文件夹是否存在
if (!file_exists($absolutePath)) {
throw new NotFoundHttpException('File or directory not found.');
} else {
$realPath = realpath($absolutePath);
$expectedPathPrefix = realpath(Yii::getAlias(Yii::$app->params['dataDirectory']) . '/' . Yii::$app->user->id);
if (!str_starts_with($realPath, $expectedPathPrefix)) {
throw new NotFoundHttpException('File or directory not found.');
}
}
// 删除文件或文件夹
if (is_dir($absolutePath)) {
// 如果是文件夹,递归删除文件夹及其内容
$this->deleteDirectory($absolutePath);
if (!$this->deleteDirectory($absolutePath)) {
Yii::$app->session->setFlash('error', 'Failed to delete directory.');
} else {
// 如果是文件,直接删除文件
$fileinfo = $absolutePath; //要删了,准备一下
$fileinfo2 = $fileinfo;
//unlink($absolutePath);
Yii::$app->session->setFlash('success', 'Directory deleted successfully.');
}
} else {
if (!unlink($absolutePath)) {
Yii::$app->session->setFlash('error', 'Failed to delete file.');
} else {
Yii::$app->session->setFlash('success', 'File deleted successfully.');
}
}
return $this->redirect(['index', 'directory' => dirname($relativePath)]);
}
/**
* 递归删除目录及其内容
* @param string $dir 目录路径
*/
protected function deleteDirectory($dir)
protected function deleteDirectory($dir): bool
{
if (!is_dir($dir)) {
return;
return false;
}
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$dir1 = $dir;
$file1 = $file;
$file2 = $file1;
is_dir("$dir/$file") ? $this->deleteDirectory("$dir/$file") : ''; //unlink("$dir/$file");
if (is_dir("$dir/$file")) {
if (!$this->deleteDirectory("$dir/$file")) {
return false;
}
$dir1 = $dir;
$dir2 = $dir1;
rmdir($dir);
} else {
if (!unlink("$dir/$file")) {
return false;
}
}
}
if (!rmdir($dir)) {
return false;
}
return true;
}
}