CentOS下Nginx+ModSecurity(3.0.x)安装及日志保存

CentOS下Nginx+ModSecurity(3.0.x)安装及配置WAF规则

本文主要介绍ModSecurity v3.0.x在CentOS+Nginx环境下的安装、WAF规则文件配置、以及防御效果的验证,因此对于Nginx仅进行简单化安装。

服务器操作系统:CentOS-7-x86_64-DVD-1810.iso;

一、安装相关依赖工具

yum install -y git wget epel-release
yum install -y gcc-c++ flex bison yajl yajl-devel curl-devel curl GeoIP-devel doxygen zlib-devel pcre-devel lmdb-devel libxml2-devel ssdeep-devel lua-devel libtool autoconf automake

二、安装Modsecurity

cd /usr/local
git clone https://github.com/SpiderLabs/ModSecurity
cd ModSecurity
git checkout -b v3/master origin/v3/master
git submodule init
git submodule update
sh build.sh
./configure
make && make install

三、安装nginx与ModSecurity-nginx

cd /usr/local
git clone https://github.com/SpiderLabs/ModSecurity-nginx
wget http://nginx.org/download/nginx-1.16.1.tar.gz
tar -xvzf nginx-1.16.1.tar.gz
cd /usr/local/nginx-1.16.1
./configure --add-module=/usr/local/ModSecurity-nginx
make 
make install

四、测试效果

启动nginx

/usr/local/nginx/sbin/nginx

模拟攻击,测试未启动ModSecurity时的访问效果,访问URL为:http://服务器IP/?param=%22%3E%3Cscript%3Ealert(1);%3C/script%3E

效果如下:

在这里插入图片描述

五、配置WAF规则

创建用于存在配置文件的文件夹

mkdir /usr/local/nginx/conf/modsecurity

/usr/local/Modsecurity/modsecurity.conf-recommended复制到/usr/local/nginx/conf/modsecurity,并重命名为modsecurity.conf

/usr/local/Modsecurity/unicode.mapping复制到/usr/local/nginx/conf/modsecurity

下载规则文件压缩包,解压后复制crs-setup.conf.example/usr/local/nginx/conf/modsecurity/下并重命名为crs-setup.conf

复制rules文件夹到/usr/local/nginx/conf/modsecurity/下,同时修改REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.exampleRESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example两个文件的文件名,将".example"删除,可将自己写的规则放置于此两个文件中;

编辑nginx.conf

