玩命狂奔的间隙,莫忘记抬头看看前路的星光

0%

安装和使用开源Web应用防火墙

通过比较多种开源库和付费WAF服务,最终还是选择了:ModSecurity

理由

  1. 开源时间够长,且持续活跃

  2. 其他开源库使用人数更少,且活跃度低

  3. 付费WAF产品都比较贵,效果不一定比这个好。

官方网站和相关资料链接

  1. 官方网站 https://www.modsecurity.org
  2. Modsecurity仓库 https://github.com/SpiderLabs/ModSecurity
  3. ModSecurity-nginx仓库 https://github.com/SpiderLabs/ModSecurity-nginx
  4. 规则仓库 https://github.com/coreruleset/coreruleset
  5. Nginx官方下载地址 http://nginx.org/en/download.html

准备环境

  1. 安装必要库

    1
    sudo apt-get install g++ flex bison curl doxygen libyajl-dev libgeoip-dev libtool dh-autoreconf libcurl4-gnutls-dev libxml2 libpcre++-dev libxml2-dev
  2. 下载库

    • 下载ModSecurity库
    1
    git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity
    • 下载ModSecurity-ngin库
    1
    git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git
  3. 下载和已经安装好的nginx匹配的nginx源文件
    地址:http://nginx.org/en/download.html
    使用命令nginx -V 可以查看ngix的版本,务必要和已经安装的nginx保持一致,不然会出问题的。

    通过实践,nginx 1.10.3这样比较低的版本无法使用ModSercurity V3.X, 因此如果要nginx版本太低,后面是没有办法编译成功的。这是趟坑的结论。

    1
    2
    3
    4
    5
    6
    7
    8
    $ nginx -V
    nginx version: nginx/1.10.3 (Ubuntu)
    built with OpenSSL 1.0.2g 1 Mar 2016
    TLS SNI support enabled
    configure arguments: --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_v2_module --with-http_sub_module --with-http_xslt_module --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-threads


    $ wget nginx-1.10.3 #下载对应的nginx

    注意保存好nginx的配置configure arguments

##安装ModSecurity需要的可选库(最好还是装上)

安装perl (可能是不必要的)

  1. 命名:

    1
    2

    sudo apt-get install libperl-dev

LUA(安装5.3版本)

  1. 一定要安装liblua5.3-dev,不然检查配置的时候是无法检查到lua的。

    1
    2

    apt-get install liblua5.3-0 liblua5.3-dev libreadline-dev libreadline6-dev libtinfo-dev libtool-bin

ssdeep 模糊哈希算法工具

ModSecurity利用其来计算是否有入侵,需要搭配指纹库使用。

  1. 下载最新版本(2020-11-23)

    1
    wget https://github.com/ssdeep-project/ssdeep/releases/download/release-2.14.1/ssdeep-2.14.1.tar.gz
  2. 解压

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    mv release-2.14.1.tar.gz  ssdeep-release-2.14.1.tar.gz   #这里主要是为了和其他下载的包区分,所以改个名字
    tar -xzf ssdeep-release-2.14.1.tar.gz
    cd ssdeep-release-2.14.1/
    ./configure #在这里会显示缺少的库,找到必须安装的,安装。
    ````

    发现需要的文件实在太多了。。。先放弃了。如果实在要用到,再一一安装吧。。。。

    ## 编译ModSecurity核心库

    安装文档:
    https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-%28v2.x%29#installation-for-nginx

    1. 从github仓库克隆仓库到服务器

    1. 预备

    ````shell
    cd ModSecurity
    git submodule init
    git submodule update
    ./build.sh
    ./configure #如有可选依赖,可以先安装需要的库,再进行下一步。
    make
    make install #如果失败,使用sudo make install

编译nginx和ModSecurity-nginx模块

  1. 编译nginx模块

    切换到nginx源码文件夹
    如果nginx版本小于1.11.5,可能无法使用动态模块,或无法使用参数–with-compat

    1
    ./configure --add-module=/path/to/ModSecurity-nginx
  2. 编译nginx动态模块

    1
    ./configure --add-dynamic-module=/path/to/ModSecurity-nginx --with-compat

