惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
Hacker News: Ask HN
Hacker News: Ask HN
V
Visual Studio Blog
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
I
InfoQ
A
About on SuperTechFans
宝玉的分享
宝玉的分享
Project Zero
Project Zero
C
CXSECURITY Database RSS Feed - CXSecurity.com
量子位
The Register - Security
The Register - Security
大猫的无限游戏
大猫的无限游戏
Blog — PlanetScale
Blog — PlanetScale
Simon Willison's Weblog
Simon Willison's Weblog
C
Cyber Attacks, Cyber Crime and Cyber Security
T
The Exploit Database - CXSecurity.com
Cyberwarzone
Cyberwarzone
L
LINUX DO - 热门话题
博客园 - 聂微东
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Privacy International News Feed
T
Tenable Blog
博客园 - 叶小钗
P
Palo Alto Networks Blog
S
Securelist
F
Full Disclosure
Help Net Security
Help Net Security
爱范儿
爱范儿
Cisco Talos Blog
Cisco Talos Blog
F
Fortinet All Blogs
Google DeepMind News
Google DeepMind News
IT之家
IT之家
S
Secure Thoughts
Martin Fowler
Martin Fowler
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
G
GRAHAM CLULEY
J
Java Code Geeks
Forbes - Security
Forbes - Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
A
Arctic Wolf
L
LangChain Blog
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
月光博客
月光博客
T
The Blog of Author Tim Ferriss
Engineering at Meta
Engineering at Meta
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

Shiroha白羽的博客

Golang 踩坑 —— interface 为参数的时候传 nil 指针 Codeforces Round 925 (Div. 3) Codeforces Round 924 (Div. 2) Codeforces Round 923 (Div. 3) Codeforces Round 922 (Div. 2) Codeforces Round 921 (Div. 2) Educational Codeforces Round 161 (Rated for Div. 2) Codeforces Round 920 (Div. 3) Codeforces Round 919 (Div. 2) Hello 2024 Good Bye 2023 Codeforces Round 918 (Div. 4) 个人备份的常用 macOS 清理命令 Codeforces Round 917 (Div. 2) Pinely Round 3 (Div. 1 + Div. 2) Educational Codeforces Round 160 (Rated for Div. 2) Codeforces Round 915 (Div. 2) Codeforces Round 914 (Div. 2) Codeforces Round 913 (Div. 3) Educational Codeforces Round 159 (Rated for Div. 2) Codeforces Round 912 (Div. 2) Codeforces Round 911 (Div. 2) CodeTON Round 7 (Div. 1 + Div. 2, Rated, Prizes!) Educational Codeforces Round 158 (Rated for Div. 2) Codeforces Round 910 (Div. 2) Codeforces Round 909 (Div. 3) Codeforces Round 908 (Div. 2) Educational Codeforces Round 157 (Rated for Div. 2) C++自定义的字面量 Codeforces Round 907 (Div. 2) Codeforces Round 916 (Div. 3) 关于 LRU map 的一些灵感 2023 杭州站 ICPC 现场赛 反复横跳的 Clang-Tidy(cert-dcl21-cpp) Codeforces Round 906 (Div. 2) 一段奇怪的 CPP 代码 Codeforces Round 905 (Div. 3) Codeforces Round 904 (Div. 2) Codeforces Round 903 (Div. 3) Educational Codeforces Round 156 (Rated for Div. 2) Codeforces Round 902 (Div. 2, based on COMPFEST 15 - Final Round) Codeforces Round 901 (Div. 2) Codeforces Round 900 (Div. 3) Codeforces Round 899 (Div. 2) Educational Codeforces Round#155 (Div. 2) Codeforces Round 898 (Div. 4) CodeTON Round 6 (Div. 2) Codeforces Round 897 (Div. 2) Codeforces Round 896 (Div. 2) Codeforces Round 887 (Div. 2) Codeforces Round 895 (Div. 3) 左值-右值-将亡值 blog.mauve.icu Pinely Round 2 (Div. 1 + Div. 2) Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2) Codeforces Round 894 (Div. 3) Codeforces Round 888 (Div. 3) Educational Codeforces Round#153 (Div. 2) Codeforces Round 893 (Div. 2) Codeforces Round 892 (Div. 2) Codeforces Round 891 (Div. 3) Codeforces Round 890 (Div. 2) Educational Codeforces Round#152 (Div. 2) blog.mauve.icu Java Script 的 null 和 undefined 随想 记一次 SQL LEFT JOIN 没有得到预期结果的错误 Codeforces Round#789(Div. 2) GCC/G++ 预编译头性能优化 使用 Junit5 和 Mockito 实现 SpringBoot 的单元测试最优美的解决方案 centOS 防火墙 docker-compse 的问题 C++ 语言实现动态变化的线程池 Codeforces Round#744 (Div. 3) 计算机图形学 Windows 通过网络访问 WSL2 原生 JavaScript 实现图片裁剪 面试复习(计算机图形学) 面试复习(算法) Codeforces Round#706(Div. 2)-Let's Go Hiking 面试复习(Java) 面试复习(Git) 面试复习(Linux) 面试复习(数据库) 面试复习(计算机网络) 面试复习(操作系统) 面试复习(C++) Codeforces Round#699 (Div. 2) 清理 WSL2 的磁盘占用 Codeforces Round#697 (Div. 3) Windows 下的 NTFS 驱动器索引 BUG 计算机网络复习 记一次 Navicat 连接 MySQL 一直报认证错误(Access denied) 计算机网络实验复习 WSL1 使用 Docker 一直无法启动 我的ACM脚印 2020牛客暑期多校训练营(第三场)D-Points Construction Problem——构造 2020牛客暑期多校训练营(第三场)E-Two Matchings——复杂思维与简单dp 2020牛客暑期多校训练营(第二场)I-Interval——最大流转对偶图求最短路 Educational Codeforces Round 80 D. Minimax Problem——二分+二进制处理 Codeforces Round 606 E. Two Fairs——图论 Codeforces Round 612 (Div. 2) C. Garland——DP
OTPAUTH,两步验证中的通用协议
Shiroha · 2023-08-16 · via Shiroha白羽的博客

起因

昨天突然 GitHub 给我发了一封邮件,要求我必须添加 2FA 的验证

GitHub-email

好吧好吧,那就创建吧。但是在创建的过程中,GitHub 问我是否有使用 1Password 之类的软件,如果有的话,可以扫码添加 2FA

嗯???因为我基本上是躺在 Apple 生态里的,所以选择让 Apple 自带的密码管理系统试试看,于是直接扫码了 GitHub 提供的二维码

结果扫码之后果真添加了 2FA 的能力

password

这个验证码很像 steam 使用的那种自生成的两步验证码,这让我觉得似乎有一种通用协议,来实现这样一套东西。立马开整

第一步,再搞到一份这样的二维码。我翻阅了 GitHub 的文档,最终找到了一份 GitHub 提供的示例二维码

qr-code

接着直接读取二维码内的信息得到了这样一个地址: otpauth://totp/GitHub:octocat-testing?secret=F76W4SX573PDRIDA&issuer=GitHub

嗯,看起来是有一个用 OTPAUTH 的验证协议,其中 OTP 大概率就是 One Time Password 了。

OTP 协议

一般 OTP 有两种策略:

计次使用(HOTP)和计时使用(TOTP)。计次使用的密码使用过一次就失效;计时使用的密码过一段时间就失效。

HOTP 的全称是 HMAC-based One Time Password,它是基于 HMAC 的一次性密码生成算法。HMAC 的全称是 Hash-based Message Authentication Code,是指密钥相关的哈希运算消息认证码。HMAC 利用 MD5、SHA-1 等哈希算法,针对输入的密钥和消息,输出消息摘要。HOTP 算法中,传入密钥 K 和计数器 C,得到数字校验码。

