如何保护密码保护环境下的CKEditor文件上传PHP脚本免受未授权访问?
保护CKEditor PHP文件上传脚本的关键措施
针对你给出的CKEditor上传脚本,我整理了几个核心的安全加固手段,帮你抵御未授权访问和常见攻击:
严格校验请求来源
你已经定义了$accepted_origins,但要注意不能只依赖$_SERVER['HTTP_REFERER'](它可以被伪造),不过结合其他验证还是有价值的。可以解析来源的主机名再做对比,避免子域名或伪造的路径绕过:if (!empty($_SERVER['HTTP_REFERER'])) { $referer_host = parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST); if (!in_array($referer_host, $accepted_origins)) { $funcNum = intval($_GET['CKEditorFuncNum']); echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Unauthorized origin.');</script>"; exit; } } else { // 无Referer的请求直接拒绝 exit('Invalid request'); }验证CKEditor专属参数
必须确保CKEditorFuncNum和CKEditor参数合法,防止恶意构造请求:if (!isset($_GET['CKEditorFuncNum']) || !is_numeric($_GET['CKEditorFuncNum'])) { exit('Invalid request parameters'); } $funcNum = intval($_GET['CKEditorFuncNum']); // 可选:验证CKEditor实例名是否符合预期 if (isset($_GET['CKEditor']) && !preg_match('/^[a-zA-Z0-9_-]+$/', $_GET['CKEditor'])) { echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Invalid editor instance.');</script>"; exit; }强文件类型与内容校验
绝不能只靠文件名后缀判断文件类型,要结合MIME类型和文件内容检测:// 用finfo检测真实文件类型 $finfo = new finfo(FILEINFO_MIME_TYPE); $file_mime = $finfo->file($_FILES['upload']['tmp_name']); $allowed_mimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (!in_array($file_mime, $allowed_mimes)) { echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Only image files are allowed.');</script>"; exit; } // 额外验证文件后缀(双重保障) $allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; $file_ext = strtolower(pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION)); if (!in_array($file_ext, $allowed_exts)) { echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Invalid file extension.');</script>"; exit; }限制文件大小
主动设置上传文件的最大限制,避免超大文件耗尽服务器资源:$max_size = 2 * 1024 * 1024; // 2MB if ($_FILES['upload']['size'] > $max_size) { echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'File size exceeds 2MB limit.');</script>"; exit; }安全处理文件名与路径
避免使用原始文件名(防止路径遍历和恶意文件名),生成随机安全文件名,同时确保上传路径在预期目录内:// 生成随机文件名 $safe_filename = uniqid('ckupload_') . '.' . $file_ext; // 确保上传目录真实存在且安全 $upload_folder = realpath('../uploads/') . '/'; if (!$upload_folder || !is_dir($upload_folder) || !is_writable($upload_folder)) { echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Upload directory unavailable.');</script>"; exit; } $upload_path = $upload_folder . $safe_filename; // 防止路径遍历攻击,确保最终路径在上传目录内 if (strpos(realpath($upload_path), $upload_folder) !== 0) { echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Invalid upload path.');</script>"; exit; }添加CSRF防护
通过CSRF令牌验证请求合法性,防止跨站请求伪造:
首先在前端页面生成并传递令牌(假设使用session存储):// 页面初始化时生成CSRF令牌 session_start(); if (!isset($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); }修改CKEditor的上传URL:
CKEDITOR.replace('your_editor_id', { filebrowserUploadUrl: '/path/to/upload.php?CKEditorFuncNum={num}&csrf_token=<?php echo $_SESSION['csrf_token']; ?>' });然后在上传脚本中验证:
session_start(); if (!isset($_GET['csrf_token']) || $_GET['csrf_token'] !== $_SESSION['csrf_token']) { $funcNum = intval($_GET['CKEditorFuncNum'] ?? 0); echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Unauthorized request.');</script>"; exit; }设置安全的文件权限
上传目录权限设为755(仅所有者可写),上传后的文件权限设为644(无执行权限):if (move_uploaded_file($_FILES['upload']['tmp_name'], $upload_path)) { chmod($upload_path, 0644); // 返回成功给CKEditor echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '$upload_path', '');</script>"; } else { echo "<script>window.parent.CKEDITOR.tools.callFunction($funcNum, '', 'Failed to upload file.');</script>"; }记录上传日志
记录所有上传请求的关键信息(IP、文件名、时间、结果),方便后续排查异常:$log_entry = sprintf( "%s - IP: %s, File: %s, Result: %s\n", date('Y-m-d H:i:s'), $_SERVER['REMOTE_ADDR'], $_FILES['upload']['name'], file_exists($upload_path) ? 'Success' : 'Failed' ); file_put_contents('../upload_logs.txt', $log_entry, FILE_APPEND);
内容的提问来源于stack exchange,提问作者leemon
相关产品推荐
相关产品推荐

