65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ModSecurity 阻止 IP。

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2013 年 4 月 9 日

CPOL

5分钟阅读

viewsIcon

52744

使用 mod_security 阻止任意数量的 IP 地址。我们可以动态地进行,而无需每次都重新启动 Apache Web 服务器。

我们可以使用 Apache 模块 mod_security 动态地将任何 IP 添加到黑名单(或白名单)。在互联网上搜索后,我们发现没有现成的解决方案,这真的很令人惊讶,但阻止任何 IP 的此功能非常重要,因为与所有其他网站一样,我们每天也会收到大量来自少数 IP 的不希望的 HTTP 请求。因此,我们继续实施了一个阻止此类 IP 的解决方案,我们很乐意与您分享该解决方案。

通常在 Linux 中,iptables 用于阻止任何不需要的 IP 或 IP 范围。下面给出了一些使用 iptables 阻止 IP 的示例用法。

# Blocks the specified IP
iptables -A INPUT -s 0.0.0.1 -j DROP

# Blocks the specified IP range
iptables -A INPUT -m iprange --src-range 0.0.0.0-0.0.0.255 -j DROP

# Same as above, but using CIDR notaion
iptables -A INPUT -s 0.0.0.0/24 -j DROP

如果机器直接连接到互联网,没有代理或负载均衡器在前面,那么 iptables 是最方便的。但在 hudku.com,我们前面有 Elastic Load Balancer,因为我们的网站运行在 AWS Elastic Beanstalk 应用程序上,在 Linux 上运行 Apache。所以我们得到的 IP 始终是负载均衡器的 IP,只有在 HTTP 标头“X-Forwarded-For”中,我们才能获得请求客户端的 IP。iptables 只能处理 IP,我们无法使其使用 HTTP 标头中的值。

因此,我们被迫寻找替代解决方案,并决定使用 mod_security 来实现此功能。该逻辑是通过利用 mod_security 提供的“Collections”来实现的。Collections 不过就是一个 HashMap。IP 地址是键,值是整数。如果值为 1,则我们将其解释为白名单 IP,如果值为 2,则我们认为该 IP 是黑名单 IP。如果键不存在,则 IP 既不在白名单也不在黑名单中,因此被正常处理。

如果我们在白名单中找到一个 IP,就不会再进行任何检查,请求也会被接受。

如果在黑名单中找到一个 IP,我们会丢弃连接并将其记录在 modsecurity 日志文件中。

要将 IP 添加到黑名单,我们只需访问应用程序的特定 URL,并将 IP 作为参数传递。这就像 www.example.com/ip/blacklist?ip=0.0.0.1 一样简单。同样,要将 IP 添加到白名单,则是 www.example.com/ip/whitelist?ip=0.0.0.2。要从白名单和黑名单中删除 IP,命令是 www.example.com/ip/remove?ip=0.0.0.3,它会从 Collection (Map) 中删除 IP(如果存在)。

然后,最重要的是确保这些命令只有在从当前机器,即 localhost 发出时才会被接受。

安装 mod_security 后,它会创建一个名为“modsecurity.d”的目录,并加载该目录中所有带有“.conf”扩展名的文件。

掌握了以上所有信息后,我们现在可以查看位于 modsecurity.d 目录下的 my_rules.conf 文件中的源代码了。

#
# Adding / Removing an ip to Dynamic Whitelist & Blacklist - allowed only for localhost
#

# Remove from Dynamic Whitelist & Blacklist - remove allowed variable
SecRule REQUEST_FILENAME "^/ip/remove$" 
"chain,phase:1,t:none,deny,nolog,status:200"
    SecRule REMOTE_ADDR "^127.0.0.1$" "chain,t:none"
    SecRule ARGS:ip "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b" 
    "t:none,initcol:ip=%{args.ip},setvar:!ip.allowed"
#
# Add to Dynamic Whitelist - allowed value is 1
SecRule REQUEST_FILENAME "^/ip/whitelist$" 
"chain,phase:1,t:none,deny,nolog,status:200"
    SecRule REMOTE_ADDR "^127.0.0.1$" "chain,t:none"
    SecRule ARGS:ip "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b" 
    "t:none,initcol:ip=%{args.ip},setvar:ip.allowed=1"
