Direct TOTP
第十一章 Direct TOTP 直达验证码页
说明v1.2.0 新增功能。现有首页、管理员后台、OneDrive、AGE 和数据库逻辑不变。
#57. 功能地址
浏览器 / Microsoft Entra🌐 执行位置:浏览器
普通首页仍然是:
https://2fa.example.com/
Direct TOTP 地址:
https://2fa.example.com/2fa/你的Base32Secret
公开测试值:
JBSWY3DPEHPK3PXP
测试:
https://2fa.example.com/2fa/JBSWY3DPEHPK3PXP
页面应显示当前 6 位验证码、30 秒倒计时、进度条和复制按钮,并自动刷新。
注意⚠️ 安全提醒
/2fa/{secret}的 Secret 位于浏览器 URL 中。它可能被浏览器历史记录保存,所以不要公开分享这类地址,也不要把真实 Secret 粘贴进工单、聊天、公开文档或截图。
#58. Direct TOTP 的计算位置
核对 / 预期结果✅ 核对说明
Direct 页面没有调用第三方验证码接口,而是继续使用:
public/assets/totp-core.js
浏览器端 Web Crypto 本地计算:
RFC 6238
SHA1
6 digits
30 seconds
Direct 页面不应:
写 database.sqlite
写 Session
写 Audit
写 localStorage
写 sessionStorage
调用第三方 TOTP API
使用 fetch/XHR/WebSocket/sendBeacon 发送 Secret
#59. Nginx 必须关闭 /2fa/ 日志
宝塔面板🧰 执行位置:宝塔面板 → 网站 → 站点 → 配置文件
在普通 location / 前加入:
location ^~ /2fa/ {
access_log off;
log_not_found off;
error_log /dev/null crit;
try_files /index.php =404;
include fastcgi_params;
fastcgi_pass unix:/tmp/php-cgi-82.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param HTTP_PROXY "";
fastcgi_read_timeout 60s;
fastcgi_hide_header X-Powered-By;
}
注意⚠️ 不要照抄错误 PHP 版本
如果你的站点实际使用 PHP 8.2,就必须是:
/tmp/php-cgi-82.sock只有真实切换到 PHP 8.3 后才改为
83。
CSP 必须继续保留:
form-action 'self' https://login.microsoftonline.com
否则 Microsoft OneDrive OAuth 会再次被浏览器安全策略阻止。
保存前检查:
/www/server/nginx/sbin/nginx -t
必须:
syntax is ok
test is successful
然后:
/etc/init.d/nginx reload
#60. Direct 页面 Header 验收
服务器 SSH / 终端🖥️ 执行位置:服务器 SSH / 终端
curl -kIsS 'https://2fa.example.com/2fa/JBSWY3DPEHPK3PXP'
至少核对:
Cache-Control: no-store
Referrer-Policy: no-referrer
X-Robots-Tag: noindex, nofollow, noarchive, nosnippet
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Security-Policy: ...
#61. 确认 Secret 没进入 Nginx 日志
服务器 SSH / 终端🖥️ 执行位置:服务器 SSH / 终端
先访问测试地址,然后:
grep -F 'JBSWY3DPEHPK3PXP' /www/wwwlogs/2fa.example.com.log
核对 / 预期结果✅ 正确结果:无输出
如果存在 error log:
grep -F 'JBSWY3DPEHPK3PXP' /www/wwwlogs/2fa.example.com.error.log
也应无输出。
#62. 与首页验证码对比
浏览器 / Microsoft Entra🌐 执行位置:浏览器
首页粘贴:
JBSWY3DPEHPK3PXP
同时打开:
https://2fa.example.com/2fa/JBSWY3DPEHPK3PXP
在同一个 30 秒时间窗内,两边 6 位验证码必须一致。
再测试小写:
https://2fa.example.com/2fa/jbswy3dpehpk3pxp
再测试非法内容:
https://2fa.example.com/2fa/INVALID0000
非法 Secret 应显示友好错误,不应 500。
#63. 从 v1.1.0 升级到 v1.2.0
服务器 SSH / 终端🖥️ 执行位置:服务器 SSH / 终端
先完整备份当前程序文件:
cd /www/wwwroot
cp -a 2fa.example.com "2fa.example.com-before-v1.2.0-$(date +%Y%m%d-%H%M%S)"
不要覆盖以下运行数据:
/www/server/totp-vault/data/database.sqlite
/www/server/totp-vault/secrets/vault.key
/www/server/totp-vault/secrets/secure-store.enc
上传新版源码后,只更新程序目录;不要重新运行会替换密钥的数据初始化逻辑。
更新完成后:
cd /www/wwwroot/2fa.example.com
/www/server/php/82/bin/php artisan totp:test
sudo -u www /www/server/php/82/bin/php artisan totp:health --online
重启 PHP:
/etc/init.d/php-fpm-82 restart
Nginx 修改后:
/www/server/nginx/sbin/nginx -t && /etc/init.d/nginx reload
#64. Direct TOTP 回滚
服务器 SSH / 终端🖥️ 执行位置:服务器 SSH / 终端
如果升级后异常,恢复升级前整站程序目录即可。运行数据目录 /www/server/totp-vault 不要删除。