1.修复上传漏洞和SQL注入漏洞-增强登录验证功能

This commit is contained in:
2026-06-24 11:47:27 +08:00
parent 546d350f3a
commit 5187330c38
25 changed files with 303 additions and 1462 deletions

View File

@@ -59,6 +59,13 @@ class Upload
public function setDriver(string $driver): self
{
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $driver)) {
throw new InvalidArgumentException(__('Driver %s not supported', [$driver]));
}
$allowed = $this->checkConfig($this->config['allowed_drivers'] ?? 'local');
if ($allowed && !in_array(strtolower($driver), $allowed)) {
throw new InvalidArgumentException(__('Driver %s not supported', [$driver]));
}
$this->driver['name'] = $driver;
return $this;
}
@@ -79,11 +86,16 @@ class Upload
protected function resolveDriverClass(string $driver): string|false
{
if ($this->driver['namespace'] || str_contains($driver, '\\')) {
$class = str_contains($driver, '\\') ? $driver : $this->driver['namespace'] . $this->studly($driver);
if (class_exists($class)) {
return $class;
}
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $driver)) {
return false;
}
$allowed = $this->checkConfig($this->config['allowed_drivers'] ?? 'local');
if ($allowed && !in_array(strtolower($driver), $allowed)) {
return false;
}
$class = $this->driver['namespace'] . $this->studly($driver);
if (class_exists($class)) {
return $class;
}
return false;
}
@@ -181,6 +193,10 @@ class Upload
throw new RuntimeException(__('The uploaded file format is not allowed'));
}
$this->assertSafeFileName($this->fileInfo['name']);
$this->assertNoMaliciousContent();
$this->assertImageMime();
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $this->topic)) {
throw new RuntimeException(__('Topic format error'));
}
@@ -257,4 +273,95 @@ class Upload
}
return $configItem ? explode(',', strtolower((string)$configItem)) : [];
}
protected function getForbiddenSuffixes(): array
{
return $this->checkConfig($this->config['forbidden_suffixes'] ?? '');
}
protected function assertSafeFileName(string $fileName): void
{
$forbidden = $this->getForbiddenSuffixes();
if (!$forbidden) {
return;
}
$parts = explode('.', strtolower($fileName));
if (count($parts) < 2) {
return;
}
foreach ($parts as $index => $part) {
if ($part === '') {
throw new RuntimeException(__('The uploaded file format is not allowed'));
}
if ($index === count($parts) - 1) {
continue;
}
if (in_array($part, $forbidden)) {
throw new RuntimeException(__('The uploaded file format is not allowed'));
}
}
$suffix = $parts[count($parts) - 1];
if (in_array($suffix, $forbidden)) {
throw new RuntimeException(__('The uploaded file format is not allowed'));
}
}
protected function assertNoMaliciousContent(): void
{
$path = $this->file->getPathname();
if (!is_file($path) || !is_readable($path)) {
return;
}
$handle = fopen($path, 'rb');
if (!$handle) {
throw new RuntimeException(__('The uploaded file format is not allowed'));
}
$chunk = fread($handle, 8192);
fclose($handle);
if ($chunk === false || $chunk === '') {
return;
}
$patterns = [
'/<\?php/i',
'/<\?=/i',
'/<\?(?!xml)/i',
'/<%[@=]?/i',
'/<script\b/i',
'/\b(eval|assert|shell_exec|system|passthru|proc_open|popen|base64_decode)\s*\(/i',
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $chunk)) {
throw new RuntimeException(__('The uploaded file contains prohibited content'));
}
}
}
protected function assertImageMime(): void
{
$imageSuffixes = ['gif', 'jpg', 'jpeg', 'bmp', 'png', 'webp'];
if (!in_array($this->fileInfo['suffix'], $imageSuffixes, true)) {
return;
}
$path = $this->file->getPathname();
if (!is_file($path)) {
throw new RuntimeException(__('The uploaded image file is not a valid image'));
}
if (function_exists('finfo_open')) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if ($finfo) {
$detected = finfo_file($finfo, $path);
finfo_close($finfo);
if (!$detected || !str_starts_with(strtolower($detected), 'image/')) {
throw new RuntimeException(__('The uploaded image file is not a valid image'));
}
}
}
}
}