注意:这里的参数要和前面已经保存下来的nginx参数合并,不要单独只执行这一条。

  1. 安装的时候,可能会缺少库,报以下错误:

    1
    ./configure: error: the HTTP XSLT module requires the libxml2/libxslt

    其他可能缺少的安装库(如果曾经安装过,就不会缺)

    1
    2
    sudo apt-get install libxml2 libxml2-dev libxslt-dev
    sudo apt-get install libgd-dev
  2. 当继续./configure而没有提示缺少库后,就可以开始编译nginx了。

    1
    make
  3. 安装nginx
    这里分3种情况:

  • 已经安装过一个低版本

  • 没有安装过nginx,执行命令进行安装。

    1
    sudo make install
  • 已经安装过符合要求的nginx
    在这个情况下,不需要执行 “sudo make install”
  1. 尝试运行,可能会遇到一些问题。

    1
    sudo nginx
  • 缺少文件夹,缺少PID,例如:

    1
    "/usr/share/nginx/logs/nginx.pid" failed (2: No such file or directory)

    就去创建这个文件,或者去/etc/nginx.conf配置中去指定PID文件路径

  • 没有设置正确的路径(在./configure里设置过日志路径,但是文件夹不存在或被删除了)

    1
    "/var/lib/nginx/body" failed (2: No such file or directory)

    同样,可以在/etc/nginx/nginx.conf配置这些路径,也可以根据错误提示去创建文件夹。

    1
    sudo mkdir -p /var/lib/nginx/body