httpserver节点中添加以下内容(在http节点添加表示全局配置,在server节点添加表示为指定网站配置.

modsecurity on;
modsecurity_rules_file /usr/local/nginx/conf/modsecurity/modsecurity.conf;

编辑modsecurity.conf

SecRuleEngine DetectionOnly改为SecRuleEngine On

SecRuleEngine DetectionOnly #它只会检测到所有的攻击,并根据攻击产生错误,但它不会在服务器上阻止任何东西

SecRuleEngine On #将在服务器上激活ModSecurity防火墙。它会检测并阻止该服务器上的任何恶意攻击。

modsecurity.conf文件配置介绍
同时添加以下内容:

Include /usr/local/nginx/conf/modsecurity/crs-setup.conf
Include /usr/local/nginx/conf/modsecurity/rules/*.conf

六、重新加载Nginx测试效果

/usr/local/nginx/sbin/nginx -s reload

在这里插入图片描述

七、其他补充

如果要确保ModSecurity v3.0.3在记录审计日志时保存请求体,SecAuditLogParts需要添加配置C,而不是IJ,否则审计日志将无法记录请求体

ModSecurity日志保存至MySQL数据库(通过Logstash)

JDK:jdk-8u241-linux-x64.tar.gz

Logstash:logstash-5.6.16.tar.gz

JDBC:mysql-connector-java-5.1.48.jar

一、软件上传

将上述JDK及Logstash的软件压缩包下载后,上传至服务器/usr/local目录下并解压,将JDBC上传至解压后的Logstash目录下。

cd /usr/local
tar -zxvf jdk-8u241-linux-x64.tar.gz
tar -zxvf logstash-5.6.16.tar.gz

二、配置JDK

如果服务器中已经安装了其他版本的JDK,且不可随意变更,可对Logstash进行单独的JDK配置,将以下内容复制到Logstash目录下bin/logstash、bin/logstash.lib.sh两个文件的首行位置即可:

export JAVA_HOME=/usr/local/jdk1.8.0_241

三、测试Logstash

首先,测试Logstash是否可以正常工作,在控制台输入以下命令等待Logstash启动:

/usr/local/logstash-5.6.16/bin/logstash -e 'input { stdin { } } output { stdout {} }'

启动成功的结果类似于下图(电脑端右击图片点击“在新标签页中打开图片”即可查看大图):
在这里插入图片描述
然后直接输入字符串,如“Test”回车,如出现类似以下信息,表明Logstash可正常工作
在这里插入图片描述
测试结束后通过Ctrl+C结束进程

四、安装logstash-output-jdbc

由于要把日志输入到数据库中,因此需要安装logstash-output-jdbc,该插件为第三方所开发,并未在Logstash 6.3及以上版本进行测试。

/usr/local/logstash-5.6.16/bin/logstash-plugin install logstash-output-jdbc
由于要从国外ubygems.org下载插件,因此在线安装过程较慢,如果发现无法在线安装,可下载logstash-output-jdbc.zip,上传至Logstash目录下。该安装包为本人基于Logstash 5.6.16在线安装logstash-output-jdbc成功后,所生成的离线安装包,其他版本的Logstash不一定适用。运行以下命令即可进行离线安装:

/usr/local/logstash-5.6.16/bin/logstash-plugin install file:///usr/local/logstash-5.6.16/logstash-output-jdbc.zip

五、创建日志同步配置文件

在Logstash目录下创建文件mslogtomysql.conf,并将以下内容复制进文件中,保存时要使用UTF8格式,或将中文注释删除,否则Logstash启动时会报错:

input {
    
    
    	file {
    
    
	    #ModSecurity审计日志的存放位置,请根据实际情况进行修改
		path => ["/var/log/modsecurity/*/*/*"]
		start_position => "beginning"
	}
}
filter{
    
    
	json{
    
    
		source => "message"
		remove_field => ["message"]
	}
    #以下到filter节点结束的内容,是为了将ModSecurity记录的日期转换为数据库可存放的datetime格式
	mutate{
    
    
		split => ["[transaction][time_stamp]"," "]    
		add_field => {
    
     "date" => "yyyy-MM-dd HH:mm:ss" }
		add_field => {
    
     "month" => "%{[transaction][time_stamp][1]}" }
		add_field => {
    
     "day" => "%{[transaction][time_stamp][2]}" }
		add_field => {
    
     "time" => "%{[transaction][time_stamp][3]}" }
		add_field => {
    
     "year" => "%{[transaction][time_stamp][4]}" } 
	}
	
	if [month] == "Jan" {
    
    
		mutate {
    
    
			gsub =>["month","Jan",'01']
		}
	} else if [month] == "Feb" {
    
    
		mutate {
    
    
			gsub =>["month","Feb",'02']
		}
	} else if [month] == "Mar"{
    
    
		mutate {
    
    
			gsub =>["month","Mar",'03']
		}
	} else if [month] == "Apr"{
    
    
		mutate {
    
    
			gsub =>["month","Apr",'04']
		}
	} else if [month] == "May"{
    
    
		mutate {
    
    
			gsub =>["month","May",'05']
		}
	} else if [month] == "Jun"{
    
    
		mutate {
    
    
			gsub =>["month","Jun",'06']
		}
	} else if [month] == "Jul"{
    
    
		mutate {
    
    
			gsub =>["month","Jul",'07']
		}
	} else if [month] == "Aug"{
    
    
		mutate {
    
    
			gsub =>["month","Aug",'08']
		}
	} else if [month] == "Sep"{
    
    
		mutate {
    
    
			gsub =>["month","Sep",'09']
		}
	} else if [month] == "Oct"{
    
    
		mutate {
    
    
			gsub =>["month","Oct",'10']
		}
	} else if [month] == "Nov"{
    
    
		mutate {
    
    
			gsub =>["month","Nov",'11']
		}
	} else if [month] == "Dec"{
    
    
		mutate {
    
    
			gsub =>["month","Dec",'12']
		}
	}
	
	mutate {
    
    
		gsub =>["date","yyyy",'%{[year]}']
		gsub =>["date","MM",'%{[month]}']
		gsub =>["date","dd",'%{[day]}']
		gsub =>["date","HH:mm:ss",'%{[time]}']
	}
}


output {
    
    
    #该节点会将最终日志数据以JSON格式打印到控制台中,便于观测进行调试,测试无问题后可将此节点删除
	stdout {
    
    
		codec => json {
    
    
			charset => "UTF-8"
		}
	}

	jdbc {
    
    
		driver_jar_path => "/usr/local/logstash-5.6.16/mysql-connector-java-5.1.48.jar"
		driver_class => "com.mysql.jdbc.Driver"
		connection_string => "jdbc:mysql://服务器IP地址:数据库端口/modsecurity?user=数据库用户名&password=数据库密码"
		statement => [ "insert into data (client_ip,time_stamp,date,server_id,client_port,host_ip,host_port,uri,unique_id,request,response,producer,messages) values (?,?,?,?,?,?,?,?,?,?,?,?,?)","[transaction][client_ip]","[transaction][time_stamp]","[date]","[transaction][server_id]","[transaction][client_port]","[transaction][host_ip]","[transaction][host_port]","[transaction][request][uri]","[transaction][unique_id","[transaction][request]","[transaction][response]","[transaction][producer]","[transaction][messages]" ]
	}
}

六、创建数据库

创建名为modsecurity的数据库,并执行以下SQL创建数据表

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `data`
-- ----------------------------
DROP TABLE IF EXISTS `data`;
CREATE TABLE `data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `client_ip` varchar(255) DEFAULT NULL,
  `time_stamp` varchar(255) DEFAULT NULL,
  `date` datetime DEFAULT NULL,
  `server_id` varchar(255) DEFAULT NULL,
  `client_port` int(11) DEFAULT NULL,
  `host_ip` varchar(255) DEFAULT NULL,
  `host_port` int(11) DEFAULT NULL,
  `uri` varchar(255) DEFAULT NULL,
  `unique_id` varchar(255) DEFAULT NULL,
  `request` mediumtext,
  `response` mediumtext,
  `producer` mediumtext,
  `messages` mediumtext,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

七、配置ModSecurity记录审计日志

modsecurity.conf文件需进行以下配置:

SecAuditEngine On
#注意,ModSecurity 3.x版本中目前不支持使用IJ记录请求体内容,如果要记录请求体,请用C代替IJ
#但此时会有一个情况,如果通过网站进行文件上传,文件的内容也会被记录到日志中,因此请勿通过网站上传大容量文件
SecAuditLogParts ABIJDEFHZ
#设置并行方式记录日志
SecAuditLogType Concurrent
#配置存放日志的目录,该目录必须存在且赋予777权限,否则可能无法正常写入日志
SecAuditLogStorageDir /var/log/modsecurity/
#配置记录的日志为JSON格式
SecAuditLogFormat JSON

配置完成后需要重新加载WEB服务

八、同步日志

运行以下命令,使Logstash以指定的配置文件启动:

/usr/local/logstash-5.6.16/bin/logstash -f /usr/local/logstash-5.6.16/mslogtomysql.conf

启动成功的结果类似于下图(电脑端右击图片点击“在新标签页中打开图片”即可查看大图):
在这里插入图片描述

启动后,网站的最新访问,会先被ModSecurity记录,然后同步到数据库中

九、最后工作(可选)

编写脚本,将Logstash以后台服务方式运行,并设置开机自启;

编写定时脚本,在凌晨1点时,将前一天的ModSecurity审计日志删除,避免日志越来越多,占用磁盘空间。

modsecurity.conf文件配置介绍

#SecRuleEngine是接受来自ModSecurity-CRS目录下的所有规则的安全规则引擎。因此,我们可以根据需求设置不同的规则。要设置不同的规则有以下几种。
#SecRuleEngine On:将在服务器上激活ModSecurity防火墙。它会检测并阻止该服务器上的任何恶意攻击。
#SecRuleEngine Detection Only:如果这个规则是在whitelist.conf文件中设置的,它只会检测到所有的攻击,并根据攻击产生错误,但它不会在服务器上阻止任何东西。
#SecRuleEngine Off::这将在服务器上上停用ModSecurity的防火墙。
SecRuleEngine DetectionOnly
#SecRequestBodyAccess是否允许ModeSecurity访问request bodies(post请求内容);备注:如果你计划检查POST_PAYLOAD 就使用这个指令,这个指令必须和"phase:2"处理阶段动作和REQUEST_BODY 变量/位置一起使用,这三部分任一一个没有配置,你就无法检查请求体。
SecRequestBodyAccess On
#SecRule是ModSecurity主要的指令,用于分析数据并根据结果执行动作,SecRule VARIABLES OPERATOR [ACTIONS];
#VARIABLES描述哪个变量被检查,举个例子,下述规则会拒绝URI中含有单词dirty的事务。每条规则可以指定一个或多个变量,如SecRule ARGS|REQUEST_HEADERS:User-Agent dirty
#OPERATOR描述如何进行检查#[ACTIONS]可选的,描述当操作进行成功的匹配一个变量时具体怎么做。
#XML内容情况下启动XML解析
SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
#json内容情况下启动XML解析
SecRule REQUEST_HEADERS:Content-Type "application/json" "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON"
#配置ModSecurity允许的最大请求体的缓存区大小,第一条是有上传文件的,默认1G,第二条是没有文件的上传的限制,默认128K;
#:配置ModSecurity 允许的最大请求体的缓存区大小,除了请求中正在传送的文件大小。这项指令便于在受到某些使用大尺寸请求进行DoS 攻击时减少影响。提供上传文件服务的WEB 应用必须配置SecRequestBodyLimit 为一个很大的值。由于大文件直接进行磁盘文件存取,不会加大内存的消耗。但是,仍然有可能有人利用超大请求体限制和发送大量大小的非上传请求。该指令消除这一漏洞。
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
#SecRequestBodyLimitAction  Reject:拒绝、Process Partial:呈现请求的第一部分    
#当请求超过SecRequestBodyLimit策略中配置的设置时该做什么。 默认情况下拒绝大于集合的请求。
SecRequestBodyLimitAction Reject
SecRule REQBODY_ERROR "!@eq 0" "id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
"id:'200003',phase:2,t:none,log,deny,status:400, \
msg:'Multipart request body failed strict validation: 
PE %{REQBODY_PROCESSOR_ERROR}, \
BQ %{MULTIPART_BOUNDARY_QUOTED}, \
BW %{MULTIPART_BOUNDARY_WHITESPACE}, 
DB %{MULTIPART_DATA_BEFORE}, \
DA %{MULTIPART_DATA_AFTER}, \
HF %{MULTIPART_HEADER_FOLDING}, \
LF %{MULTIPART_LF_LINE}, \
SM %{MULTIPART_MISSING_SEMICOLON}, \
IQ %{MULTIPART_INVALID_QUOTING}, \
IP %{MULTIPART_INVALID_PART}, \
IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
"id:'200004',phase:2,t:none,log,deny, \
msg:'Multipart parser detected a possible unmatched boundary.'"
#设置PCRE库中的匹配限制。
SecPcreMatchLimit 1000
#在PCRE库中设置匹配限制递归,提高效率。
SecPcreMatchLimitRecursion 1000SecRule TX:/^MSC_/ "!@streq 0" \
"id:'200005',phase:2,t:none,deny, \
msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
#允许ModSecurity访问response body,您应该启用此指令以识别错误和数据泄漏问题。 启用这个指令会增加内存消耗和响应延迟。
SecResponseBodyAccess On
#备注:如果你计划检查HTML 的响应,需要使用这个指令。这个指令必须和"phase:4"处理阶段动作和REQUEST_BODY 变量/位置一起使用,这三部分任一一个没有配置,你就无法检查请求体。
#设置返回体的MIME资源的媒体类型
SecResponseBodyMimeType text/plain text/html text/xml
#配置允许缓存的最大响应体大小,默认512K
SecResponseBodyLimit 524288
#配置SecResponseBodyLimit,控制如果request body 大小超过上面限制的情况,默认时ModSecurity拒绝超过指定长度的响应体,
#然而一些WEB站点,会产生一些非常长的响应为适当限制带来难度。#这类网站不得不极大的提高关注度,把限制放到了首位(控制内存消耗)。#有能力选择的是发生站点限制时,管理员能选择仅仅检查响应的第一部分,这部分可融入理想的限制,并让其通过。#可以证明未经检查就允许部分响应是个漏洞,理论上这是对的,但仅适用于攻击者控制输出的案例(如它可以任意的长)。
#不管怎样,在这种情况下,无论如何是阻止不了漏洞的。攻击者在数据回送前可以压缩,打乱或者甚至是加密,因为可以穿越任意监控设备。
SecResponseBodyLimitAction ProcessPartial|Reject#ModSecurity存放持久化数据(如ip 地址数据,session 数据等)路径,initcol、setsid和setuid需要用到这个指令
SecDataDir /tmp/
#配置拦截文件存储的目录
#SecUploadDir /opt/modsecurity/var/upload/
#配置是否保存事务处理后的拦截文件
#SecUploadKeepFiles RelevantOnly
#SecUploadFileMode 0600
#调试日志路径,1~3级别一直用于产生apache/nginx的错误日志,因为你可以在产品中一直使用0级别做为默认的日志级别,级别4-9用于调试,不建议在产品中使用这么高级别的日志,过度的日志记录会显著服务器的性能。#SecDebugLog /opt/modsecurity/var/log/debug.log
#SecDebugLogLevel 3
#配置审计日志引擎的开启与否;On - 默认情况下记录所有事务的日志;RelevantOnly - 默认只记录事务中由warning或error触发的日志,或者记录一些特意考虑过的状态码
SecAuditEngine RelevantOnly
#记录由规则标记的事务,以及触发服务器错误(由5xx或4xx确定,不包括404, #级别响应状态代码)。#备注:必须将SecAuditEngine 设置为RelevantOnly,其参数是个正则表达式。
#这个指令最主要的目的是允许你配置审计产生特殊HTTP 响应状态码的唯一事务,这个指令通常用于减少审计日志文件的总体大小。记住一点,如果使用了这个参数,那么返回状态码是200 的成功攻击事件不会记录。
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
#定义每个事务中记录到审计日志中的部分。默认:ABCFHZ.可用的审计日志部分:A - 审计日志标题(强制的)B - 请求标题C - 请求体(目前仅针对请求体存在,并且ModSecurity已经配置成拦截)D - 为中间人响应头保留,暂未实现E - 中间人响应体(目前仅对配置了拦截响应体和配置审计日志引擎记录有效)。中间人响应体和实际的响应体相同,除非ModSecurity拦截了中间人响应体,这种情况下,实际响应体会包含出错信息(可能是apache的默认错误信息,也可能是出错文档页面)。F - 最终响应头(除了日期和服务器标题以外的被apache添加的近期内容传递信息)。G - 为实际响应体保留,暂未实现。H - 审计日志索引I - 这C部分的替换,使用multipart/form-data编码时,在所有的异常情形下会记录与C相同的数据,在这种情况下,会记录假的application/x-www-form-urlencoded内容,这包含参数的相关信息,但不是这个文件的。如果你不想用文件(通常很大)来存储你的审计日志,这是很方便的。J - 保留。实现后,这部分会包含文件使用multipart/form-data编码上传的信息。K - 这部分包含一个完整的列表,按顺序匹配(每行一个),这些规则是完全合格的,从而表明继承默认的动作和操作,从2.5.0开始支持。Z - 最终分界,意味着是条目的最后(强制的)
SecAuditLogParts ABIJDEFHZ
#配置使用审计日志记录机制的类型Serial|Concurrent,Serial - 所有的审计日志条目都被存储在主审计日志记录文件中,随意使用是很方便,但是它很慢,因为任何时候只有一个文件被打开也只能写入一条审计日志条目。Concurrent - 审计日志条目被存储于不同的文件中,每个事务一个,如果你要把审计日志数据发送到远程ModSecurity控制主机上就使用Concurrent日志模式。
SecAuditLogType Serial
#审计日志路径SecAuditLog /var/log/modsec_audit.log
#指定的字符做为application/x-www-form-urlencoded内容的分隔符,默认是&,非常少的情况下应用会使用分号(;)。这个指令用于后台WEB应用在使用非标准的参数分隔符,如果没有在每一个WEB应用中合理设置这个指令,那么ModSecurity可能无法适当的分析所有的参数,并且规则匹配的效果可能会显著的降低。
SecArgumentSeparator &
#选择当前配置文本中使用的cookie格式,0 - 使用version 0 (Netscape) cookies,这是大部分应用使用的,也是默认值;1 - 使用version 1 cookies
SecCookieFormat 0
#定义将由urlDecodeUni变换函数用于在规范化期间映射Unicode代码点的文件的路径,并指定要使用的代码点。
SecUnicodeMapFile unicode.mapping 20127
#控制状态报告功能。 使用基于DNS的报告将软件版本信息发送到ModSecurity项目团队。
SecStatusEngine On
#Load OWASP Config 
Include crs-setup.conf
#Load all other Rules 
Include rules/*.conf

猜你喜欢

转载自blog.csdn.net/ilqgffvramusm2864/article/details/107663339