如何在 Debian/Ubuntu 上使用 Apache 设置 mod_security
序幕
Mod security 是一款免费的 Web 应用程序防火墙 (WAF),可与 Apache、Nginx 和 IIS 配合使用。它支持灵活的规则引擎来执行简单和复杂的操作,并附带一个核心规则集 (CRS),其中包含 SQL 注入、跨站点脚本、特洛伊木马、不良用户代理、会话劫持和许多其他攻击的规则。对于 Apache,它是一个附加模块,可以轻松安装和配置。
为了完成本教程,您需要在服务器上安装 LAMP。
安装 mod_security
Modsecurity 在 Debian/Ubuntu 存储库中可用:
apt-get install libapache2-modsecurity
验证是否加载了 mod_security 模块。
apachectl -M | grep --color security
您应该会看到一个名为 security2_module (shared)
的模块,这表明该模块已加载。
Modsecurity 的安装包括一个必须重命名的推荐配置文件:
mv /etc/modsecurity/modsecurity.conf{-recommended,}
重新加载阿帕奇
service apache2 reload
您会在 Apache 日志目录中找到一个新的 mod_security 日志文件:
root@droplet:~# ls -l /var/log/apache2/modsec_audit.log
-rw-r----- 1 root root 0 Oct 19 08:08 /var/log/apache2/modsec_audit.log
配置 mod_security
开箱即用,modsecurity 不做任何事情,因为它需要规则才能工作。默认配置文件设置为 DetectionOnly,它根据规则匹配记录请求并且不阻止任何内容。这可以通过编辑 modsecurity.conf
文件来更改:
nano /etc/modsecurity/modsecurity.conf
找到这一行
SecRuleEngine DetectionOnly
并将其更改为:
SecRuleEngine On
如果您在生产服务器上尝试此操作,请仅在测试所有规则后更改此指令。
另一个要修改的指令是 SecResponseBodyAccess
。这配置是否缓冲响应主体(即由 modsecurity 读取)。只有在需要数据泄漏检测和保护时才需要这样做。因此,让它开启会耗尽 droplet 资源并增加日志文件的大小。
找到这个
SecResponseBodyAccess On
并将其更改为:
SecResponseBodyAccess Off
现在我们将限制可以发布到您的 Web 应用程序的最大数据量。两个指令配置这些:
SecRequestBodyLimit
SecRequestBodyNoFilesLimit
SecRequestBodyLimit
指令指定最大 POST 数据大小。如果客户端发送任何更大的内容,服务器将响应 413 Request Entity Too Large 错误。如果你的 web 应用程序没有任何文件上传,这个值可以大大减少。
配置文件中提到的值是
SecRequestBodyLimit 13107200
这是12.5MB。
与此类似的是 SecRequestBodyNoFilesLimit
指令。唯一的区别是这个指令限制了 POST 数据减去文件上传的大小——这个值应该“尽可能低”。
配置文件中的值为
SecRequestBodyNoFilesLimit 131072
这是128KB。
沿着这些指令的路线是另一个影响服务器性能的指令:SecRequestBodyInMemoryLimit
。该指令几乎是不言自明的;它指定了多少“请求主体”数据(POSTed 数据)应该保存在内存(RAM)中,更多的东西将被放置在硬盘中(就像交换一样)。由于液滴使用 SSD,所以这不是太多一个问题;但是,如果您有备用 RAM,则可以设置一个不错的值。
SecRequestBodyInMemoryLimit 131072
这是配置文件中指定的值 (128KB)。
测试 SQL 注入
在继续配置规则之前,我们将创建一个易受 SQL 注入攻击的 PHP 脚本并进行试用。请注意,这只是一个没有会话处理的基本 PHP 登录脚本。请务必更改以下脚本中的 MySQL 密码,以便它连接到数据库:
<代码>/var/www/login.php
<html>
<body>
<?php
if(isset($_POST['login']))
{
$username = $_POST['username'];
$password = $_POST['password'];
$con = mysqli_connect('localhost','root','password','sample');
$result = mysqli_query($con, "SELECT * FROM `users` WHERE username='$username' AND password='$password'");
if(mysqli_num_rows($result) == 0)
echo 'Invalid username or password';
else
echo '<h1>Logged in</h1><p>A Secret for you....</p>';
}
else
{
?>
<form action="" method="post">
Username: <input type="text" name="username"/><br />
Password: <input type="password" name="password"/><br />
<input type="submit" name="login" value="Login"/>
</form>
<?php
}
?>
</body>
</html>
该脚本将显示一个登录表单。输入正确的凭据将显示一条消息“A Secret for you”。
我们需要数据库中的凭据。创建一个 MySQL 数据库和一个表,然后插入用户名和密码。
mysql -u root -p
这将带您进入 mysql>
提示符
create database sample;
connect sample;
create table users(username VARCHAR(100),password VARCHAR(100));
insert into users values('jesin','pwd');
insert into users values('alice','secret');
quit;
打开浏览器,导航至 http://yourwebsite.com/login.php
并输入正确的凭据对。
Username: jesin
Password: pwd
您会看到一条消息,表明登录成功。现在回来输入一对错误的凭据——您会看到消息无效的用户名或密码。
我们可以确认脚本工作正常。接下来的工作是尝试使用 SQL 注入来绕过登录页面。在用户名字段中输入以下内容:
' or true --
请注意,在 --
之后应该有一个空格,如果没有该空格,此注入将无法工作。将密码字段留空,然后点击登录按钮。
瞧!该脚本显示了针对经过身份验证的用户的消息。
设置规则
为了让您的生活更轻松,有很多规则已经与 mod_security 一起安装。这些称为 CRS(核心规则集),位于
root@droplet:~# ls -l /usr/share/modsecurity-crs/
total 40
drwxr-xr-x 2 root root 4096 Oct 20 09:45 activated_rules
drwxr-xr-x 2 root root 4096 Oct 20 09:45 base_rules
drwxr-xr-x 2 root root 4096 Oct 20 09:45 experimental_rules
drwxr-xr-x 2 root root 4096 Oct 20 09:45 lua
-rw-r--r-- 1 root root 13544 Jul 2 2012 modsecurity_crs_10_setup.conf
drwxr-xr-x 2 root root 4096 Oct 20 09:45 optional_rules
drwxr-xr-x 3 root root 4096 Oct 20 09:45 util
该文档位于
root@droplet1:~# ls -l /usr/share/doc/modsecurity-crs/
total 40
-rw-r--r-- 1 root root 469 Jul 2 2012 changelog.Debian.gz
-rw-r--r-- 1 root root 12387 Jun 18 2012 changelog.gz
-rw-r--r-- 1 root root 1297 Jul 2 2012 copyright
drwxr-xr-x 3 root root 4096 Oct 20 09:45 examples
-rw-r--r-- 1 root root 1138 Mar 16 2012 README.Debian
-rw-r--r-- 1 root root 6495 Mar 16 2012 README.gz
要加载这些规则,我们需要告诉 Apache 查看这些目录。编辑 modsecurity.conf
文件。
nano /etc/apache2/mods-enabled/modsecurity.conf
在
中添加以下指令:
Include "/usr/share/modsecurity-crs/*.conf"
Include "/usr/share/modsecurity-crs/activated_rules/*.conf"
activated_rules
目录类似于 Apache 的 mods-enabled
目录。规则在目录中可用:
/usr/share/modsecurity-crs/base_rules
/usr/share/modsecurity-crs/optional_rules
/usr/share/modsecurity-crs/experimental_rules
必须在 activated_rules
目录中创建符号链接才能激活它们。让我们激活 SQL 注入规则。
cd /usr/share/modsecurity-crs/activated_rules/
ln -s /usr/share/modsecurity-crs/base_rules/modsecurity_crs_41_sql_injection_attacks.conf .
必须重新加载 Apache 才能使规则生效。
service apache2 reload
现在打开我们之前创建的登录页面并尝试在用户名字段上使用 SQL 注入查询。如果您已将 SecRuleEngine
指令更改为 On,您将看到 403 Forbidden 错误。如果留给 DetectionOnly 选项,注入将成功,但尝试将记录在 modsec_audit.log
文件中。
编写自己的 mod_security 规则
在本节中,我们将创建一个规则链,如果在 HTML 表单中输入某些“垃圾”单词,它会阻止请求。首先,我们将创建一个 PHP 脚本,它从文本框中获取输入并将其显示回用户。
<代码>/var/www/form.php
<html>
<body>
<?php
if(isset($_POST['data']))
echo $_POST['data'];
else
{
?>
<form method="post" action="">
Enter something here:<textarea name="data"></textarea>
<input type="submit"/>
</form>
<?php
}
?>
</body>
</html>
可以将自定义规则添加到任何配置文件或放置在 modsecurity 目录中。我们会将我们的规则放在一个单独的新文件中。
nano /etc/modsecurity/modsecurity_custom_rules.conf
将以下内容添加到此文件中:
SecRule REQUEST_FILENAME "form.php" "id:'400001',chain,deny,log,msg:'Spam detected'"
SecRule REQUEST_METHOD "POST" chain
SecRule REQUEST_BODY "@rx (?i:(pills|insurance|rolex))"
保存文件并重新加载 Apache。在浏览器中打开 http://yourwebsite.com/form.php
并输入包含以下任何词的文本:pills、insurance、rolex。
您将看到一个 403 页面和一个日志条目,或者仅看到一个基于 SecRuleEngine
设置的日志条目。 SecRule 的语法是
SecRule VARIABLES OPERATOR [ACTIONS]
在这里,我们使用链式操作将变量 REQUEST_FILENAME 与 form.php、REQUEST_METHOD 与 POST 和 REQUEST_BODY 与正则表达式 (@rx) string (pills|insurance|rolex) 匹配. ?i: 不区分大小写匹配。成功匹配所有这三个规则后,ACTION 将拒绝并记录消息“检测到垃圾邮件”。 chain 操作模拟逻辑 AND 以匹配所有三个规则。
排除主机和目录
有时排除特定目录或域名是有意义的,如果它正在运行像 phpMyAdmin 这样的应用程序作为 modsecurity 并将阻止 SQL 查询。最好排除 WordPress 等 CMS 应用程序的管理后端。
要为完整的 VirtualHost 禁用 modsecurity,请放置以下内容
<IfModule security2_module>
SecRuleEngine Off
</IfModule>
在
部分内。
对于特定目录:
<Directory "/var/www/wp-admin">
<IfModule security2_module>
SecRuleEngine Off
</IfModule>
</Directory>
如果您不想完全禁用 modsecurity,请使用 SecRuleRemoveById
指令通过指定其 ID 来删除特定规则或规则链。
<LocationMatch "/wp-admin/update.php">
<IfModule security2_module>
SecRuleRemoveById 981173
</IfModule>
</LocationMatch>
延伸阅读
官方 modsecurity 文档 https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual