diff --git a/_drafts/Article/Translation/how-i-would-do-auth.md b/_drafts/Article/Translation/how-i-would-do-auth.md
index 34de183..9a91494 100644
--- a/_drafts/Article/Translation/how-i-would-do-auth.md
+++ b/_drafts/Article/Translation/how-i-would-do-auth.md
@@ -1,5 +1,5 @@
 ---
-title: How I would do auth
+title: 我会如何实现身份验证
 date: 2025-03-10T03:29:26.732Z
 authorURL: ""
 originalURL: https://pilcrowonpaper.com/blog/how-i-would-do-auth/
@@ -7,10 +7,101 @@ translator: ""
 reviewer: ""
 ---
 
-Copyright © pilcrowonpaper
+这是一篇关于我如何为面向公众的应用实现身份验证的简短文章。这不会是太深入或权威的内容 - 只是我当前观点的集合。不过,对一些人来说,它可能作为入门指南很有用。
 
-<!-- more -->
+首先,如果应用是为开发者设计的,而我需要一些非常快速的东西,我会直接使用 GitHub OAuth。10 分钟内搞定。
 
-[Source code][1]
+现在进入主要部分 - 我会如何实现基于密码的身份验证?对我来说,最低要求是密码加上使用认证器应用的双因素认证(2FA)。密钥通行证(Passkeys)还不够普及,而我只是觉得魔术链接(magic-links)很烦人。
 
-[1]: https://github.com/pilcrowonpaper/pilcrow
\ No newline at end of file
+> 始终实现速率限制,即使是非常基础的实现!
+
+会话管理
+------------------
+
+100%使用数据库会话。我真的非常不喜欢 JWT,大多数情况下它们不应该被用作会话。
+
+假设我只需要处理已认证的会话,我首选的方法是 30 天过期,但每次使用会话时都会延长过期时间。这确保活跃用户保持认证状态,而不活跃用户则会被登出。
+
+注册
+------------
+
+有争议的观点 - 我认为应用分享电子邮件是否已存在于系统中是可以的。如果电子邮件已被占用,只需告诉用户他们已经有一个账户。显著更好的用户体验,安全性损失很小。如果你不喜欢这样,就不要使用电子邮件进行身份验证。
+
+无论如何,比防止用户枚举更重要的是检查密码是否在之前的泄露中出现过。[`haveibeenpwned.com`][1] API 可能是这方面的最佳选择。这将减少凭证填充攻击的有效性,即攻击者使用从其他网站泄露的密码来攻击账户。
+
+密码使用 Argon2id 或 Scrypt 进行哈希处理 - 它们都足够好。Bcrypt 也可以,但不幸的是它有 50-70 个字符的限制。
+
+速率限制将设置为每个 IP 地址每秒约 1 次尝试。如果开始收到垃圾信息,则使用验证码。
+
+### 电子邮件验证
+
+首先,我不会费心使用那些 100 个字符长的正则表达式。这是你唯一需要的电子邮件正则表达式:
+
+```regex
+^.+@.+\..+$
+```
+
+我还会检查电子邮件是否以空格开头或结尾,以确保用户没有输错。
+
+对于电子邮件验证,我个人更喜欢一次性密码(OTP)而不是链接,但两者都可以。对于 OTP,每个账户每小时 5-10 次尝试的基本限制应该足够了。验证码将在 10 分钟,最多 15 分钟内有效。对于验证链接,我会将过期时间设置为 2 小时。
+
+以下是我生成这些 OTP 的一些方法:
+
+```golang
+bytes := make([]byte, 5)
+rand.Read(bytes)
+// 8个字符,40位熵
+// 我可能会使用自定义字符集来移除1、I、0和O。
+otp := base32.StdEncoding.EncodeToString(bytes)
+```
+
+```golang
+// 8个字符,熵相当于~26位
+// 这引入了微小的偏差。
+// 参见RFC 4226了解为什么这样做没问题。
+bytes := make([]byte, 4)
+rand.Read(bytes)
+num := int(binary.BigEndian.Uint32(bytes) % 100000000)
+otp := fmt.Sprintf("%08d", num)
+```
+
+登录
+-----
+
+同样,如果电子邮件无效,只需返回"账户不存在"。
+
+登录限制将基于电子邮件。增加超时时间直到 5-10 分钟(例如 1、2、4、8、15、30、60、120、300 秒)。你不希望时间更长,以防止攻击者通过故意失败来阻止合法尝试。基于 IP 地址的速率限制将设置为每个 IP 每秒 1 次尝试。多个用户可以共享 IP 地址,所以我不想在这里太严格。无论如何,我对登录限制不太担心,因为我们在注册期间检查密码强度,并且我们启用了 2FA。
+
+如果我真的担心账户锁定,我可能会考虑实现[设备 cookie][2],尽管如果我处理这类攻击,我可能需要监控请求并手动阻止请求。
+
+2FA
+---
+
+对我来说,2FA 是必须的。应该始终提供通过认证器应用(TOTP)的 2FA。它对用户来说相对容易使用,对我来说也容易实现。密钥通行证和安全密钥将是我的下一个优先事项(注册密钥通行证的用户也应该被允许使用它们代替密码+2FA)。另一方面,SMS 成本高且容易受到 SIM 卡交换攻击。我会避免使用它。反正没人喜欢它们。
+
+对于 TOTP,同样每个账户每小时 5-10 次尝试的基本限制应该足够了。对于密钥通行证,可能是每个 IP 地址每秒 1 次尝试。暴力破解密钥通行证是不可能的,但验证签名在某种程度上是资源密集型的,因此可能容易受到 DoS 攻击。
+
+> 密钥通行证可以用作第二因素,也可以作为密码的替代品,但安全密钥应该只用作第二因素。
+
+可选的,但如果我要实现恢复码,我会生成 5 或 10 个字节并进行 base32 编码,就像我在电子邮件验证部分展示的那样。使用 Argon2id/Scrypt 对它们进行哈希处理,并给用户随时重新生成它们的选项。它将是一次性使用的,使用它将断开与账户关联的所有 2FA 方法。我在这里会对尝试次数相当严格,设置为每个账户每小时或甚至每天 5 次尝试。
+
+最后,用户应该使用他们的第二因素之一进行身份验证,然后才能编辑他们的 2FA 方法。这对用户来说会开始变得烦人,所以我只会每小时(或更少)要求一次,通过在会话中存储他们上次使用 2FA 的时间。
+
+密码重置
+--------------
+
+同样,我不介意告诉用户电子邮件是否有效。
+
+登录限制和速率限制将与登录非常相似,并将基于电子邮件和 IP 地址。必要时添加验证码。
+
+一次性 OTP 和链接都可以工作,它们的过期时间将类似于电子邮件验证。我会对代码或令牌进行哈希处理以确保安全,特别是因为这并不难。
+
+即使是密码重置,也应该需要 2FA。
+
+我遗漏了什么吗?
+--------------------
+
+如果有任何我应该添加到文章中的内容,请在 Twitter 或 Discord 上告诉我!
+
+[1]: https://haveibeenpwned.com/
+[2]: https://owasp.org/www-community/Slow_Down_Online_Guessing_Attacks_with_Device_Cookies