# -*- coding: utf-8 -*- r'''真正验证后台密码是否生效(不能只看 302)。 == 为什么要重做这个测试 == 第一版是这样: curl -X POST -d 'password=对的' /admin/login -> 302 curl -X POST -d 'password=错的' /admin/login -> 302 curl /admin -> 302 **三个都是 302** —— 说明状态码跟密码对不对**无关**,测试等于没测。 (实际行为:密码对了 302 到后台并种 cookie;密码错了 302 回登录页。 单看状态码区分不出来。) == 正确的验法 == 1. 正确密码 -> 存 cookie -> 带着 cookie 请求 /admin -> 应该拿到**后台页**(含「抢救报告」「下载区」) 2. 错误密码 -> cookie 是空的 -> 请求 /admin -> 应该拿到**登录页**(含 password 输入框) 3. 账号写错、密码对 -> 也应该进不去 4. 完全不带凭据 -> 登录页 5. 经 Cloudflare 的域名也走一遍 这样才能确定"密码真的改了,而且只认这个密码"。 == 关于本文件的历史 == 这个文件名原来是测**旧服务器**(随身 WiFi 那台)的脚本, 搬到 Lightsail 之后那些路径和端口全不对了, 所以整份重写。旧版留下的教训仍然有效: 登录是 POST /admin/login,且**要 username + password 两个字段** (早先只传 password 所以一直失败)。 == 又一个 % 格式化的坑 == 远端命令里有 curl 的 `%{http_code}`。**绝不能用 % 格式化拼**, 否则抛 "unsupported format character '{'"。 这一轮我踩了五次 —— 凡带百分号的远端命令一律用 replace 拼。 ''' from __future__ import annotations import os import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "_build")) from lssh import run # noqa: E402 USERNAME = "xmz7891" def _password() -> str: """从外部读密码 —— **绝不写死在代码里**(这个文件会进公开仓库)。 2026-10-08 这个仓库已经吃过一次"管理员密码硬编码进提交历史"的教训, 不能再犯。优先级: 1. 环境变量 YKXMZ_ADMIN_PASSWORD 2. _build/.admin_password(已 gitignore,一行密码) """ env = os.environ.get("YKXMZ_ADMIN_PASSWORD", "").strip() if env: return env f = Path(__file__).resolve().parent / ".admin_password" if f.is_file(): return f.read_text(encoding="utf-8").strip() raise SystemExit("没拿到密码:设 YKXMZ_ADMIN_PASSWORD," "或把密码写进 _build/.admin_password") PASSWORD = _password() BASE = "http://127.0.0.1:13245" DOMAIN_URL = "https://yk.xxmxxs.top" SCRIPT = r''' B=__BASE__ D=__DOMAIN__ U=__USER__ P=__PASS__ JAR=/tmp/ykxmz-cookie-test.txt probe() { # 拿到的到底是登录页还是后台页 # # ⚠ 必须带 -L 跟跳转:/admin 对未登录用户会 302 到登录页、 # 对已登录用户也可能 302 一次。不带 -L 拿到的是空 body # (第一版就是栽在这里,全显示"长度 0")。 body=$(curl -sL -b $JAR --max-time 15 "$1/admin") if echo "$body" | grep -q 'name=password'; then echo "登录页(没进去)" elif echo "$body" | grep -qE '抢救报告|下载区|下载文件'; then echo "后台页(进去了)" else echo "认不出来(长度 $(echo -n "$body" | wc -c))" fi } echo "════ 0. 账号与密码文件 ════" grep -m1 'ADMIN_USER_DEFAULT' /opt/ykxmz-relay/relay.py | sed 's/^/ /' printf ' 密码文件: '; ls -la /opt/ykxmz-relay/data/admin_password.txt \ | awk '{print $1, $3":"$4, $5" 字节"}' echo echo "════ 1. 正确密码 ════" rm -f $JAR code=$(curl -s -o /dev/null -w '%{http_code}' -c $JAR -X POST \ -d "username=$U" -d "password=$P" "$B/admin/login") echo " 登录 HTTP $code 种下 cookie: $(grep -c lr_admin $JAR 2>/dev/null || echo 0) 条" echo " 访问 /admin -> $(probe $B)" echo echo "════ 2. 错误密码 ════" rm -f $JAR code=$(curl -s -o /dev/null -w '%{http_code}' -c $JAR -X POST \ -d "username=$U" -d "password=definitely-wrong-xyz" "$B/admin/login") echo " 登录 HTTP $code 种下 cookie: $(grep -c lr_admin $JAR 2>/dev/null || echo 0) 条" echo " 访问 /admin -> $(probe $B)" echo echo "════ 3. 账号写错(密码对)════" rm -f $JAR code=$(curl -s -o /dev/null -w '%{http_code}' -c $JAR -X POST \ -d "username=someone-else" -d "password=$P" "$B/admin/login") echo " 登录 HTTP $code 种下 cookie: $(grep -c lr_admin $JAR 2>/dev/null || echo 0) 条" echo " 访问 /admin -> $(probe $B)" echo echo "════ 4. 完全不带凭据 ════" rm -f $JAR echo " 访问 /admin -> $(probe $B)" echo echo "════ 5. 经 Cloudflare 域名(正确密码)════" rm -f $JAR code=$(curl -s -o /dev/null -w '%{http_code}' -c $JAR -X POST \ -d "username=$U" -d "password=$P" "$D/admin/login") echo " 登录 HTTP $code 种下 cookie: $(grep -c lr_admin $JAR 2>/dev/null || echo 0) 条" echo " 访问 /admin -> $(probe $D)" echo echo "════ 6. 密码有没有泄漏到代码里 ════" if grep -rq "$P" /opt/ykxmz-relay/*.py /opt/ykxmz-relay/*.sh 2>/dev/null; then echo " ✗ 代码/脚本里出现了明文密码" else echo " ✓ 代码和脚本里都没有明文密码" fi rm -f $JAR ''' def main() -> int: print("=" * 62) print(" 真正验证后台密码") print("=" * 62) out = run(SCRIPT.replace("__BASE__", BASE) .replace("__DOMAIN__", DOMAIN_URL) .replace("__USER__", USERNAME) .replace("__PASS__", PASSWORD), timeout=300, quiet=True) print(out[1] if isinstance(out, tuple) else out) return 0 if __name__ == "__main__": sys.exit(main())