<p>dr:这是由“外部客户机”中的一个错误引起的。它是一个设计糟糕的HTTP客户机,应该避免,因为它不仅会导致此错误,还可能为安全漏洞利用开辟途径。在</p>
<p>为了了解发生了什么,你需要反向工作。在</p>
<hr/>
<p>首先,让我们从Django内置服务器的日志行开始:</p>
<pre><code>[05/Mar/2014 18:01:35] code 400, message Bad request syntax ('GET /checkout/wx_signature?signature=b226bb8f6e9ce2fdecb752c6808a979c62e235f7&echostr=5987526888415258224&timestamp=1394042480&nonce=1394079741Content-Length: 445Connection: closeContent-Type: text/html; charset=iso-8859-1 HTTP/1.0')
</code></pre>
<p>“代码400”是指HTTP状态代码400。这意味着实际的<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html" rel="noreferrer">HTTP request</a>结构不好,无法理解。幸运的是,Django记录了错误的输入,这样我们就可以分析它了。在</p>
<hr/>
<p>既然我们了解了问题的本质,我们将删除不相关的日志绒毛和长签名,以便更深入地了解实际请求:</p>
^{2}$
<p>这里我们看到一个无效的HTTP请求的第一行。在</p>
<hr/>
<p>从<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1" rel="noreferrer">RFC2616 Section 5.1</a>:</p>
<blockquote>
<p>The Request-Line begins with a method token, followed by the Request-URI and the protocol version, and ending with CRLF. The elements are separated by SP characters. No CR or LF is allowed except in the final CRLF sequence.</p>
<pre><code> Request-Line = Method SP Request-URI SP HTTP-Version CRLF
</code></pre>
</blockquote>
<p>在无效请求中,我们可以确定HTTP动词<code>GET</code>在那里,而{<cd2>}的版本结尾在那里,所以这些不是问题所在。中间部分,即URL,如下所示:</p>
<pre><code>/checkout/wx_signature?[SIGNATURE REMOVED]Content-Length: 445Connection: closeContent-Type: text/html; charset=iso-8859-1
</code></pre>
<p>在发送到服务器之前,URL中的空格通常被<code>+</code>或{<cd4>}替换。如您所见,这里不是这种情况,这是无效请求的原因。一个好的HTTP客户机永远不会这样做,因为它会自动转义URL。<strong>这是一个危险信号,表明您使用的“外部客户”质量低劣。</strong></p>
<hr/>
<p>注意,这个空间出现在一些看起来很奇怪的区域旁边。在</p>
<p>如果您查看<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13" rel="noreferrer">RFC2616 Section 14.13</a>,您将看到<code>Content-Length</code>实际上是HTTP1.1头的名称。这也是<code>Connection</code>,和<code>Content-Type</code>的情况。在</p>
<p>它显然不属于那里,那么为什么它与URL连接?在</p>
<p>从这里我只能猜测,因为我不能访问你的代码。不过,我想我对发生的事情有一个很好的了解。在</p>
<hr/>
<p>让我们先了解一下HTTP头的性质。我们将发送一个原始请求来模拟访问“<a href="http://google.com" rel="noreferrer">http://google.com</a>”时发生的情况。这将触发Google将我们重定向到“<a href="http://www.google.com" rel="noreferrer">http://www.google.com</a>”。在</p>
<p><strong>原始请求</strong>:</p>
<pre><code>GET / HTTP/1.1
Host: google.com
</code></pre>
<p><strong>原始响应</strong>:</p>
<pre><code>HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
Date: Thu, 15 May 2014 21:28:46 GMT
Expires: Sat, 14 Jun 2014 21:28:46 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 219
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Alternate-Protocol: 80:quic
[HTML content removed]
</code></pre>
<p>哇,谷歌返回了一大堆标题!我们只对前几行感兴趣:</p>
<pre><code>HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
...
Content-Length: 219
</code></pre>
<p>在这里您可以看到<code>Content-Type</code>,<code>Content-Length</code>,以及其他头文件都跟在<code>Location</code>头之后。实际的顺序通常并不重要,因为HTTP客户机或服务器足够智能,能够理解每一个客户机或服务器的含义。但是,如果去掉<code>Location</code>头之后的行尾怎么办?在</p>
<p>你最终会得到这样的结果:</p>
<pre><code>HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/Content-Type: text/html; charset=UTF-8Content-Length: 219
</code></pre>
<p>哦哦。。。如果您是HTTP客户机,您会认为我希望将您重定向到<code>http://www.google.com/Content-Type: text/html; charset=UTF-8Content-Length: 219</code>。在</p>
<hr/>
<p>这看起来和你的症状一模一样。。。但为什么会这样呢?在</p>
<p>Apache不太可能以这种损坏的形式返回头(除非您自定义了一个插件或其他类似的代码)。在</p>
<p>你的“外部客户”也不太可能在收到邮件后故意删除邮件头中的行尾。在</p>
<p><em>很可能的情况是,“外部客户机”被编码为将内容之前和<code>Location:</code>之后的所有内容解释为URL,然后在某处去除CRLF字符(通常在处理HTTP报头时出于安全原因而这样做,讽刺的是,在本例中错误地处理)。客户端试图用HTTP/1.0而不是HTTP/1.1发送请求,这一事实支持这一点,因为HTTP/1.0客户端通常在功能方面非常有限,并且往往基于其过时的知识做出大量假设。在</p>
<p>您的“外部客户机”很可能会将请求行之后的整个头读入字符串,并自动将字符串处理程序读入字符串我剥了CRLF。在</p>
<hr/>
<p>我认为很明显问题出在“外部客户”身上,尽管没有足够的信息来挖掘它。在</p>
<p>我建议您使用不同的客户机或库来完成请求。在</p>