#
# Add to Dynamic Blacklist - allowed value is 2
SecRule REQUEST_FILENAME "^/ip/blacklist$" 
"chain,phase:1,t:none,deny,nolog,status:200"
    SecRule REMOTE_ADDR "^127.0.0.1$" "chain,t:none"
    SecRule ARGS:ip "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b" 
    "t:none,initcol:ip=%{args.ip},setvar:ip.allowed=2"



# Allow any request from localhost
SecRule REMOTE_ADDR "^127.0.0.1$" 
"phase:1,t:none,allow,nolog,ctl:ruleEngine=off"



#
# Initialize IP Collection using the IP address obtained from x-forwarded-for or remote_addr
#
SecRule REQUEST_HEADERS:x-forwarded-for "^\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b" 
"phase:1,t:none,pass,nolog,capture,setvar:tx.client_ip=%{tx.1}"
SecRule &TX:CLIENT_IP "@eq 0" 
"phase:1,t:none,pass,nolog,setvar:tx.client_ip=%{remote_addr}"
SecRule &TX:CLIENT_IP "!@eq 0" 
"phase:1,t:none,pass,nolog,initcol:ip=%{tx.client_ip}"



#
# Process Dynamic Whitelist & Blacklist
#
# Allow if IP is present in Dynamic Whitelist
SecRule IP:ALLOWED "@eq 1" "phase:1,t:none,allow,nolog,ctl:ruleEngine=off"
#
# Drop if IP is present in Dynamic Blacklist
SecRule IP:ALLOWED "@eq 2" "phase:1,t:none,drop,log,logdata:'Dynamic Blacklist'"

第一段代码处理“remove”请求,用于从白名单和黑名单中删除 IP。第二行是进行重要检查的地方,以确保所有这些请求只有在从 localhost (127.0.0.1) 发出时才会被接受。然后,在第三行,IP 从集合中删除。集合的名称是“ip”,我们使用的变量或计数器的名称是“allowed”。

同样,下一个块处理包含在白名单中的请求,而下一个块处理黑名单。所有这些块都首先初始化集合“ip”,然后设置变量“allowed”的值。

然后,我们做一个简单的检查,允许所有来自 localhost 的请求,而不应用任何进一步的规则。

下一节实际上初始化了 modsecurity 集合。我们首先从 HTTP 标头中提取 IP
X-Forwarded-For 并将其存储在 TX:CLIENT_IP 中。此集合“TX”属于 modsecurity,并且通常可用。

如果 X-Forwarded-For 不包含任何 IP,那么我们尝试使用 remote_addr 变量中的可用值。

下一行使用 mod_security 操作符“initcol”并提供当前 IP 地址来初始化我们名为“ip”的集合。

在最后一个块中,第一行检查变量“allowed”的值。如果为 1,则表示这是一个白名单 IP,因此允许其继续进行 HTTP 请求,而无需进一步检查,方法是关闭规则引擎。

最后一行检查它是否是黑名单 IP。如果是,则丢弃连接并记录信息。

以下是将 IP 添加到黑名单、白名单或从两者中删除的示例用法。我们使用 Linux curl 命令来访问 URL,我们只对 HTTP 响应代码感兴趣,可以使用“http_code”选项获取。

# Blacklist few IPs
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/blacklist?ip=0.0.0.1
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/blacklist?ip=0.0.0.2
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/blacklist?ip=0.0.0.3

# Whitelist few IPs
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/whitelist?ip=0.0.1.1
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/whitelist?ip=0.0.1.2
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/whitelist?ip=0.0.1.3

# Remove IP from both the lists if present
curl -s -o /dev/null -I -w "%{http_code}" localhost/ip/remove?ip=127.0.0.1

对白名单或黑名单所做的更改即使在重启后仍然有效,因为我们使用的是 mod_security 的持久化集合。它将此数据存储在指令 SecDataDir 中指定的目录中。但是,如果我们想将此列表维护在数据库中,那么我们可以在执行 curl 命令之前或之后使用一些 bash 脚本来完成此工作。

就这样,大家。我们现在可以使用 mod_security 成功阻止任意数量的 IP 地址。我们可以动态地进行,而无需每次都重新启动 Apache Web 服务器。

如果您发现此帖子有用,请使用社交按钮“分享”。

© . All rights reserved.