TOTP Vault DocsDeployment & Recovery
TOTP VAULT · OPERATIONS GUIDE · v1.2.0

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 不要删除。