如何在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,}
重新加载 Apache
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 读取)。 只有在需要数据泄漏检测和保护时才需要这样做。 因此,将其保留为 On 会耗尽 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
您将看到指示成功登录的消息。 现在回来输入一对错误的凭据 - 您将看到消息 Invalid username or password。
我们可以确认脚本工作正常。 接下来的工作是尝试使用 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
在 <IfModule security2_module> </IfModule>
中添加以下指令:
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
并输入包含以下任何单词的文本:药丸、保险、劳力士。
您将看到 403 页面和日志条目,或者仅基于 SecRuleEngine
设置的日志条目。 SecRule 的语法是
SecRule VARIABLES OPERATOR [ACTIONS]
在这里,我们使用 chain 操作将变量 REQUEST_FILENAME 与 form.php、REQUEST_METHOD 与 POST 和 [ X155X]REQUEST_BODY 与正则表达式 (@rx) 字符串 (pills|insurance|rolex)。 ?i: 进行不区分大小写的匹配。 在成功匹配所有这三个规则时,ACTION 将与 deny 和 log 相匹配,并带有消息“检测到垃圾邮件”。 chain 动作模拟逻辑 AND 以匹配所有三个规则。
排除主机和目录
有时排除特定目录或域名是有意义的,如果它正在运行像 phpMyAdmin 这样的应用程序作为 modsecurity 并且会阻止 SQL 查询。 最好排除 WordPress 等 CMS 应用程序的管理后端。
要为完整的 VirtualHost 禁用 modsecurity,请放置以下内容
<IfModule security2_module> SecRuleEngine Off </IfModule>
在 <VirtualHost>
部分内。
对于特定目录:
<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