配置nginx自启动

  1. 在/etc/init.d 新建nginx文件,加入下面的脚本。
    如果PATH路径不对,则需要自己修改和添加哦,一般都是不用改的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    #!/bin/sh

    ### BEGIN INIT INFO
    # Provides: nginx
    # Required-Start: $local_fs $remote_fs $network $syslog $named
    # Required-Stop: $local_fs $remote_fs $network $syslog $named
    # Default-Start: 2 3 4 5
    # Default-Stop: 0 1 6
    # Short-Description: starts the nginx web server
    # Description: starts nginx using start-stop-daemon
    ### END INIT INFO

    PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
    DAEMON=/usr/sbin/nginx
    NAME=nginx
    DESC=nginx

    # Include nginx defaults if available
    if [ -r /etc/default/nginx ]; then
    . /etc/default/nginx
    fi

    STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"

    test -x $DAEMON || exit 0

    . /lib/init/vars.sh
    . /lib/lsb/init-functions

    # Try to extract nginx pidfile
    PID=$(cat /etc/nginx/nginx.conf | grep -Ev '^\s*#' | awk 'BEGIN { RS="[;{}]" } { if ($1 == "pid") print $2 }' | head -n1)
    if [ -z "$PID" ]; then
    PID=/run/nginx.pid
    fi

    if [ -n "$ULIMIT" ]; then
    # Set ulimit if it is set in /etc/default/nginx
    ulimit $ULIMIT
    fi

    start_nginx() {
    # Start the daemon/service
    #
    # Returns:
    # 0 if daemon has been started
    # 1 if daemon was already running
    # 2 if daemon could not be started
    start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
    || return 1
    start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
    $DAEMON_OPTS 2>/dev/null \
    || return 2
    }

    test_config() {
    # Test the nginx configuration
    $DAEMON -t $DAEMON_OPTS >/dev/null 2>&1
    }

    stop_nginx() {
    # Stops the daemon/service
    #
    # Return
    # 0 if daemon has been stopped
    # 1 if daemon was already stopped
    # 2 if daemon could not be stopped
    # other if a failure occurred
    start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PID --name $NAME
    RETVAL="$?"
    sleep 1
    return "$RETVAL"
    }

    reload_nginx() {
    # Function that sends a SIGHUP to the daemon/service
    start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --name $NAME
    return 0
    }

    rotate_logs() {
    # Rotate log files
    start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME
    return 0
    }

    upgrade_nginx() {
    # Online upgrade nginx executable
    # http://nginx.org/en/docs/control.html
    #
    # Return
    # 0 if nginx has been successfully upgraded
    # 1 if nginx is not running
    # 2 if the pid files were not created on time
    # 3 if the old master could not be killed
    if start-stop-daemon --stop --signal USR2 --quiet --pidfile $PID --name $NAME; then
    # Wait for both old and new master to write their pid file
    while [ ! -s "${PID}.oldbin" ] || [ ! -s "${PID}" ]; do
    cnt=`expr $cnt + 1`
    if [ $cnt -gt 10 ]; then
    return 2
    fi
    sleep 1
    done
    # Everything is ready, gracefully stop the old master
    if start-stop-daemon --stop --signal QUIT --quiet --pidfile "${PID}.oldbin" --name $NAME; then
    return 0
    else
    return 3
    fi
    else
    return 1
    fi
    }

    case "$1" in
    start)
    log_daemon_msg "Starting $DESC" "$NAME"
    start_nginx
    case "$?" in
    0|1) log_end_msg 0 ;;
    2) log_end_msg 1 ;;
    esac
    ;;
    stop)
    log_daemon_msg "Stopping $DESC" "$NAME"
    stop_nginx
    case "$?" in
    0|1) log_end_msg 0 ;;
    2) log_end_msg 1 ;;
    esac
    ;;
    restart)
    log_daemon_msg "Restarting $DESC" "$NAME"

    # Check configuration before stopping nginx
    if ! test_config; then
    log_end_msg 1 # Configuration error
    exit $?
    fi

    stop_nginx
    case "$?" in
    0|1)
    start_nginx
    case "$?" in
    0) log_end_msg 0 ;;
    1) log_end_msg 1 ;; # Old process is still running
    *) log_end_msg 1 ;; # Failed to start
    esac
    ;;
    *)
    # Failed to stop
    log_end_msg 1
    ;;
    esac
    ;;
    reload|force-reload)
    log_daemon_msg "Reloading $DESC configuration" "$NAME"

    # Check configuration before stopping nginx
    #
    # This is not entirely correct since the on-disk nginx binary
    # may differ from the in-memory one, but that's not common.
    # We prefer to check the configuration and return an error
    # to the administrator.
    if ! test_config; then
    log_end_msg 1 # Configuration error
    exit $?
    fi

    reload_nginx
    log_end_msg $?
    ;;
    configtest|testconfig)
    log_daemon_msg "Testing $DESC configuration"
    test_config
    log_end_msg $?
    ;;
    status)
    status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
    ;;
    upgrade)
    log_daemon_msg "Upgrading binary" "$NAME"
    upgrade_nginx
    log_end_msg $?
    ;;
    rotate)
    log_daemon_msg "Re-opening $DESC log files" "$NAME"
    rotate_logs
    log_end_msg $?
    ;;
    *)
    echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}" >&2
    exit 3
    ;;
    esac
  2. 启动服务

    • 手动启动服务
    1
    systemctl start nginx
    • 将服务加入自启动
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
      sudo systemctl enable nginx
    ····


    ### 动态加载Modsecurity-nginx.so模块

    如果是之前编译nginx的时候,./configure的参数,选择的是动态模块的方式加载Modescurity-nginx,则需要在nginx.conf文件增加该模块。
    如果不是,则忽略。
    编辑/etc/nginx/nginx.conf文件,加载到最外层:

    ````shell
    worker_processes 1 #这句本来就有的 可能数字不一样,根据CPU数量来配置。
    load_module "modules/ngx_http_modsecurity_module.so";

在nginx.conf配置Modsecurity

参考文档:https://github.com/SpiderLabs/ModSecurity-nginx,
编辑context:server,或各vhost的配置文件

  1. 创建modescurity文件夹,用于放置防火墙规则等相关配置文件,方便加载

    1
    sudo mkdir /etc/nginx/modsecurity
  2. 打开Modsecurity源码根目录,拷贝modsecurity.conf配置文件到上一步创建的文件夹。

    1
    2
    cd ~/download/ModSecurity/
    sudo cp modsecurity.conf-recommended /etc/nginx/modsecurity/modsecurity.conf

    修改nginx配置文件,引入modsecurity

    1
    2
    3
    4
    5
    6
    7
    #省略其他配置,这里只记录和modsecurity相关的设置
    server {
    modsecurity on;
    location / {
    modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
    }
    }

    配置防火墙规则

coreruleset 规则

官方网站 https://coreruleset.org/installation/
规则仓库 https://github.com/coreruleset/coreruleset
安装引导 https://www.netnea.com/cms/nginx-tutorial-7_including-owasp-modsecurity-core-rule-set/
规则仓库包括收费的话,会有多种,但是不要开启太多规则,不然会降低效能。
详细的安装方法,请查看上面的官方网站

