← 返回首页
逆向2026 · 08 · 0454 分钟阅读

Gmail Sanitizer 深度拆解:8 步全链路凭据切断的自动化实现

引言

先问一个问题:你从卡网买了一个 Google 账号,改了密码,是不是就安全了?

不是。

改密码只切断了 6 条"绳子"中的 1 条。卖家仍然可以通过以下方式盗回你的账号:

| 后门 | 盗回方式 | | 恢复邮箱 | 通过"忘记密码"流程,Google 会把重置链接发到卖家的恢复邮箱 | | 恢复手机号 | 同上,短信验证码直接发到卖家手机 | | Passkey | 卖家的设备上注册了 Passkey,无需密码即可登录 | | TOTP 2FA 密钥 | 卖家手里有 Base32 密钥,能随时生成你的 2FA 验证码 | | 旧会话 / Cookie | 卖家的浏览器可能还保持着登录状态 | | OAuth 授权 | 授权过的第三方应用可以读取邮件、通讯录等数据 |

你只改了密码?卖家用恢复邮箱就能把密码改回来。你改了密码又关了 2FA?Passkey 还在。你把 Passkey 也删了?OAuth 授权的第三方应用还在偷偷读你的数据。

Gmail Sanitizer 要做的事情,就是一口气把这 6 条绳子全部切断。

一、核心功能:8 步全链路凭据切断

这个系统的核心是一条 8 步流水线,每一步精确对应一个后门:

| 步骤 | 操作 | 切断的后门 | 关键难点 | | Step 1 | 登录验证 | 验证旧凭据有效性 | 2FA 挑战、Account Chooser、中间提示页 | | Step 2 | 替换恢复邮箱 | 卖家的恢复邮箱 → 买家自建接码邮箱 | Google 可能要求邮箱验证码 | | Step 3 | 移除恢复手机 | 卖家绑定的手机号 | 可能需要 Sudo 二次验证 | | Step 4 | 删除所有 Passkey | 卖家设备的无密码登录凭据 | 循环删除,最多 20 个 | | Step 5 | 重置 2FA | 旧 TOTP 密钥 → 新 Base32 密钥 | 需要提取页面上的 Base32 密钥文本 | | Step 6 | 修改密码 + 全设备登出 | 旧密码 + 旧会话 | 密码强度校验、设备登出确认 | | Step 7 | 撤销 OAuth 授权 | 所有第三方应用权限 | 循环撤销,最多 30 个 | | Step 8 | 新凭据验证 | 确认所有切断生效 | 用新密码 + 新 2FA 重新登录 |

注意步骤顺序不是 1-2-3-4-5-6-7-8,而是 1-2-5-4-3-6-7-8——Step 5(重置 2FA)被提前到 Step 4(删除 Passkey)之前执行。为什么?因为 Step 5 需要在 Sudo Gate 中输入旧 TOTP 码来验证身份,如果先删了 Passkey 可能导致 Google 挑选 Passkey 作为 Sudo 验证方式却找不到,引发意外的验证失败。

1.1 Sudo Gate:Google 的二次验证拦截

这是整个系统最精巧的部分。

Google 在你修改安全设置时,会弹出"Sudo Gate"——一个二次身份验证页面。问题是,Sudo Gate 不是固定的,Google 会根据你的账号状态动态选择挑战方式:

