0708做的安全检测

发布时间:2026-07-08 03:33 最后更新:2026-07-08 03:33 浏览量:共 35 次浏览

帮我把全站代码,再检查 一下有没有安全漏洞?然后输出解决方案给我。
03:01

思考过程

好的,我来对全站代码进行全面安全审计。这是一个复杂任务,我将并行启动多个搜索代理检查不同维度的安全漏洞。

思考过程

全站安全审计报告 & 解决方案

共审计 5 大维度,发现 54 个安全问题(去重后)。其中:

  • P0 致命级(立即修复):4 类,共 8 项
  • P1 高危:10 项
  • P2 中危:21 项
  • P3 低危:15 项

亮点:项目在 SQL 注入(PDO 真实预处理)、命令注入(无危险函数)、代码注入(无 eval/assert)、密码哈希(bcrypt)、CSRF 双重提交 Cookie、登录暴力破解防护等方面做得优秀。


P0 致命级问题(必须 24 小时内修复)

P0-1:敏感文件泄露服务器密码(最高危)

问题文件:

  • haoping.sql 第 4 行 — 明文包含宝塔密码、Debian root 密码、MySQL 密码、服务器 IP
  • api/db_config -zxw.php 第 17 行 — 明文 Redis 密码、JWT 密钥(文件名含空格,会以纯文本返回)
  • api/db_config.local.php 第 11/15/21 行 — 明文 DB 密码、JWT_SECRET、Redis 密码
  • haoping.sql 第 6056 行起 — 全部用户密码哈希、手机号

风险:任何人通过浏览器直接访问 https://域 名/haoping.sql 即可下载,等同服务器被完全接管。

解决方案:

  1. 立即删除 上述 3 个文件(或移出 Web 根目录到 /www/secure/)
  2. 立即修改 所有被泄露密码:宝塔面板、Debian root、MySQL、Redis、JWT_SECRET
  3. 强制重置所有用户密码(哈希已泄露)
  4. 在 .gitignore 中排除 *.sql、db_config*.php、*.local.php

P0-2:微信支付私钥 PEM 文件可被直接下载

问题文件:

风险:/admin/ 目录无 .htaccess 保护,攻击者直接 URL 下载私钥即可伪造任意支付/退款/分账请求。

解决方案:

  1. 将 PEM 文件移到 Web 根目录外(如 /www/secure_keys/)
  2. 更新数据库 payment_configs.private_key_path 为新路径
  3. 重置微信支付商户 API 证书(旧私钥视为已泄露)
  4. 在 /admin/.htaccess 添加:

<FilesMatch "\.(pem|key|sql|log|lock)$">
    Require all denied
</FilesMatch>

P0-3:缺失全部 HTTP 安全响应头 + 无 HTTPS 强制跳转

风险:无 X-Frame-Options → 点击劫持;无 HSTS → 中间人降级攻击;HTTP 不跳转 HTTPS → Cookie 明文传输。

解决方案:在 Database::jsonOut 和所有 HTML 入口统一添加:

header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: strict-origin-when-cross-origin');
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
    header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
}

在 Web 服务器 80 端口配置 301 跳转到 443。

P0-4:logs/、cache/、cron/ 目录可被直接访问

问题:

  • logs/ 目录无 .htaccess,日志含 IP、UA、业务数据
  • cache/security/ 存登录失败计数文件,文件名含 md5(手机号)
  • cron/profit_split.php 等多个 cron 脚本无 CLI 模式校验,可被 HTTP 触发

解决方案:

  1. 在 logs/、cache/、cron/ 各创建 .htaccess:Require all denied
  2. 所有 cron 脚本开头添加:

if (php_sapi_name() !== 'cli') { http_response_code(403); exit; }

需修复的 cron 文件:profit_split.php、saas_invoice_generate.php、saas_expire_check.php、renewal_reminder.php(对比已正确的 stats_flush.php:42)。


P1 高危问题

P1-1:退出登录未失效 Token

文件:auth_api.php:211-213

问题:logout 仅返回成功,未执行 UPDATE users SET token_invalidated_at = NOW()。退出后 JWT 仍有效 24 小时。

解决方案:

case 'logout':
    $db->execute(
        'UPDATE users SET token_invalidated_at = NOW() WHERE id = ?',
        [$currentUser['user_id']]
    );
    Database::jsonSuccess([], '已退出登录');
    break;

P1-2:JWT 存 localStorage + URL 传参 Token

文件:admin_common.js 多处、middleware.php:68-71

