HTTP 参数未在 Apache 2.4 中断功能中发送
Posted
技术标签:
【中文标题】HTTP 参数未在 Apache 2.4 中断功能中发送【英文标题】:HTTP Parameters not being sent in Apache 2.4 breaking functionality 【发布时间】:2014-09-03 16:26:16 【问题描述】:让我们从一些背景开始。我有一个 3 层系统,在 django 中实现了一个 API,在 Apache2 服务器上运行 mod_wsgi。
今天我决定将运行在DigitalOcean 的服务器从 Ubuntu 12.04 升级到 Ubuntu 14.04。没什么特别的,只是 Apache2 也更新到了 2.4.7 版本。在浪费了一天的大部分时间弄清楚 they actually changed the default folder from /var/www to /var/www/html 破坏功能之后,我决定测试我的 API。没有接触任何一行代码,我的一些功能就无法正常工作。
我将使用其中一个较小的函数作为示例:
# Returns the location information for the specified animal, within the specified period.
@csrf_exempt # Prevents Cross Site Request Forgery errors.
def get_animal_location_reports_in_time_frame(request):
start_date = request.META.get('HTTP_START_DATE')
end_date = request.META.get('HTTP_END_DATE')
reports = ur_animal_location_reports.objects.select_related('species').filter(date__range=(start_date, end_date), species__localizable=True).order_by('-date')
# Filter by animal if parameter sent.
if request.META.get('HTTP_SPECIES') is not None:
reports = reports.filter(species=request.META.get('HTTP_SPECIES'))
# Add each information to the result object.
response = []
for rep in reports:
response.append(dict(
ID=rep.id,
Species=rep.species.ai_species_species,
Species_slug=rep.species.ai_species_species_slug,
Date=str(rep.date),
Lat=rep.latitude,
Lon=rep.longitude,
Verified=(rep.tracker is not None),
))
# Return the object as a JSON string.
return HttpResponse(json.dumps(response, indent = 4))
经过一些调试,我观察到request.META.get('HTTP_START_DATE')
和request.META.get('HTTP_END_DATE')
正在返回None
。我尝试了很多客户端,从 REST 客户端(例如 PyCharm 和 RestConsole for Chrome 中的客户端)到通常与 API 通信的 android 应用程序,但结果是一样的,这两个参数没有被发送。
然后我决定测试是否正在发送其他参数,令我惊恐的是,它们确实如此。在上述函数中,request.META.get('HTTP_SPECIES')
将具有正确的值。
在对名称进行了一番摆弄之后,我观察到 ALL 标题中包含 _
字符的参数不会进入 API。
所以我想,很酷,我将使用 -
而不是 _
,这应该可行,对吧?错误的。 -
以 _
的形式到达 API!
此时我完全迷惑了,所以我决定找出罪魁祸首。我使用 django 开发服务器运行 API,通过运行:
sudo python manage.py runserver 0.0.0.0:8000
当使用相同的客户端发送相同的参数时,它们会被 API 很好地拾取!因此,django 没有导致这个,Ubuntu 14.04 没有导致这个,唯一可能导致它的是 Apache 2.4.7!
现在将默认文件夹从/var/www
移动到/var/www/html
,从而破坏了功能,所有这些都是(在我看来)非常愚蠢的理由已经够糟糕了,但这太过分了。
有没有人知道这里实际发生了什么以及为什么?
【问题讨论】:
你的 .htaccess 是什么? 【参考方案1】:这是 Apache 2.4 中的一个变化。
这是来自Apache HTTP Server Documentation Version 2.4:
MOD CGI, MOD INCLUDE, MOD ISAPI, ... 将标头转换为环境变量比以前更严格 通过标头注入减轻一些可能的跨站点脚本攻击。包含无效字符的标头 (包括下划线) 现在被静默删除。 Apache 中的环境变量(第 81 页)有一些指示 关于如何解决需要此类标头的损坏的旧客户端。 (这会影响所有使用 这些环境变量。)
– 第 11 页
出于可移植性的原因,环境变量的名称只能包含字母、数字和下划线字符。此外,第一个字符可能不是数字。 在传递给 CGI 脚本和 SSI 页面时,不符合此限制的字符将被替换为下划线。
– 第 86 页
换句话说,一个相当显着的变化。因此,您需要重写您的应用程序,以便发送破折号而不是下划线,而 Apache 会反过来替换下划线。
编辑
似乎有办法解决这个问题。如果您在 apache.org 上查看this document,您可以看到您可以通过将您的foo_bar
的值放入一个名为foo-bar
的新变量中来修复它在.htaccess
foo_bar
来自 Apache。请参见下面的示例:
SetEnvIfNoCase ^foo.bar$ ^(.*)$ fix_accept_encoding=$1
RequestHeader set foo-bar %fix_accept_encodinge env=fix_accept_encoding
唯一的缺点是您必须为每个标头制定规则,但您不必对客户端或服务器端的代码进行任何更改。
【讨论】:
虽然我完全不赞成他们的所作所为,但您的回答说明了正在发生的事情。谢谢! 这也让我感到困惑。奇怪的是,这似乎是一个很大的变化。【参考方案2】:你确定 Django 也没有升级吗?
https://docs.djangoproject.com/en/dev/ref/request-response/
除了上面给出的 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 标头都通过将所有字符转换为大写、用下划线替换任何连字符并在名称中添加 HTTP_ 前缀来转换为 META 键。因此,例如,一个名为 X-Bender 的标头将映射到 META 键 HTTP_X_BENDER。
关键位是:Django 将“-”转换为下划线,并在其前面加上“HTTP_”。如果您在 调用 api 时已经添加了 HTTP_ 前缀,那么它可能会加倍。例如'HTTP_HTTP_SPECIES'
【讨论】:
进行名称翻译的不是 Django,而是 WSGI 服务器。 WSGI 规范要求 WSGI 服务器填充 WSGI 环境字典,该字典在 Django 中成为 META,名称按照 CGI 规范规则进行翻译。所以最终这是由于 WSGI 的各个部分所基于的 CGI 规范所决定的。 Django 也确实得到了升级,但这应该不是问题,因为使用 django 服务器运行 API 时,一切正常。我知道上面提到的事情,但是它没有提到下划线是不可接受的,也没有提到这将是新版本的 django 中的内容。格雷厄姆,你可能正在那里做些什么,这可能是 wsgi 的罪魁祸首。 您是否尝试过简单地打印出 request.META 映射及其所有键值?这会告诉你到底发生了什么。 我有 Jon,但缺少包含下划线的参数。以上是关于HTTP 参数未在 Apache 2.4 中断功能中发送的主要内容,如果未能解决你的问题,请参考以下文章
Apache 2.4 将本地虚拟主机重定向到 https 而不是 http