<span class="line"><span class="cl"><span class="k">def</span> <span class="nf">handle_google_sudo_gate</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">page</span><span class="p">,</span> <span class="n">password</span><span class="p">,</span> <span class="n">totp_secret</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                            <span class="n">gmail</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">recovery_email</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">cycle</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">6</span><span class="p">):</span>  <span class="c1"># 最多循环 6 次</span>
</span></span><span class="line"><span class="cl">        <span class="n">current_url</span> <span class="o">=</span> <span class="n">page</span><span class="o">.</span><span class="n">url</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1"># 1. Account Chooser(会话过期)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="s2">"accountchooser"</span> <span class="ow">in</span> <span class="n">current_url</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 点击账号重新选择</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="c1"># 2. 2FA 方式选择页</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="s2">"challenge/selection"</span> <span class="ow">in</span> <span class="n">current_url</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 智能选择:优先 recovery email > TOTP</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="c1"># 3. 恢复邮箱验证 (challenge/kpe)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">is_rec_email_challenge</span> <span class="ow">and</span> <span class="n">recovery_email</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 输入恢复邮箱地址</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="c1"># 4. 密码重输 (challenge/pwd)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">is_pwd_challenge</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 重新输入当前密码</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="c1"># 5. TOTP 二次验证 (challenge/totp)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">is_totp_challenge</span> <span class="ow">and</span> <span class="n">totp_secret</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># 生成并输入 TOTP 验证码</span>
</span></span>

关键设计:while 循环。Google 可能在一次操作中连续弹出多种挑战(先要密码,再要 TOTP),所以 Sudo Gate 处理器设计为最多循环 6 次,每次检测当前 URL 和页面元素,动态选择对应的处理策略。不是 if-else,而是 for cycle in range(6)。

1.2 TOTP 密钥提取:从页面 HTML 中挖掘 Base32

Step 5 重置 2FA 时,需要从 Google 的 Authenticator 设置页面提取新的 Base32 密钥。这个密钥显示在页面上的格式是 4 字符一组、空格分隔的文本(如 ABCD EFGH IJKL MNOP)。

提取策略是两层漏斗:

<span class="line"><span class="cl"><span class="c1"># 第一层:正则匹配 4-8 组 Base32 字符块</span>
</span></span><span class="line"><span class="cl"><span class="n">m_groups</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="sa">r</span><span class="s2">"\b(?:[a-zA-Z2-7]</span><span class="si">{4}</span><span class="s2">\s+){3,7}[a-zA-Z2-7]</span><span class="si">{4}</span><span class="s2">\b"</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">    <span class="n">dialog_text</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">m_groups</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">candidate</span> <span class="o">=</span> <span class="n">m_groups</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">candidate</span><span class="p">)</span> <span class="ow">in</span> <span class="p">(</span><span class="mi">16</span><span class="p">,</span> <span class="mi">24</span><span class="p">,</span> <span class="mi">32</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">new_secret</span> <span class="o">=</span> <span class="n">candidate</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 第二层:暴力遍历所有元素文本</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="ow">not</span> <span class="n">new_secret</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">page</span><span class="o">.</span><span class="n">eles</span><span class="p">(</span><span class="s2">"xpath://div[@role='dialog']//..."</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">m</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="sa">r</span><span class="s2">"\b([a-zA-Z2-7]{16,32})\b"</span><span class="p">,</span> <span class="n">txt</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">" "</span><span class="p">,</span> <span class="s2">""</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">m</span> <span class="ow">and</span> <span class="n">candidate</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">"AUTHENTICATOR"</span><span class="p">,</span> <span class="s2">"VERIFICATION"</span><span class="p">,</span> <span class="o">...</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">            <span class="n">new_secret</span> <span class="o">=</span> <span class="n">candidate</span>
</span></span>

提取到密钥后,立即用 pyotp.TOTP(new_secret).now() 生成验证码填入确认框,完成 2FA 重置闭环。

1.3 断点续跑:任意步骤中断后恢复

每完成一个步骤,引擎立即将进度持久化:

<span class="line"><span class="cl"><span class="n">account</span><span class="o">.</span><span class="n">mark_step</span><span class="p">(</span><span class="s2">"step2_email_changed"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="bp">self</span><span class="o">.</span><span class="n">_save_sync</span><span class="p">(</span><span class="n">account</span><span class="p">)</span>
</span></span>

StepProgress 用 10 个布尔字段精确记录每一步的完成状态。重试时,引擎检查哪些步骤已完成,直接跳过:

<span class="line"><span class="cl"><span class="k">if</span> <span class="ow">not</span> <span class="n">account</span><span class="o">.</span><span class="n">step_progress</span><span class="o">.</span><span class="n">step2_email_changed</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">ok</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_step2_recovery_email</span><span class="p">(</span><span class="n">page</span><span class="p">,</span> <span class="n">cred</span><span class="p">,</span> <span class="n">account</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># ...</span>
</span></span>

这意味着如果 Step 5 因为网络超时失败,重试时会跳过 Step 1-4 直接从 Step 5 开始。

二、技术架构

2.1 双引擎设计

系统提供两套浏览器引擎,适配不同场景:

| | DrissionPage 引擎 | Playwright 引擎 | | Chrome 类型 | 真实 Chrome 进程 | Chromium 内核 | | 反检测能力 | 天然通过——就是真 Chrome | 需要 JS 注入伪装 | | 并发模式 | 每个任务独立端口 + Profile | 每个任务独立 BrowserContext | | 适用场景 | 生产环境(高通过率) | 开发调试(API 更友好) | | 进程隔离 | 每个账号独立 Chrome 进程 | 同一 Browser 下的 Context 隔离 |

DrissionPage 引擎是生产首选。它直接控制真实 Chrome 浏览器,Google 的反自动化检测基本无效——因为从 Google 的角度看,这就是一个普通用户在操作浏览器。

进程隔离的实现很巧妙:

<span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_make_options</span><span class="p">()</span> <span class="o">-></span> <span class="n">ChromiumOptions</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">port</span> <span class="o">=</span> <span class="n">_find_free_port</span><span class="p">()</span>  <span class="c1"># 每个任务随机端口</span>
</span></span><span class="line"><span class="cl">    <span class="n">opt</span><span class="o">.</span><span class="n">set_local_port</span><span class="p">(</span><span class="n">port</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># 独立 Profile 目录,防止 Chrome SingletonLock 死锁</span>
</span></span><span class="line"><span class="cl">    <span class="n">profile_dir</span> <span class="o">=</span> <span class="sa">f</span><span class="s2">"data/chrome_profiles/profile_</span><span class="si">{</span><span class="n">port</span><span class="si">}</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl">    <span class="n">opt</span><span class="o">.</span><span class="n">set_user_data_path</span><span class="p">(</span><span class="n">profile_dir</span><span class="p">)</span>
</span></span>

每个账号对应一个独立的 Chrome 实例(独立端口 + 独立 Profile),彻底避免了并发时的 Cookie 串扰和 SingletonLock 死锁。

2.2 反检测体系

Playwright 引擎需要额外的反检测处理。stealth_browser.py 实现了完整的指纹随机化:

<span class="line"><span class="cl"><span class="c1">// 隐藏 webdriver 属性
</span></span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">navigator</span><span class="p">,</span> <span class="s1">'webdriver'</span><span class="p">,</span> <span class="p">{</span><span class="nx">get</span><span class="o">:</span> <span class="p">()</span> <span class="p">=></span> <span class="kc">undefined</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 伪造 plugins(无头浏览器默认为空)
</span></span></span><span class="line"><span class="cl"><span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">navigator</span><span class="p">,</span> <span class="s1">'plugins'</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">get</span><span class="o">:</span> <span class="p">()</span> <span class="p">=></span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="s1">'Chrome PDF Plugin'</span><span class="p">,</span> <span class="nx">filename</span><span class="o">:</span> <span class="s1">'internal-pdf-viewer'</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span>

配合随机 User-Agent、随机分辨率、随机时区和随机语言:

<span class="line"><span class="cl"><span class="n">_USER_AGENTS</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"Chrome/125.0"</span><span class="p">,</span> <span class="s2">"Chrome/124.0"</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>  <span class="c1"># 5 种</span>
</span></span><span class="line"><span class="cl"><span class="n">_VIEWPORTS</span>   <span class="o">=</span> <span class="p">[</span><span class="mi">1920</span><span class="n">x1080</span><span class="p">,</span> <span class="mi">1440</span><span class="n">x900</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>             <span class="c1"># 5 种</span>
</span></span><span class="line"><span class="cl"><span class="n">_LOCALES</span>     <span class="o">=</span> <span class="p">[</span><span class="s2">"en-US"</span><span class="p">,</span> <span class="s2">"zh-CN"</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span>                 <span class="c1"># 5 种</span>
</span></span><span class="line"><span class="cl"><span class="n">_TIMEZONES</span>   <span class="o">=</span> <span class="p">[</span><span class="s2">"America/New_York"</span><span class="p">,</span> <span class="s2">"Asia/Shanghai"</span><span class="p">,</span> <span class="o">...</span><span class="p">]</span> <span class="c1"># 5 种</span>
</span></span>

每个 BrowserContext 从这些池子中随机组合,确保指纹不重复。

2.3 任务队列 + 死信队列

并发调度用经典的 asyncio 生产者-消费者模型:

┌──────────────┐     ┌──────────────┐
│   任务入队    │────▶│ asyncio.Queue │
└──────────────┘     └──────┬───────┘
                            │
              ┌─────────────┼─────────────┐
              ▼             ▼             ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │ Worker 0 │ │ Worker 1 │ │ Worker N │
        └────┬─────┘ └────┬─────┘ └────┬─────┘
             │             │             │
             ▼             ▼             ▼
        ┌──────────────────────────────────┐
        │  DrissionEngine.run_sync(task)   │
        └──────────┬───────────────────────┘
                   │
          ┌────────┴────────┐
          ▼                 ▼
    ┌──────────┐     ┌──────────┐
    │ VERIFIED │     │  FAILED  │──▶ 死信队列
    └──────────┘     └──────────┘     (指数退避重试)

死信队列的设计很讲究:

  • 不可重试类型自动隔离:WRONG_PASSWORD(密码错误)、ACCOUNT_DISABLED(封号)、CAPTCHA_BLOCKED(风控拦截)—— 这些重试也没用,直接进人工审核
  • 可重试类型指数退避:STEP_TIMEOUT(超时)、PROXY_ERROR(代理挂了)—— 等 2^n 秒后重试,最多 3 次
  • Passkey 误判恢复:如果失败详情包含 passkeyenrollment 或 speedbump,说明不是真正的失败而是卡在了中间提示页,强制纳入可重试队列

2.4 AES-256-GCM 加密冷库

所有敏感字段(新密码、TOTP 密钥、Cookie)在存入数据库前经过 AES-256-GCM 加密:

<span class="line"><span class="cl"><span class="k">class</span> <span class="nc">_Cipher</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">encrypt</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">plaintext</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">nonce</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">urandom</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span>  <span class="c1"># 96-bit 随机 nonce</span>
</span></span><span class="line"><span class="cl">        <span class="n">ct</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_aesgcm</span><span class="o">.</span><span class="n">encrypt</span><span class="p">(</span><span class="n">nonce</span><span class="p">,</span> <span class="n">plaintext</span><span class="o">.</span><span class="n">encode</span><span class="p">(),</span> <span class="kc">None</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span><span class="n">nonce</span> <span class="o">+</span> <span class="n">ct</span><span class="p">)</span><span class="o">.</span><span class="n">hex</span><span class="p">()</span>  <span class="c1"># nonce 和密文拼接后存储</span>
</span></span>

GCM 模式提供认证加密——不仅保密,还防篡改。即使数据库被拖库,没有 32 字节主密钥也无法解密。而且每次加密使用随机 nonce,相同明文产生不同密文,抵抗已知明文攻击。

三、验证码邮件管道

Step 2(替换恢复邮箱)可能触发 Google 的邮箱验证码。系统通过 Cloudflare Email Worker 实现秒级接码:

Google 发送验证码邮件
    ↓
Cloudflare Email Routing(Catch-All 规则)
    ↓
Email Worker(正则提取 6 位验证码)
    ↓
HTTP POST → 主程序回调接口
    ↓
主程序轮询取码 → 填入浏览器

Email Worker 的正则提取覆盖了 Google 验证码邮件的多种模板:

<span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">codePatterns</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="sr">/\b([0-9]{6})\b(?=\s*(?:is your|是您的|verification|验证|code))/i</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="sr">/(?:verification code|验证码)[^\d]*([0-9]{6})/i</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="sr">/G-([0-9]{6})/i</span><span class="p">,</span>          <span class="c1">// Google 特定格式 "G-123456"
</span></span></span><span class="line"><span class="cl">  <span class="sr">/\b([0-9]{6})\b/</span><span class="p">,</span>         <span class="c1">// 兜底:提取第一个 6 位数字
</span></span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span>

如果邮件通道超时(30 秒),系统还有一个优雅降级——直接在终端提示操作者手动输入验证码:

<span class="line"><span class="cl"><span class="k">if</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">isatty</span><span class="p">():</span>
</span></span><span class="line"><span class="cl">    <span class="n">user_code</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s2">"6-digit code (or Enter to skip): "</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span>
</span></span>

四、安全设计亮点

| 设计 | 实现 | 目的 | | 进程隔离 | 每账号独立 Chrome 进程 + 独立端口 + 独立 Profile | 防 Cookie 串扰、防 SingletonLock | | 加密存储 | AES-256-GCM + 随机 Nonce | 数据库脱库也无法解密 | | 人类行为模拟 | random.uniform(0.8, 2.0) 秒操作间隔 + Worker 领任务前 1.5-5.0 秒抖动 | 降低 Google 风控触发率 | | 中间提示页自动跳过 | 智能识别 Passkey 引导、恢复邮箱确认等弹窗 | 防止流程卡死 | | 密码强度保证 | 18 位、大小写 + 数字 + 符号、secrets 模块生成 | 通过 Google 密码强度校验 | | 密码热切换 | Step 6 修改密码后立即 cred.password = new_password | 后续步骤用新密码通过 Sudo Gate |

五、与同类方案对比

| 维度 | 手动操作 | 简单脚本 | Gmail Sanitizer | | 覆盖后门数 | 取决于经验(通常漏 2-3 个) | 1-2 个(通常只改密码) | 全部 6 类后门 | | 单号耗时 | 15-30 分钟 | 3-5 分钟 | 1-3 分钟 | | 并发能力 | 1 | 1-3 | 20+(可配置) | | 断点续跑 | 不支持 | 不支持 | 支持(10 步粒度) | | 失败自愈 | 手动重试 | 无 | 死信队列 + 指数退避 | | 凭据保护 | 明文 | 明文 | AES-256-GCM | | 反检测 | 不需要 | Selenium(易被检测) | 真实 Chrome / 指纹随机化 | | Sudo Gate 处理 | 人工判断 | 通常卡住 | 6 种挑战类型自动适配 |

写在最后

Gmail Sanitizer 这个项目的技术含量不在于"自动化"本身——Selenium 填表单谁都会。它的价值在于对 Google 安全机制的深度理解:知道 Google 有哪些后门、知道 Sudo Gate 有哪些挑战类型、知道 Base32 密钥在页面上以什么格式显示、知道修改密码后旧 TOTP 密钥还能用多久。

从工程角度看,断点续跑 + 死信队列 + 加密冷库 的组合,让它从"一次性脚本"变成了"可运维的生产系统"。这是大多数同类工具缺失的维度。

对于大体量的 Google 账号采购场景,先批量跑一遍 8 步清洗,再投入使用——这是成本最低的安全保障。

原文出处blog.caowo.de