问题:XSS 可读取 localStorage 窃取 JWT;?token=xxx 会被记录在日志、Referer、浏览器历史。

解决方案(架构性,建议中期实施):

  • 短期:修复所有 XSS 漏洞(见 P1-4),缩短 JWT 有效期至 2 小时
  • 中期:JWT 改为 HttpOnly; Secure; SameSite=Strict Cookie 存储;文件下载改用短期一次性下载令牌

P1-3:默认密码为手机号后 8 位

文件:password_helpers.php:44-45

解决方案:改为随机生成 8-12 位密码(含大小写+数字),通过短信发送;强制首次登录改密。

P1-4:存储型 XSS(3 处高危)

问题:

解决方案:

  1. 教程内容(富文本):服务端引入 HTML Purifier 过滤;前端用 DOMPurify.sanitize() 后再 innerHTML
  2. 纯文本字段(菜品名、品牌名、page.name):前端渲染时调用 escapeHtml()

P1-5:IDOR 越权 — 4 个接口缺失 else 兜底

问题文件:

问题:权限校验 if/elseif 链无 else 兜底,promoter 角色或 shop_id 为空时 WHERE 条件为空,返回全表数据。

解决方案:在每个权限分支链末尾添加:

} else {
    Database::jsonError('权限不足', 403);
}

shop_manager 分支中 shopId 为空时直接 403。

P1-6:文件上传缺少图片内容真实性校验

文件:ImageManager.php 第 98、392、628 行(3 个方法)

问题:仅 finfo_file 检测 magic bytes,可被 polyglot 文件绕过(头部图片+尾部 PHP 代码)。

解决方案:在 move_uploaded_file 前添加:

$imgInfo = @getimagesize($file['tmp_name']);
if ($imgInfo === false) {
    return ['success' => false, 'message' => '非有效图片文件'];
}
// 可选:用 GD 重新渲染清除元数据
$img = imagecreatefromstring(file_get_contents($file['tmp_name']));
if ($img !== false) {
    imagejpeg($img, $file['tmp_name'], 90);
    imagedestroy($img);
}

P1-7:SSRF — download_image DNS Rebinding

文件:public_api.php:382-396

问题:gethostbyname 解析后 curl_exec 会再次解析 DNS,攻击者可 DNS rebinding 访问内网。

解决方案:用 CURLOPT_RESOLVE 固定 DNS 结果:

$ip = gethostbyname($host);
// ... IP 校验后 ...
curl_setopt($ch, CURLOPT_RESOLVE, [$host . ':443:' . $ip, $host . ':80:' . $ip]);
curl_setopt($ch, CURLOPT_URL, $imageUrl);

P1-8:api_v3_key 明文存储(与表注释"加密存储"矛盾)

文件:saas_config_api.php:187-194

解决方案:参考 RemoteImageService::encrypt()/decrypt(),入库前 AES-256-CBC 加密,读取时解密。使用独立密钥(非 JWT_SECRET)。

P1-9:private_key_path 未做白名单校验

文件:saas_config_api.php:167,191-193

解决方案:

$privateKeyPath = $_POST['private_key_path'] ?? '';
if (!empty($privateKeyPath)) {
    $realPath = realpath($privateKeyPath);
    $allowedDir = realpath(__DIR__ . '/../admin/apiclient_key');
    if ($realPath === false || strpos($realPath, $allowedDir . DIRECTORY_SEPARATOR) !== 0) {
        Database::jsonError('私钥路径必须在 admin/apiclient_key 目录下');
    }
    // ... 存储校验后的路径
}

P1-10:RemoteImageService 硬编码密钥回退

文件:RemoteImageService.php:16

问题:JWT_SECRET 未定义时回退到硬编码 'C@nYinV6_SuperSecret_Key_2026!@#',可解密所有云存储 AccessKey Secret。

解决方案:删除回退,未定义时抛异常。同样修复 JiaochengService.php:31、JiaochengGroupService.php:35。


P2 中危问题(21 项)

安全随机数(5 处 mt_rand → random_int)

文件 行号 用途 修复
WechatPay.php 688-696 支付签名 nonce random_int()
WechatPay.php 370,589 退款单号/订单号 random_int()
ProfitSharingService.php 261 分账单号 random_int()
AliyunSms.php 216 签名防重放 bin2hex(random_bytes(16))
jiaocheng_api.php 875,940,1075 slug 生成 bin2hex(random_bytes(3))