精炼步骤:

  1. 从官方网站下载稳定版本的规则

    1
    2
    3
    wget https://github.com/coreruleset/coreruleset/archive/v3.3.0.tar.gz  #下载
    mv v3.3.0.tar.gz coreruleset_v3.3.0.tar.gz #改名方便辨别
    tar -xzf coreruleset_v3.3.0.tar.gz #解压
  2. 复制配置文件和规则到nginx的配置文件夹

    进入刚刚下载的文件夹coreruleset_v3.3.0,进行复制

    1
    2
    sudo cp crs-setup.conf.example  /etc/nginx/modsecurity/crs-setup.conf
    sudo cp -r ./rules /etc/nginx/modsecurity/
  3. 修改配置 /etc/nginx/modsecurity/modsecurity.conf

    • 根据服务的需要,调整配置
    1
    2
    3
    SecRuleEngine DetectionOnly #它只会检测到所有的攻击,并根据攻击产生错误,但它不会在服务器上阻止任何东西

    SecRuleEngine On #将在服务器上激活ModSecurity防火墙。它会检测并阻止该服务器上的任何恶意攻击。
    • 引入规则库
    1
    2
    Include /etc/nginx/modsecurity/crs-setup.conf
    Include /etc/nginx/modsecurity/rules/*.conf
  1. 根据自己的需要编辑crs-setup.conf 配置防火墙规则

解决错误

重新启动nginx看看会发生什么,如果一切顺利,正常启动,那么就祝贺你,太走运了。
如果有错误,不用害怕,根据下面的,一步一步来解决。
去/var/log/nginx/error.log 查看出错信息(日志地址根据nginx.conf设置来,你的不一定是这个):

  1. 错误1: Failed to locate the unicode map file

    1
    2020/11/24 18:00:36 [emerg] 3015#3015: "modsecurity_rules_file" directive Rules error. File: /etc/nginx/modsecurity/modsecurity.conf. Line: 236. Column: 17. Failed to locate the unicode map file from: unicode.mapping Looking at: 'unicode.mapping', 'unicode.mapping', '/etc/nginx/modsecurity/unicode.mapping', '/etc/nginx/modsecurity/unicode.mapping'.  in /etc/nginx/conf.d/dev2.ddkids.com.conf:13

    解决方法:
    在modesecurity源码文件夹将 unicode.mapping 复制到/nginx/modesecurity,问题得到解决。

编写简单脚本,测试是否生效。

  1. 在 /etc/nginx/modsecurity/ crs-setup.conf 找到 [Anti-Automation / DoS Protection]一节,
    打开下面的动作,测试是否能阻止DoS攻击,被阻止会返回403错误给访问来源。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    SecAction \
    "id:900700,\
    phase:1,\
    nolog,\
    pass,\
    t:none,\
    setvar:'tx.dos_burst_time_slice=60',\
    setvar:'tx.dos_counter_threshold=100',\
    setvar:'tx.dos_block_timeout=120'"
  2. 注入式攻击测试

    网页打开下面的地址,如果看到403错误,就代表生效了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     http://dev2.ddkids.com/?q=%3Cscript%3E%20alert(1)%3C/script%3E
    ````



    ### 日志查看
    日常被阻挡的4xx,5xx请求都会被记录在:

    ````shell
    /var/log/modsec_audit.log

    可以通过编写脚本,定时将数据保存到数据库,或另存到本地。如果要合规的话,应该需要让日志方便可查吧?

    日志文件会很大,为了合规必须开启,但是最多保留15-30天的数据。如果开启,则必须编写脚本定时处理。

高级日志分析

  1. ModSecurity日志数据格式: https://github.com/SpiderLabs/ModSecurity/wiki/ModSecurity-2-Data-Formats

  2. 自动化存储和分析
    可以使用其他自动化工具来做可视化分析

使用扫描漏洞工具来测试

  1. 工具: Nikto2 基于WEB信息漏洞扫描
  1. WebScarab 网络爬虫:抓取目标网站

  2. NoSQLMap 代码注入攻击测试
    要使用稳定版,不要使用master版本,但是可能是自己不会用,没有攻击出什么东西。