在 .htaccess 文件中,正则表达式无法匹配空格字符。

3
我希望使用mod_rewrite阻止任何包含非数字字符或空ID的请求。我在我的.htaccess文件中使用了以下规则:
RewriteCond %{QUERY_STRING} ID=(\d*[^\d&]+\d*)*(&|$)
RewriteRule .* - [F]

这段代码是有效的,除了包含空格字符的请求,例如。

GET /page.php?ID=5 5 HTTP/1.1

当我使用各种测试套件(如https://regex101.com/)时,[^\d&]+成功匹配了两个5之间的空格字符,但是这种类型的请求仍然可以通过。

我需要改变什么?

(是的,由于我的PHP处理不正确的用户输入,所以这没有关系。)


在编程中,空格使用 %20 在URL中进行编码,你为什么担心这个? - Andreykul
我相信你可以使用 ID=[0-9]*[^0-9&] 正则表达式。如果不使用 %1 / %2,则无需捕获任何内容。 - Wiktor Stribiżew
@Andreykul 空格在常规浏览器请求中是被编码的,但这些请求是用于探测漏洞的。 - Orinoco
@Wiktor Stribiżew 我也想要阻止一个空的ID。 - Orinoco
2个回答

3

也许这对你有用:

RewriteCond %{QUERY_STRING} !(?:^|&)ID=\d+(?:&|$)
RewriteRule ^ - [F]

如果您只想让它影响查询字符串中有ID参数的请求(因此允许没有ID的请求):

RewriteCond %{QUERY_STRING} (?:^|&)(?:\%(?:20|09))*ID(?:\%(?:20|09))*= [NC]
RewriteCond %{QUERY_STRING} !(?:^|&)ID=\d+(?:&|$)
RewriteRule ^ - [F]

我还添加了[NC](不区分大小写),这样iD等也将被包括在内。


1
在_CondPattern_参数名称周围使用\s*的原因是什么?我不一定会在_negated_条件上包含NC标志。在PHP中,URL参数名称区分大小写,因此最好阻止iD等。 (?) - MrWhite
\s* 的作用是确保捕获所有尝试指定 ID 的操作,这是 OP 最关心的问题。值得这样做吗?关于否定条件中的 NC 的好观点,已经将其删除。 - user2493235
2
但是 \s* 能匹配到任何东西吗?QUERY_STRING 服务器变量是 URL 编码的,因此在有效请求中它不应该包含字面上的空格字符 - 任何格式错误的请求都应该被服务器阻止。对我来说,\s* 看起来多余了,但也许我错过了什么?只是好奇。 (+1) - MrWhite
1
我一直在研究URL是否在匹配之前被解码,但是刚刚测试发现并不是这样。我同意你的观点,并已将其删除。 - user2493235
@user82217,我使用URL编码的空格或水平制表符替换了先前的\s匹配,但实际上需要测试以查看Apache是否允许该匹配通过,并且PHP是否忽略空格。 - user2493235
显示剩余2条评论

2

@Andreykul 对于来自常规浏览器的请求,空格会被编码,但这些请求是为了探测漏洞而发起的。

可能是针对 Web 服务器本身的漏洞,而不是针对您的 Web 应用程序的漏洞... (?)

GET /page.php?ID=5 5 HTTP/1.1
这个问题在于这是一个无效/格式不正确的请求。为了使其有效,它必须进行URL编码。第一行请求中的(文字)空格是特殊字符,充当标头中“方法”、“请求URI”和“HTTP版本”部分之间的分隔符。
由于该请求无效,合理的期望是服务器级别已经使用400 Bad Request阻止了它。
如果服务器没有阻止该请求,则可能会遇到意外行为。这可能就是你在这里看到的情况...
对于这样的请求,如果你检查QUERY_STRING服务器变量,你会发现它不包含空格或第二个5。在文字空格之前,值被截断,它只包含ID=5。(因此,这也是PHP所看到的。)因此,你的正则表达式(CondPattern)永远不会匹配。
然而,完整的请求URI存在于请求的第一行中(如你上面发布的),可以在THE_REQUEST Apache服务器变量中获得。最好直接阻止包含文字空格的任何请求(这本来就是无效的),而不是专门搜索包含ID参数的请求。例如:
RewriteCond %{THE_REQUEST} \s.*\s.*\s
RewriteRule ^ - [R=400]

这个检查是否包含在外部空格分隔符之间的任何空白字符。
参考:
https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html

1
这是一个很棒的答案,谢谢你,很好地看到了问题的根源。 - Orinoco
不错的解决方案 :) - user2493235

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接