CSRF 相关(3 项)

  1. 文件上传绕过 CSRF — middleware.php:48-55:移除 $isFileUpload 旁路,文件上传也校验 CSRF(通过表单隐藏字段 _csrf_token)
  2. 提现缺二次验证 — saas_promotion_api.php:428-473:增加短信验证码或密码确认
  3. 修改密码强度不一致 — auth_api.php:256-258:change_password 也执行强密码校验(与 forgot_password 一致)

路径遍历(4 处 strpos 前缀校验可绕过)

文件:saas_invoice_api.php:84、images_api.php:561、qrcode_api.php:502、public_api.php:433

修复:

// 错误:strpos($fullPath, $allowedDir) !== 0
// 正确:
if ($fullPath !== $allowedDir && strpos($fullPath, $allowedDir . DIRECTORY_SEPARATOR) !== 0) {
    Database::jsonError('无效的文件路径', 403);
}

信息泄露(4 项)

  1. API 异常返回 $e->getMessage() — 19 处(saas_config_api.php 16 处、operation_api.php:524、jiaocheng_api.php:427、public_api.php:1139):改为通用错误提示,详情仅 error_log
  2. ai_config_api.php SELECT * — 第 34-35 行:改为白名单字段,api_key 返回掩码 sk-****1234
  3. 登录响应泄露用户名存在性 — auth_api.php:137-141:show_forgot_password 统一返回
  4. 公开接口可枚举门店 — public_api.php:88-114:增加 IP 速率限制;phone 字段脱敏

数据存储安全(3 项)

  1. SMS 验证码明文存储 — sms_api.php:160-163:入库前 password_hash(),校验用 password_verify()
  2. 教程访问密码明文存储 — jiaocheng_api.php:231-232:改用 password_hash(),管理端改为重置而非回显
  3. TencentVideoService SSL 校验禁用 — TencentVideoService.php:111-122:移除 CURLOPT_SSL_VERIFYPEER => false

其他中危(2 项)

  1. 授权域名校验用 HTTP_HOST — verify_license.php:190-194:改用 SERVER_NAME
  2. Redis 密码强度不足 — 使用 32+ 位随机字符串,启用 ACL

P3 低危问题(15 项,简列)

问题 文件 修复
LIKE 通配符未转义 jiaocheng_api.php:814 等 3 处 str_replace(['\\','%','_'],['\\\\','\\%','\\_'],$kw)
图片文件名用 rand() ImageManager.php 3 处 bin2hex(random_bytes(4))
ImageManager delete 无 realpath 校验 ImageManager.php:137 删除前 realpath 校验
WechatPay 私钥路径未校验 WechatPay.php:143 同 P1-9
qrcode LOGO 拉取 SSRF qrcode_api.php:574 同 P1-7
凭证上传允许 PDF saas_promotion_api.php:987 下载时 Content-Disposition: attachment
跳转参数校验不严 admin/index.html:181 增加 /\ 开头拒绝
OSS 端点管理员可控 RemoteImageService.php:490 域名白名单校验
无 Referer/Origin 校验 全项目 写操作增加 Origin 校验
Cron 输出堆栈信息 profit_split.php:116 写入独立日志文件
verify_license 调试入口 verify_license.php:335 删除直接访问分支
db_config 缺失返回路径 db_config.php:33 改为通用 500 错误
微信证书缓存权限 0755 WechatPay.php:491 改为 0700
phpqrcode echo 异常 phpqrcode.php:309 严格捕获输出
缺少 php.ini 配置文档 全项目 补充部署文档

修复优先级建议

第一阶段(立即,24 小时内):P0 全部 4 类 — 删除敏感文件、移动私钥、加安全头、加目录保护

第二阶段(1 周内):P1 全部 10 项 — logout 失效 token、修复 XSS、补 else 兜底、图片内容校验、SSRF 修复、密钥加密

第三阶段(1 月内):P2 中危 21 项 — 随机数升级、CSRF 强化、路径校验、信息泄露

第四阶段(迭代中):P3 低危 15 项


重要提醒:P0-1 和 P0-2 涉及的密码/密钥/证书已视为泄露,仅删除文件不够,必须轮换所有凭证(服务器密码、MySQL、Redis、JWT_SECRET、微信支付证书)。

需要我开始按优先级修复这些问题吗?建议从 P0 开始。

相关文章

CDN-SSL
我把此套程序,部署在阿里云的 2核 4G 5M的ECS上,部署OSS和REDIS。请问,这台服务器可以带动200家门店使用吗?
小黑内网服务器影射的NGINX规则
电子台账相关说明
利润测算软件的配置说明
密码保护:联想 System X3850 X6 阵列卡组建一例