feat: 上线前安全加固与发布工程化;appmall 由插件迁移为独立应用
安全(P0) - 发布包签名:新增 scripts/package_keygen.php、scripts/package_verify.php、 ywxapp\service\PackageVerifier;FrameworkService::apply() 解压前强制验签 (ywxapp.framework_package_verify,默认开启) - JWT 收紧:config/jwt.php 移除公开默认密钥,JwtService::assertStrongSecret() 校验非空 / 长度≥32 / 已知弱值;.env.production.example 键名由 JWT_SECRET_KEY 修正为 JWT_SECRET - /api/v1/Article:免登录白名单收窄为只读接口,写操作字段白名单 + assertOwner() 越权校验 - 开发者令牌:查找接口不再回显明文、统一失败文案 + 限流 - TemplateInstaller:文件名白名单 + realpath 归一化 + 目录内断言,修复 Zip Slip;补 use ZipArchive - 后台登录:接入验证码、失败锁定与登录限流(config/ratelimit.php) - 缓存 / Session 驱动可由 .env 覆盖(CACHE_DRIVER / SESSION_TYPE),集群部署改 redis - paycenter 支付日志开关改为 PAY_LOG,不再跟随 APP_DEBUG 工程化 / 交付 - 新增 CHANGELOG.md 与 .env.production.example;composer.lock 入库锁定依赖版本 - 插件离线安装 CLI 增加 --uid,后台上传安装透传 uid,付费插件授权校验可通过 - 授权拒绝文案区分「未携带 uid」与「未找到有效授权」,便于排障 - .gitignore 放行 .env.production.example / .env.addon.example / .gitattributes / public/.htaccess 结构 - appmall 由 addon/appmall 迁移为独立应用 app/appmall(市场 / 开发者中心 / 审核 / 分润)
This commit is contained in:
1 parent
1d49e6f5ee
commit
18101cf11a
636 files changed
+58517
-9519
No files matched your search
@@ -13,35 +13,40 @@ namespace ywxapp\library;
|
||||
/**
|
||||
* 搜索引擎蜘蛛识别
|
||||
*
|
||||
* 仅按 User-Agent 特征串识别(不做 DNS 反查,保证零阻塞)。
|
||||
* 新增蜘蛛只需在 SPIDERS 追加一行:标识 => [UA特征串列表, 显示名]。
|
||||
* 两层判定:
|
||||
* 1. detect() —— 仅按 User-Agent 特征串识别(零阻塞,可被伪造)
|
||||
* 2. rdnsSuffixes() —— 返回该蜘蛛官方反向域名后缀,供 SpiderVerify 做
|
||||
* 「PTR 反查 + A 正查」双向验证,识别伪造 UA 的假蜘蛛。
|
||||
*
|
||||
* 新增蜘蛛:追加一行 标识 => [UA特征串列表, 显示名, 官方rDNS域名后缀列表]。
|
||||
* rDNS 后缀留空数组表示「官方不提供 rDNS,无法校验」,此类只按 UA 计数。
|
||||
*
|
||||
* @author ywxapp <admin@ywxapp.cn>
|
||||
*/
|
||||
class SpiderDetect
|
||||
{
|
||||
/**
|
||||
* 蜘蛛特征表:key=入库标识,value=[UA 特征串(不区分大小写,命中任一即算), 中文显示名]
|
||||
* 蜘蛛特征表:key=入库标识,value=[UA 特征串(不区分大小写,命中任一即算), 中文显示名, 官方 rDNS 域名后缀]
|
||||
*/
|
||||
public const SPIDERS = [
|
||||
'baidu' => [['Baiduspider'], '百度'],
|
||||
'google' => [['Googlebot', 'Google-InspectionTool', 'AdsBot-Google'], '谷歌'],
|
||||
'bing' => [['bingbot', 'msnbot', 'BingPreview'], '必应'],
|
||||
'sogou' => [['Sogou web spider', 'Sogou inst spider', 'Sogou Pic Spider'], '搜狗'],
|
||||
'so360' => [['360Spider', 'HaoSouSpider', 'qihoobot'], '360'],
|
||||
'bytedance' => [['Bytespider', 'ToutiaoSpider'], '字节跳动'],
|
||||
'shenma' => [['YisouSpider'], '神马'],
|
||||
'huawei' => [['PetalBot'], '华为花瓣'],
|
||||
'yandex' => [['YandexBot'], 'Yandex'],
|
||||
'duckduckgo' => [['DuckDuckBot', 'DuckDuckGo-Favicons-Bot'], 'DuckDuckGo'],
|
||||
'yahoo' => [['Yahoo! Slurp'], '雅虎'],
|
||||
'apple' => [['Applebot'], '苹果'],
|
||||
'gptbot' => [['GPTBot', 'ChatGPT-User', 'OAI-SearchBot'], 'OpenAI'],
|
||||
'claudebot' => [['ClaudeBot', 'anthropic-ai'], 'Anthropic'],
|
||||
'semrush' => [['SemrushBot'], 'Semrush'],
|
||||
'ahrefs' => [['AhrefsBot'], 'Ahrefs'],
|
||||
'mj12' => [['MJ12bot'], 'Majestic'],
|
||||
'facebook' => [['facebookexternalhit', 'FacebookBot'], 'Facebook'],
|
||||
'baidu' => [['Baiduspider'], '百度', ['baidu.com', 'baidu.jp']],
|
||||
'google' => [['Googlebot', 'Google-InspectionTool', 'AdsBot-Google', 'Mediapartners-Google'], '谷歌', ['googlebot.com', 'google.com']],
|
||||
'bing' => [['bingbot', 'msnbot', 'BingPreview', 'adidxbot'], '必应', ['search.msn.com', 'msn.com']],
|
||||
'sogou' => [['Sogou web spider', 'Sogou inst spider', 'Sogou Pic Spider', 'Sogou Spider'], '搜狗', ['sogou.com', 'sogou.com.cn']],
|
||||
'so360' => [['360Spider', 'HaoSouSpider', 'qihoobot'], '360', ['360.cn', '360safe.com', 'qihoo.net']],
|
||||
'bytedance' => [['Bytespider', 'ToutiaoSpider'], '字节跳动', ['bytedance.com', 'bytespider.com']],
|
||||
'shenma' => [['YisouSpider'], '神马', ['sm.cn', 'yisou.com']],
|
||||
'huawei' => [['PetalBot'], '华为花瓣', ['huawei.com']],
|
||||
'yandex' => [['YandexBot', 'YandexImages', 'YandexMetricaBot'], 'Yandex', ['yandex.ru', 'yandex.net', 'yandex.com']],
|
||||
'duckduckgo' => [['DuckDuckBot', 'DuckDuckGo-Favicons-Bot'], 'DuckDuckGo', ['duckduckgo.com']],
|
||||
'yahoo' => [['Yahoo! Slurp'], '雅虎', ['yahoo.com', 'yahoo.net', 'crawl.yahoo.net']],
|
||||
'apple' => [['Applebot'], '苹果', ['apple.com']],
|
||||
'gptbot' => [['GPTBot', 'ChatGPT-User', 'OAI-SearchBot'], 'OpenAI', ['openai.com']],
|
||||
'claudebot' => [['ClaudeBot', 'anthropic-ai'], 'Anthropic', ['anthropic.com']],
|
||||
'semrush' => [['SemrushBot'], 'Semrush', ['semrush.com']],
|
||||
'ahrefs' => [['AhrefsBot'], 'Ahrefs', ['ahrefs.com']],
|
||||
'mj12' => [['MJ12bot'], 'Majestic', ['majestic12.co.uk', 'mj12bot.com']],
|
||||
'facebook' => [['facebookexternalhit', 'FacebookBot'], 'Facebook', ['facebook.com', 'fbsv.net', 'tfbnw.net']],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -81,4 +86,24 @@ class SpiderDetect
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 该蜘蛛的官方 rDNS 域名后缀列表(空数组 = 官方不提供 rDNS,无法做真伪校验)
|
||||
*
|
||||
* @param string $key 蜘蛛标识
|
||||
* @return string[]
|
||||
*/
|
||||
public static function rdnsSuffixes(string $key): array
|
||||
{
|
||||
$suffixes = self::SPIDERS[$key][2] ?? [];
|
||||
return is_array($suffixes) ? $suffixes : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 该蜘蛛是否支持 rDNS 真伪校验
|
||||
*/
|
||||
public static function verifiable(string $key): bool
|
||||
{
|
||||
return self::rdnsSuffixes($key) !== [];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user