1.修复上传漏洞和SQL注入漏洞-增强登录验证功能
This commit is contained in:
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace app\admin\controller;
|
||||
|
||||
use ba\Terminal;
|
||||
use ba\TableManager;
|
||||
use support\think\Db;
|
||||
use app\admin\model\AdminLog;
|
||||
@@ -166,18 +165,6 @@ class Ajax extends Backend
|
||||
]);
|
||||
}
|
||||
|
||||
public function changeTerminalConfig(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) return $response;
|
||||
|
||||
AdminLog::instance($request)->setTitle(__('Change terminal config'));
|
||||
if (Terminal::changeTerminalConfig()) {
|
||||
return $this->success();
|
||||
}
|
||||
return $this->error(__('Failed to modify the terminal configuration. Please modify the configuration file manually:%s', ['/config/terminal.php']));
|
||||
}
|
||||
|
||||
public function clearCache(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
@@ -193,13 +180,4 @@ class Ajax extends Backend
|
||||
event_trigger('cacheClearAfter');
|
||||
return $this->success(__('Cache cleaned~'));
|
||||
}
|
||||
|
||||
public function terminal(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) return $response;
|
||||
|
||||
(new Terminal())->exec();
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,14 +46,10 @@ class Index extends Backend
|
||||
'siteName' => get_sys_config('site_name'),
|
||||
'version' => get_sys_config('version'),
|
||||
'apiUrl' => $apiUrl,
|
||||
'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types']),
|
||||
'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types', 'forbidden_suffixes']),
|
||||
'cdnUrl' => full_url(),
|
||||
'cdnUrlParams' => config('buildadmin.cdn_url_params'),
|
||||
],
|
||||
'terminal' => [
|
||||
'phpDevelopmentServer' => str_contains($_SERVER['SERVER_SOFTWARE'] ?? '', 'Development Server'),
|
||||
'npmPackageManager' => config('terminal.npm_package_manager'),
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ return [
|
||||
'File uploaded successfully' => 'File uploaded successfully',
|
||||
'No files were uploaded' => 'No files were uploaded',
|
||||
'The uploaded file format is not allowed' => 'The uploaded file format is no allowance.',
|
||||
'The uploaded file contains prohibited content' => 'The uploaded file contains prohibited content',
|
||||
'The uploaded image file is not a valid image' => 'The uploaded image file is not a valid image',
|
||||
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => 'The uploaded file is too large (%sMiB), maximum file size:%sMiB',
|
||||
'No files have been uploaded or the file size exceeds the upload limit of the server' => 'No files have been uploaded or the file size exceeds the server upload limit.',
|
||||
|
||||
@@ -31,6 +31,7 @@ return [
|
||||
'File uploaded successfully' => '文件上传成功!',
|
||||
'No files were uploaded' => '没有文件被上传',
|
||||
'The uploaded file format is not allowed' => '上传的文件格式未被允许',
|
||||
'The uploaded file contains prohibited content' => '上传的文件包含禁止的内容',
|
||||
'The uploaded image file is not a valid image' => '上传的图片文件不是有效的图像',
|
||||
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => '上传的文件太大(%sM),最大文件大小:%sM',
|
||||
'No files have been uploaded or the file size exceeds the upload limit of the server' => '没有文件被上传或文件大小超出服务器上传限制!',
|
||||
|
||||
@@ -58,7 +58,7 @@ class Index extends Frontend
|
||||
'siteName' => get_sys_config('site_name'),
|
||||
'version' => get_sys_config('version'),
|
||||
'cdnUrl' => full_url(),
|
||||
'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types']),
|
||||
'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types', 'forbidden_suffixes']),
|
||||
'recordNumber' => get_sys_config('record_number'),
|
||||
'cdnUrlParams' => config('buildadmin.cdn_url_params'),
|
||||
],
|
||||
|
||||
@@ -21,6 +21,7 @@ return [
|
||||
'File uploaded successfully' => 'File uploaded successfully!',
|
||||
'No files were uploaded' => 'No files were uploaded',
|
||||
'The uploaded file format is not allowed' => 'The uploaded file format is not allowed',
|
||||
'The uploaded file contains prohibited content' => 'The uploaded file contains prohibited content',
|
||||
'The uploaded image file is not a valid image' => 'The uploaded image is not valid',
|
||||
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => 'The file is too large (%sMiB), maximum: %sMiB',
|
||||
'No files have been uploaded or the file size exceeds the upload limit of the server' => 'No file uploaded or size exceeds server limit',
|
||||
|
||||
@@ -22,6 +22,7 @@ return [
|
||||
'File uploaded successfully' => 'Fail berjaya dimuat naik!',
|
||||
'No files were uploaded' => 'Tiada fail dimuat naik',
|
||||
'The uploaded file format is not allowed' => 'Format fail yang dimuat naik tidak dibenarkan',
|
||||
'The uploaded file contains prohibited content' => 'Fail yang dimuat naik mengandungi kandungan yang dilarang',
|
||||
'The uploaded image file is not a valid image' => 'Fail imej yang dimuat naik tidak sah',
|
||||
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => 'Fail terlalu besar (%sM), saiz maksimum: %sM',
|
||||
'No files have been uploaded or the file size exceeds the upload limit of the server' => 'Tiada fail dimuat naik atau saiz melebihi had pelayan',
|
||||
|
||||
@@ -20,6 +20,7 @@ return [
|
||||
'File uploaded successfully' => '文件上传成功!',
|
||||
'No files were uploaded' => '没有文件被上传',
|
||||
'The uploaded file format is not allowed' => '上传的文件格式未被允许',
|
||||
'The uploaded file contains prohibited content' => '上传的文件包含禁止的内容',
|
||||
'The uploaded image file is not a valid image' => '上传的图片文件不是有效的图像',
|
||||
'The uploaded file is too large (%sMiB), Maximum file size:%sMiB' => '上传的文件太大(%sM),最大文件大小:%sM',
|
||||
'No files have been uploaded or the file size exceeds the upload limit of the server' => '没有文件被上传或文件大小超出服务器上传限制!',
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user