You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
69 lines
2.2 KiB
69 lines
2.2 KiB
|
24 hours ago
|
<?php
|
||
|
|
|
||
|
|
namespace App\Support;
|
||
|
|
|
||
|
|
use Illuminate\Validation\ValidationException;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 上传文件名校验:只信任最后一个后缀,拒绝空字节与危险后缀段。
|
||
|
|
*/
|
||
|
|
final class UploadFilenameGuard
|
||
|
|
{
|
||
|
|
/** @var list<string> */
|
||
|
|
public const DENIED_EXTENSIONS = [
|
||
|
|
'php', 'phtml', 'phar', 'php3', 'php4', 'php5', 'php7', 'php8',
|
||
|
|
'pht', 'phps', 'shtml', 'cgi', 'exe', 'js', 'html', 'htm', 'svg',
|
||
|
|
];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* @param list<string> $allowedExtensions
|
||
|
|
*/
|
||
|
|
public static function lastAllowedExtension(string $originalName, array $allowedExtensions): string
|
||
|
|
{
|
||
|
|
if ($originalName === '' || str_contains($originalName, "\0") || str_contains(strtolower($originalName), '%00')) {
|
||
|
|
throw ValidationException::withMessages([
|
||
|
|
'file' => ['文件名非法'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$normalized = str_replace('\\', '/', $originalName);
|
||
|
|
$basename = basename($normalized);
|
||
|
|
if ($basename === '' || $basename === '.' || $basename === '..') {
|
||
|
|
throw ValidationException::withMessages([
|
||
|
|
'file' => ['文件名非法'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$parts = explode('.', $basename);
|
||
|
|
if (count($parts) < 2) {
|
||
|
|
throw ValidationException::withMessages([
|
||
|
|
'file' => ['不支持的文件类型'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
$last = strtolower((string) array_pop($parts));
|
||
|
|
if ($last === '' || ! in_array($last, $allowedExtensions, true)) {
|
||
|
|
throw ValidationException::withMessages([
|
||
|
|
'file' => ['不支持的文件类型'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (in_array($last, self::DENIED_EXTENSIONS, true)) {
|
||
|
|
throw ValidationException::withMessages([
|
||
|
|
'file' => ['不支持的文件类型'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach ($parts as $part) {
|
||
|
|
$segment = strtolower((string) $part);
|
||
|
|
if ($segment !== '' && in_array($segment, self::DENIED_EXTENSIONS, true)) {
|
||
|
|
throw ValidationException::withMessages([
|
||
|
|
'file' => ['不支持的文件类型'],
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return $last;
|
||
|
|
}
|
||
|
|
}
|