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

时间:2014-03-05 18:05:11

标签: python django apache mod-rewrite

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

RewriteRule ^/?checkout/ http://%{HTTP_HOST}:8000/checkout/ [L,QSA]

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

但是,外部客户端(在没有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')

2 个答案:

答案 0 :(得分: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')

“代码400”指的是HTTP状态代码400.这意味着实际HTTP request 构造错误且无法理解。幸运的是,Django记录了错误的输入,因此我们可以对其进行分析。


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

GET /checkout/wx_signature?[SIGNATURE REMOVED]Content-Length: 445Connection: closeContent-Type: text/html; charset=iso-8859-1 HTTP/1.0

这里我们看到HTTP请求的第一行无效。


来自RFC2616 Section 5.1

  

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

   Request-Line   = Method SP Request-URI SP HTTP-Version CRLF

在无效请求中,我们可以确定HTTP动词GET在那里,版本以HTTP/1.0结尾,所以这些都不是问题。中间部分,应该是URL如下:

/checkout/wx_signature?[SIGNATURE REMOVED]Content-Length: 445Connection: closeContent-Type: text/html; charset=iso-8859-1

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


请注意,空间旁边出现了许多奇怪的字段。

如果查看RFC2616 Section 14.13,您会看到Content-Length实际上是HTTP 1.1标头的名称。 ConnectionContent-Type也是如此。

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

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


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

原始请求

GET / HTTP/1.1
Host: google.com

原始回复

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]

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

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
...
Content-Length: 219

您可以在此处看到Content-TypeContent-Length和其他标题都在Location标题后面。实际的顺序无关紧要,因为HTTP客户端或服务器足够聪明,可以理解每个客户端或服务器的含义。但是,如果在Location标题之后删除行结尾怎么办?

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

HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/Content-Type: text/html; charset=UTF-8Content-Length: 219

呃 - 哦......如果您是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。


我认为很明显问题出在“外部客户”中,尽管没有足够的信息可以深入研究。

我建议您使用其他客户端或库来执行请求。

答案 1 :(得分:2)

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