Django:从Apache mod_rewrite重定向时的错误请求语法

Tom*_*ing 5 python apache django mod-rewrite

我在端口8000上运行Django,在80上运行Apache.我在apache中配置了以下重写规则以重定向到django:

RewriteRule ^/?checkout/ http://%{HTTP_HOST}:8000/checkout/ [L,QSA]
Run Code Online (Sandbox Code Playgroud)

如果在浏览器中打开网址,它可以正常工作并完美重定向.

但是,外部客户端(在没有apache的情况下直接连接到django时效果很好)总是会在Django服务器上导致错误的请求语法错误.来自Django Log的Heres片段.看起来Apache会自动将这些"Content-length"内容附加到查询中,为什么?

[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')
Run Code Online (Sandbox Code Playgroud)

use*_*130 7

tl; dr:这是由"外部客户端"中的错误引起的.它是一个设计糟糕的HTTP客户端,应该避免,因为它不仅会导致此错误,还可能为安全漏洞开辟道路.

为了理解正在发生的事情,您需要向后工作.


首先,让我们从Django内置服务器的日志行开始:

[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')
Run Code Online (Sandbox Code Playgroud)

"代码400"指的是HTTP状态代码400这意味着实际的HTTP请求严重构造,并且不能被理解.幸运的是,Django记录了错误的输入,因此我们可以对其进行分析.


现在我们了解了问题的本质,我们将删除不相关的日志绒毛和长签名,以深入了解实际请求:

GET /checkout/wx_signature?[SIGNATURE REMOVED]Content-Length: 445Connection: closeContent-Type: text/html; charset=iso-8859-1 HTTP/1.0
Run Code Online (Sandbox Code Playgroud)

在这里,我们看到HTTP请求的第一行无效.


来自RFC2616第5.1节:

Request-Line以方法标记开头,后跟Request-URI和协议版本,以CRLF结尾.元素由SP字符分隔.除最终的CRLF序列外,不允许使用CR或LF.

   Request-Line   = Method SP Request-URI SP HTTP-Version CRLF
Run Code Online (Sandbox Code Playgroud)

在无效请求中,我们可以识别HTTP动词GET是否存在且版本结尾HTTP/1.0是否存在,因此这些不是问题.中间部分,应该是URL如下:

/checkout/wx_signature?[SIGNATURE REMOVED]Content-Length: 445Connection: closeContent-Type: text/html; charset=iso-8859-1
Run Code Online (Sandbox Code Playgroud)

URL中的空格通常在发送到服务器之前+%20之前被替换.如您所见,这不是这种情况,它是无效请求的原因.一个好的HTTP客户端永远不会这样做,因为它会自动转义URL.这是一个红旗,您使用的"外部客户"质量很差.


请注意,空间旁边出现了许多看起来很奇怪的字段.

如果你看一下RFC2616第14.13节,你会发现它Content-Length实际上是HTTP 1.1标头的名称.这也是的情况下Connection,和Content-Type.

它显然不属于那里,为什么它与URL连接?

从这里我只能猜测,因为我无法访问您的代码.但是,我想我对发生的事情非常了解.


让我们暂时了解HTTP标头的本质.如果我们访问" http://google.com ",我们会发送原始请求来模拟会发生什么.这将触发Google将我们重定向到" http://www.google.com ".

原始请求:

GET / HTTP/1.1
Host: google.com
Run Code Online (Sandbox Code Playgroud)

原始响应:

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]
Run Code Online (Sandbox Code Playgroud)

哇,谷歌回复了一大堆标题!我们只关注前几行:

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
...
Content-Length: 219
Run Code Online (Sandbox Code Playgroud)

在这里,你可以看到Content-Type,Content-Length和其它标题跟随Location头.实际的顺序无关紧要,因为HTTP客户端或服务器足够聪明,可以理解每个客户端或服务器的含义.但是,如果在Location标题后删除行结尾怎么办?

你最终会得到这样的东西:

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/Content-Type: text/html; charset=UTF-8Content-Length: 219
Run Code Online (Sandbox Code Playgroud)

呃哦...如果你是一个HTTP客户端,你会认为我想重定向你http://www.google.com/Content-Type: text/html; charset=UTF-8Content-Length: 219.


这看起来就像你的症状......但为什么会这样呢?

Apache以这种损坏的形式返回标头是非常不可能的(除非你自定义编码插件或其他东西).

收到后,你的"外部客户" 也不太可能故意剥夺标题中的行结尾.

一个可能的情况是,"外部客户端"被编码为解释内容之前的所有内容以及之后的Location:URL以及之后的某个地方剥离CRLF字符(通常在处理HTTP标头时出于安全原因,在这种情况下讽刺地做错了) .客户端尝试使用HTTP/1.0而不是HTTP/1.1发送请求的事实支持这一点,因为HTTP/1.0客户端在功能方面通常非常有限,并且倾向于根据其过时的知识做出重大假设.

您的"外部客户端" 也可能在请求行之后将整个标头读入字符串,并且字符串处理程序会自动删除CRLF.


我认为很明显,问题在于"外部客户端",尽管没有足够的信息可以深入研究.

我建议您使用不同的客户端或库来执行请求.


Max*_*ant 2

当您在 Django 中使用 HTTPS url时,似乎会出现此消息。您可能还需要在 Apache2 中配置 HTTPS 配置,灵感来自于这个问题,例如:SSL based virtual host with django and mod_wsgi