实际使用 HOTP 中,服务端会给用户生成密钥 K,并约定起始计数器 C。客户端根据 K 和 C 生成校验码,并在用户点击刷新按钮后将计数器加 1,同时更新校验码;而服务端会在每次校验成功后将计数器加 1,这就保证了校验码只能使用一次。但客户端刷新并不通知服务端,很可能出现客户端计数器大于服务端的情况。所以一般的实现里,服务端如果用 PASSWORD = HOTP(K, C) 验证失败,还会尝试 C+1、C+2…,如果匹配上了,就更新服务端的计数器,保证跟客户端步调一致。出于安全考虑,服务端会设置一个最大值,并不会无限制地尝试下去。

HOTP 的优点是可以事先算好一批校验码,用户可以把他们打印出来随身携带逐个使用,用一个划掉一个,达到客户端计数器累加的效果,这样可以完全不依赖于电子设备。HOTP 的缺点是计数器很容易不一致,服务端经常需要通过不断尝试来同步计数器,从而降低了安全性。

TOTP 的全称是 Time-based One-time Password,它是基于时间的一次性密码生成算法。TOTP 算法需要约定一个起始时间戳 T0,以及间隔时间 TS。把当前时间戳 now 减去 T0,用得到的时间差除以 TS 并取整,可以得到整数 TC。根据 PASSWORD = HOTP(K, TC) 就可以得到数字校验码。

TOTP 实际上只是把 HOTP 的递增计数器换成了与当前时间有关的 TS,从而在服务端 / 客户端时间一致的前提下,解决了 HOTP 需要同步计数器的问题。同时,TOTP 算法需要用到当前时间,需要现场计算,无法提前算好打印出来。默认情况下,TOTP 在间隔时间 TS 内都能通过校验,并不是一次有效。这个问题可以通过在服务端记录最后一次 TC 来解决,由于 TS 一般很短,通常也可以忽略。

翻阅 google-authenticator 的 wiki 可以看到,这里有非常详细关于 URL 的参数的描述

Secret

  • REQUIRED: The secret parameter is an arbitrary key value encoded in Base32 according to RFC 3548. The padding specified in RFC 3548 section 2.2 is not required and should be omitted.

Issuer

  • STRONGLY RECOMMENDED: The issuer parameter is a string value indicating the provider or service this account is associated with, URL-encoded according to RFC 3986. If the issuer parameter is absent, issuer information may be taken from the issuer prefix of the label. If both issuer parameter and issuer label prefix are present, they should be equal.

  • Valid values corresponding to the label prefix examples above would be: issuer=Example, issuer=Provider1, and issuer=Big%20Corporation.

  • Older Google Authenticator implementations ignore the issuer parameter and rely upon the issuer label prefix to disambiguate accounts. Newer implementations will use the issuer parameter for internal disambiguation, it will not be displayed to the user. We recommend using both issuer label prefix and issuer parameter together to safely support both old and new Google Authenticator versions.

Algorithm

  • OPTIONAL: The algorithm may have the values:
    • SHA1 (Default)
    • SHA256
    • SHA512

Currently, the algorithm parameter is ignored by the Google Authenticator implementations.

Digits

  • OPTIONAL: The digits parameter may have the values 6 or 8, and determines how long of a one-time passcode to display to the user. The default is 6.

Currently, on Android and Blackberry the digits parameter is ignored by the Google Authenticator implementation.

Counter

  • REQUIRED if type is hotp: The counter parameter is required when provisioning a key for use with HOTP. It will set the initial counter value.

Period

  • OPTIONAL only if type is totp: The period parameter defines a period that a TOTP code will be valid for, in seconds. The default value is 30.

Currently, the period parameter is ignored by the Google Authenticator implementations.