From 699d2bd951aa54856fa64cbb9a6b547e007c7116 Mon Sep 17 00:00:00 2001 From: xcbyy <2578898740@qq.com> Date: Mon, 19 Oct 2026 13:36:36 +0800 Subject: [PATCH] =?UTF-8?q?security:=20=E5=90=8E=E5=8F=B0=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=8F=A3=E4=BB=A4=E7=94=9F=E4=BA=A7=20fail-closed?= =?UTF-8?q?=EF=BC=88=E4=BF=AE=E5=A4=8D=20admin/admin=20=E5=8F=AF=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E7=9A=84=E9=AB=98=E5=8D=B1=E6=BC=8F=E6=B4=9E=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 生产环境未配置 ADMIN_PASSWORD(或仍为 admin/admin123)时,后台登录直接 503 拒绝 - 启动自检新增两条告警:未配有效 ADMIN_PASSWORD、非 production 身份校验放宽 - 部署文档补充 ADMIN_PASSWORD/ADMIN_USERNAME 必配说明 - 新增回归护栏 admin-credential-hardening.spec.ts(88 套件/642 用例全绿);冒烟 5/5 --- docs/部署验收与1分钱支付闭环.md | 4 +++ server/src/index.ts | 23 ++++++++++++++- .../tests/admin-credential-hardening.spec.ts | 28 +++++++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 server/tests/admin-credential-hardening.spec.ts diff --git a/docs/部署验收与1分钱支付闭环.md b/docs/部署验收与1分钱支付闭环.md index fd1a7b4..be9e65d 100644 --- a/docs/部署验收与1分钱支付闭环.md +++ b/docs/部署验收与1分钱支付闭环.md @@ -14,6 +14,10 @@ PORT=3000 ADMIN_TOKEN_SECRET=<强随机长串> USER_TOKEN_SECRET=<另一个强随机长串> +# 后台登录口令(生产必配;未配置或仍为 admin/admin123 时后台登录被禁用) +ADMIN_USERNAME=<后台账号,默认 admin> +ADMIN_PASSWORD=<强口令;多个可用 | 分隔> + # 数据库 MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 diff --git a/server/src/index.ts b/server/src/index.ts index a4a7b6e..6ab8183 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -180,7 +180,10 @@ const ADMIN_TOKEN_SECRET = (() => { return DEFAULT_ADMIN_TOKEN_SECRET; })(); const ADMIN_USERNAME = process.env.ADMIN_USERNAME || 'admin'; -const ADMIN_PASSWORDS = (process.env.ADMIN_PASSWORD || 'admin|admin123').split('|').filter(Boolean); +const ADMIN_PASSWORD_SET = String(process.env.ADMIN_PASSWORD || '').trim(); +const ADMIN_PASSWORDS = (ADMIN_PASSWORD_SET || 'admin|admin123').split('|').filter(Boolean); +// 生产环境安全闸门:未配置 ADMIN_PASSWORD(或仍使用内置默认口令)时,禁止后台登录,避免默认口令被爆破 +const ADMIN_PASSWORD_CONFIGURED = Boolean(ADMIN_PASSWORD_SET) && !/^(admin|admin123)$/i.test(ADMIN_PASSWORD_SET); function signAdminToken(): string { const payload = Buffer.from(JSON.stringify({ role: 'admin', ts: Date.now() })).toString('base64url'); @@ -376,6 +379,14 @@ app.post('/api/user/login', (req: Request, res: Response) => { if (isLoginLocked(loginKey)) { return res.status(429).json({ code: 50000, message: '登录失败次数过多,请 10 分钟后再试' }); } + // 生产环境 fail-closed:未配置 ADMIN_PASSWORD(或仍为内置默认口令)时,禁止后台登录 + if (IS_PRODUCTION && !ADMIN_PASSWORD_CONFIGURED) { + logServerError('adminLogin', new Error('生产环境未配置 ADMIN_PASSWORD(或仍为默认口令),已禁用后台登录')); + return res.status(503).json({ + code: 50000, + message: '生产环境未配置后台登录口令:请设置环境变量 ADMIN_PASSWORD(不得为 admin/admin123)后重启服务', + }); + } // 管理凭证默认 admin/admin,可通过环境变量 ADMIN_USERNAME / ADMIN_PASSWORD 覆盖 const passwordOk = ADMIN_PASSWORDS.some((p) => safeStringEqual(String(password), p)); if (safeStringEqual(String(username), ADMIN_USERNAME) && passwordOk) { @@ -4497,6 +4508,16 @@ async function logStartupConfigSelfCheck(): Promise { console.log(`[Peiwan Config] ADMIN_TOKEN_SECRET=${adminSecretSet ? '已设置' : '开发默认值'} USER_TOKEN_SECRET=${userSecretSet ? '已设置' : '按 ADMIN 派生'}`); } + // 后台登录口令闸门:生产未配置(或为默认口令)时后台登录被禁用 + if (isProd && !ADMIN_PASSWORD_CONFIGURED) { + console.warn('[Peiwan Config][WARN] 未设置有效的 ADMIN_PASSWORD(不得为 admin/admin123):生产环境后台登录已被禁用,请尽快设置后重启。'); + } + + // 非生产环境身份校验放宽(允许客户端声明身份),禁止用于公网部署 + if (!isProd) { + console.warn('[Peiwan Config][WARN] 当前非 production:用户身份校验已放宽(未登录请求将采用客户端声明的 userId),仅限本地联调,切勿用于公网部署。'); + } + const cfg: any = await resolveWechatPayRuntimeConfig(); const missing: string[] = []; if (!cfg.appId) missing.push('appId'); diff --git a/server/tests/admin-credential-hardening.spec.ts b/server/tests/admin-credential-hardening.spec.ts new file mode 100644 index 0000000..c24239c --- /dev/null +++ b/server/tests/admin-credential-hardening.spec.ts @@ -0,0 +1,28 @@ +// 已读取AGENTS.md +import * as fs from 'fs'; +import * as path from 'path'; + +/** + * 第七十二批回归闸门:后台默认口令生产 fail-closed。 + * 背景:ADMIN_PASSWORD 未配置时默认 admin/admin123,公网部署等于开放后台。 + */ +describe('第七十二批后台凭证闸门', () => { + const root = path.resolve(__dirname, '../../'); + const indexCode = fs.readFileSync(path.join(root, 'server/src/index.ts'), 'utf-8'); + const doc = fs.readFileSync(path.join(root, 'docs/部署验收与1分钱支付闭环.md'), 'utf-8'); + + it('生产未配置 ADMIN_PASSWORD 时禁用后台登录(fail-closed)', () => { + expect(indexCode).toContain('ADMIN_PASSWORD_CONFIGURED'); + expect(indexCode).toContain('if (IS_PRODUCTION && !ADMIN_PASSWORD_CONFIGURED)'); + expect(indexCode).toContain('生产环境未配置后台登录口令'); + }); + + it('启动自检提示默认口令与非生产放宽风险', () => { + expect(indexCode).toContain('未设置有效的 ADMIN_PASSWORD'); + expect(indexCode).toContain('用户身份校验已放宽'); + }); + + it('部署文档包含 ADMIN_PASSWORD', () => { + expect(doc).toContain('ADMIN_PASSWORD'); + }); +});