【问题标题】:Modsecurity & Apache: How to limit access rate by header?Modsecurity & Apache:如何通过标头限制访问速率?
【发布时间】:2018-12-04 20:00:05
【问题描述】:

我让 Apache 和 Modsecurity 一起工作。我正在尝试通过请求的标头(例如“facebookexternalhit”)来限制命中率。然后返回友好的“429 Too Many Requests”和“Retry-After: 3”。

我知道我可以读取以下标题文件:

SecRule REQUEST_HEADERS:User-Agent "@pmFromFile ratelimit-bots.txt"

但是我在构建规则时遇到了麻烦。

任何帮助将不胜感激。谢谢。

【问题讨论】:

标签: apache security mod-security


【解决方案1】:

经过 2 天的研究和了解 Modsecurity 的工作原理,我终于做到了。仅供参考,我正在使用 Apache 2.4.37 和 Modsecurity 2.9.2 这就是我所做的:

在我的自定义文件规则中:/etc/modsecurity/modsecurity_custom.conf 我添加了以下规则:

# Limit client hits by user agent
SecRule REQUEST_HEADERS:User-Agent "@pm facebookexternalhit" \
    "id:400009,phase:2,nolog,pass,setvar:global.ratelimit_facebookexternalhit=+1,expirevar:global.ratelimit_facebookexternalhit=3"
SecRule GLOBAL:RATELIMIT_FACEBOOKEXTERNALHIT "@gt 1" \
    "chain,id:4000010,phase:2,pause:300,deny,status:429,setenv:RATELIMITED,log,msg:'RATELIMITED BOT'"
    SecRule REQUEST_HEADERS:User-Agent "@pm facebookexternalhit"
Header always set Retry-After "3" env=RATELIMITED
ErrorDocument 429 "Too Many Requests"

解释:

注意:我想限制为每 3 秒 1 个请求。

  1. 第一条规则将请求标头用户代理与“facebookexternalhit”相匹配。如果匹配成功,它会在 global 集合中创建 ratelimit_facebookexternalhit 属性,其初始值为 1(它会随着每个点击匹配用户代理)。然后,它将这个 var 的过期时间设置为 3 秒。如果我们收到一个匹配“facebookexternalhit”的新命中,它的总和为 1 到 ratelimit_facebookexternalhit。如果我们在 3 秒后没有收到匹配“facebookexternalhit”的点击,ratelimit_facebookexternalhit 将消失并重新启动此过程。
  2. 如果 global.ratelimit_clients > 1(我们在 3 秒内收到 2 次或更多点击)并且用户代理匹配“facebookexternalhit”(此 AND 条件很重要,否则如果产生匹配,所有请求都将被拒绝),我们设置 RATELIMITED =1,使用 429 http 错误停止操作,并在 Apache 错误日志中记录自定义消息:“RATELIMITED BOT”。
  3. RATELIMITED=1 设置只是为了添加自定义标题“Retry-After: 3”。在这种情况下,这个 var 会被 Facebook 的爬虫 (facebookexternalhit) 解释,并会在指定的时间重试操作。
  4. 我们为 429 错误映射自定义返回消息(如果需要)。

您可以通过添加@pmf 和.data 文件来改进此规则,然后初始化initcol:global=%{MATCHED_VAR} 之类的全局集合,因此您不仅限于单个规则匹配。我没有测试这最后一步(这是我现在需要的)。如果我这样做了,我会更新我的答案。

更新

我已经调整了规则,以便能够拥有一个包含我想要限制的所有用户代理的文件,因此可以跨多个机器人/爬虫使用单个规则:

# Limit client hits by user agent
SecRule REQUEST_HEADERS:User-Agent "@pmf data/ratelimit-clients.data" \
    "id:100008,phase:2,nolog,pass,setuid:%{tx.ua_hash},setvar:user.ratelimit_client=+1,expirevar:user.ratelimit_client=3"

SecRule USER:RATELIMIT_CLIENT "@gt 1" \
    "chain,id:1000009,phase:2,deny,status:429,setenv:RATELIMITED,log,msg:'RATELIMITED BOT'"                                                                                     
    SecRule REQUEST_HEADERS:User-Agent "@pmf data/ratelimit-clients.data"

Header always set Retry-After "3" env=RATELIMITED

ErrorDocument 429 "Too Many Requests"

因此,带有用户代理的文件(每行一个)位于此规则同一目录下的子目录中:/etc/modsecurity/data/ratelimit-clients.data。然后我们使用@pmf 来读取和解析文件(https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-(v2.x)#pmfromfile)。我们使用用户代理初始化 USER 集合:setuid:%{tx.ua_hash}tx.ua_hash/usr/share/modsecurity-crs/modsecurity_crs_10_setup.conf 的全局范围内)。我们只是使用 user 作为集合而不是 global。就是这样!

【讨论】:

  • 我通过伪造用户代理使用 curl 测试了这条规则(第一个不是更新)。如果您按顺序发送请求,它会按预期在第一个请求之后以 429 阻塞。但是如果你用这样的后台操作符链接它们: curl -A "facebookexternalhit" example.com & curl -A "facebookexternalhit" example.com & .... 那么所有请求都返回 200
  • 发生这种情况是因为每个进程都有一个独立的集合,并且它们只会定期同步。因此,如果您同时发送大量请求,它们的初始 ratelimit_client 变量都将为 0,直到它被同步。如果有人知道解决方法,请分享。
  • 不错的收获。您可以尝试在他们的支持论坛中提问 sourceforge.net/projects/mod-security/lists/mod-security-users 我也订阅了,所以如果您得到答案,阅读会很有趣。
猜你喜欢
  • 1970-01-01
  • 2022-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-03
  • 2021-04-15
  • 2021-12-09
相关资源
最近更新 更多