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

标签 python django apache mod-rewrite

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

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

如果在浏览器中打开URL,它可以正常工作并且可以完美重定向。

但是,外部客户端(在不使用apache的情况下直接连接到django时,效果很好)始终会在Django服务器上导致错误请求语法错误。 Django日志的以下代码段。看起来Apache会自动将那些“内容长度”的内容附加到查询中,为什么呢?
[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')

最佳答案

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:

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.

   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 header 的名称。 ConnectionContent-Type也是这种情况。

它显然不属于该目录,因此为什么将其与URL串联?

由于无法访问您的代码,因此只能在这里进行猜测。但是,我认为我对所发生的事情有很好的了解。

让我们暂时了解一下HTTP header 的性质。我们将发送原始请求以模拟如果访问“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]

哇,Google返回了一大堆标题!不过,我们只对前几行感兴趣:
HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
...
Content-Length: 219

在这里,您可以看到Content-TypeContent-Length和其他 header 跟随在Location header 之后。通常,实际顺序并不重要,因为HTTP客户端或服务器足够聪明,可以理解每个客户端的含义。但是,如果您删除Location header 之后的行尾怎么办?

您将得到如下结果:
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不太可能以这种损坏的形式返回 header (除非您自定义编码插件或实现此目的的东西)。

您的“外部客户”也不太可能在收到报头后故意删除报头中的行尾。

一种可能的情况是“外部客户端”被编码为将内容之前和之后的Location:后的所有内容解释为URL,并在之后的某个地方剥离CRLF字符(出于处理安全性的考虑,通常在处理HTTP header 时这样做,在这种情况下,具有讽刺意味的是,这样做不正确)。客户端尝试使用 HTTP/1.0而不是HTTP/1.1 发送请求的事实支持了这一点,因为HTTP/1.0客户端通常在功能方面非常有限,并且往往基于过时的知识做出大量假设。

也可能是您的“外部客户端”在请求行之后将整个 header 读取为字符串,并且字符串处理程序自动剥离了CRLF。

我认为很明显,问题出在“外部客户”上,尽管没有足够的信息来挖掘它。

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

关于python - Django:从Apache mod_rewrite重定向时,错误的请求语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22205647/

相关文章:

Python 选择获取空值

django - 导入错误: No module named 'models' in Python 3

php - htaccess 多 URL 变量干净的 URL

django - 如何将可翻译文本(包括英语)放入 Django 中的文件中

django - Apache/Windows/mod_wsgi 上的多个 django 站点 - win32 的问题

apache - HTTPD 到底是什么?

java - 无法解析符号 'IOUtils'

python - 如何确保所有正则表达式候选字符串都可以匹配

python - 如何检查一列中的值是否等于另一列数据框中的值

python - 如何使用 tf.estimator 返回预测和标签(使用 predict 或 eval 方法)?