———————————–

对于服务拒绝类攻击,分为识别和控制两阶段。

控制包含封禁、隔离和限制等手段。封禁是完全不允许访问;隔离是将异常流量隔离到沙箱环境,提供有限的服务器资源;限制类似上面的limit_req功能,即提供定量的服务资源。我倾向于封禁和隔离两种手段。

封禁可以按规则、模型。由于我们希望发现的是大量异常流量,而非个别异常点,故个人感觉,封禁规则更加合适。

封禁规则上,可以任意的http header变量为key(一般是ip或者特殊的cookie值),检查在M时间窗口内的访问频率,若达到频率限制,则封禁N时间,到期自动解封。个人感觉,利用Lua+redis会是一种比较简单可行的方法。对于一次请求而言:

  • 计算当前时间窗口的剩余秒数
  • 以指定的http header变量为key,增加redis计数器 ,以上一步剩余时间为超时时间
  • 检查redis计数器,若达到频率限制,则403forbid

整个nginx集群,使用统一的redis服务器,可以key做hash分组,以支持性能扩展。

应对以下几种攻击场景:

  • 单一IP频繁访问:以binary_remote_addr为key,来自该IP的访问都被限制
  • 单一IP频繁访问:以binary_remote_addr+user agent为key,来自该IP相同UA的访问都被限制
  • DDOS攻击相同URL:以refer+request_uri为key
  • 机器流量:以登录后的cookie中的userid为key,限制同一userid的频繁访问
  • 机器流量:以登录后的cookie中的userid+UserAgent+remote_Ip为key,限制同一userid、相同UA、相同IP的频繁访问
另外还可限制未登录用户的非法访问,检查cookie若不匹配,则不允许访问登录可见的URL。虽然这样会引入应用层逻辑,但在遭到攻击的情况下,可以减轻应用层server的压力。

隔离是将服务器分组,实现物理分离。可以按照ip切分,也可以按照cookie中种下的特殊值来切分(需要处理cookie为空的情况),甚至按照url切分,或者将频繁访问的流量隔离,让他们自己竞争去。

识别手段包含实时和非实时两种。上面的Lua实现方案采取的是实时识别,对于历史上发生过的攻击、或者已建立规则的攻击,可以实时识别。但不可避免遇到新类型的攻击,这就需要非实时的识别,并快速反馈到封禁策略(出于快速反馈这一点,我也希望使用Lua来实现封禁)。

日志分析,是一种非实时识别手段,可以多种维度比较正常与异常情况的流量,以报表/图表的形式,半自动化的发现异常流量和流量特征。与实时识别相比,该方式可以添加更多种类的监控。

比如,正常情况下对于某url的访问是100/s,某一时刻如果突然增加至800/s,那就可能是异常了,这时,就需要将这部分流量切分出来,查看IP/refer/cookie,找到合适的特征,一旦判断需要封禁,就可以快速组织Lua脚本,添加到nginx中。

———————————–

生成证书

1、制作CA证书:

ca.key CA私钥,其中需要Enter pass phrase for ca.key,长度在4到8191个字符之间,会生成ca.key文件,这里用123456:

$openssl genrsa -des3 -out ca.key 2048

生成的ca.key文件格式为:

—–BEGIN RSA PRIVATE KEY—–
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,3F630DA2B00F0C16

bJeth9Fw+lEyqPND9hgK2PZY7RmYLhwan9m2rJ3oU2xEEBdXo9ZLmw5Hn/jAvMk7

……

mdp+9KWmLOx6wlO4yQaNh8x8hFvPEKjnGQQLXv691prRRvzHRmvZVA==
—–END RSA PRIVATE KEY—–

ca.crt CA根证书(公钥),需要输入上一步时的pass phrase,并且输入区域、公司、姓名等信息:

$ openssl req -new -x509 -days 7305 -key ca.key -out ca.crt

生成的ca.crt文件格式为:

—–BEGIN CERTIFICATE—–
MIIEbTCCA1WgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBhTELMAkGA1UEBhMCQ04x

……

oIjcL41W4PddlVuwdXSK5FG1tmEmH+70OZ0TjwWeCGvz
—–END CERTIFICATE—–

2、制作生成网站的证书并用CA签名认证

假设网站名称为test.flykobe.com,生成的pem文件名称是test.flykobe.com.pem,期间需要Enter pass phrase for test.flykobe.com.pem,这里也是用123456

$ openssl genrsa -des3 -out test.flykobe.com.pem 1024

生成的test.flykobe.com.pem文件格式是:

—–BEGIN RSA PRIVATE KEY—–
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,929238294D9113A7

ZEjMq/Z9nAxE0l45maJ6F1qgQY5hKLEj54cItH3U1guOwvCVhsw0360OegCqxRjX

……

—–END RSA PRIVATE KEY—–

制作解密后的test.flykobe.com证书私钥,期间需要输入上一步的pass phrase:

$ openssl rsa -in test.flykobe.com.pem -out test.flykobe.com.key

生成的test.flykobe.com.key文件格式是:

—–BEGIN RSA PRIVATE KEY—–

MIICXAIBAAKBgQCzZNnCXHrT2O/snBw/qk0uURfDhlrYtq7ORSsWz9n3KYlDOmbB

……

sU37j0ywMYhzU7EuFUJfsgKQN8IHRTNDrLajywBzvcQ=
—–END RSA PRIVATE KEY—–

生成签名请求,期间需要输入上一阶段的pass phrase,并且输入区域等信息。

注意:在common name中填入网站域名,如test.flykobe.com,同时也可以使用泛域名如*.flykobe.com来生成所有二级域名可用的网站证书。

$ openssl req -new -key test.flykobe.com.pem -out test.flykobe.com.csr

生成的test.flykobe.com.csr文件格式是:

—–BEGIN CERTIFICATE REQUEST—–
MIIB3TCCAUYCAQAwgYUxCzAJBgNVBAYTAkNOMRAwDgYDVQQIEwdCZWlqaW5nMRAw

……

Lg==
—–END CERTIFICATE REQUEST—–

用CA进行签名,这里默认使用配置文件/usr/share/ssl/openssl.cnf,其中关于CA_default的dir配置路径默认是./demoCA,则需要从openssl路径下将demoCA目录cp到当前路径来:

$ cp /home/work/local/openssl/ssl/misc/demoCA . -rf

$ openssl ca -policy policy_anything -days 1460 -cert ca.crt -keyfile ca.key -in test.flykobe.com.csr -out test.flykobe.com.crt

生成的test.flykobe.com.crt文件包含Certificate信息和CERTIFICATE字符串。

合并crt文件,因为不这样做,可能会有某些浏览器不支持。:

$ cat ca.crt >> test.flykobe.com.crt

最终所需的文件是:test.flykobe.com.key test.flykobe.com.crt

配置nginx服务器

server {
    listen              443;
    server_name         test.flykobe.com;
    ssl                 on;
    ssl_certificate     /path/to/test.flykobe.com.crt;
    ssl_certificate_key /path/to/test.flykobe.com.key;

    ssl_protocols       SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ...
}

有些浏览器可能不接受这种个人生成的证书,可用curl测试,在本机上:curl "https://test.flykobe.com:443/" --cacert /home/work/local/nginx/sslbycy/ca.crt

IP封禁尝试

nginx的limit_req、limit_connect、limit_access等封禁限制模块,使用lua也可以实现自定义的封禁策略。这里测试https的封禁:

    server {
        listen 443;
        server_name  test.flykobe.com;

        ssl on;
        ssl_certificate /home/work/local/nginx/sslbycy/test.flykobe.com.crt;
        ssl_certificate_key /home/work/local/nginx/sslbycy/test.flykobe.com.key;

        location / {

            default_type text/html;
            content_by_lua '
                if (ngx.var.remote_addr == "172.22.178.21") then
                    ngx.say("Denied!")
                else
                    ngx.say("hello, world")
                end
                ';
        }
    }

HttpAccessModule是官方module,简单的基于ip进行deny和allow,可以结合error_page 403指令,自定义deny后的rewrite url。

HttpLimitReqModule是官方module,可以基于http header变量进行访问频率限制。具体的限制策略是:

The directive specifies the zone (zone) and the maximum possible bursts of requests (burst). If the rate exceeds the demands outlined in the zone, the request is delayed, so that queries are processed at a given speed. Excess requests are delayed while their number does not exceed a specified number of bursts. If the number of waiting requests exceed burst, the request is completed with the code 503 “Service Temporarily Unavailable”. By default, the burst is zero.

即,可以指定最大访问频率,例如10r/s(10 request/seconds),一旦超过该频率,后续请求将被delay。delay的最大队列个数是burst个。超过该个数的请求,会直接响应503。

HttpLimitZoneModule是官方module,可以基于http header变量限制并发连接数。对于超过并发数的请求,响应503。该module已经不再维护:Deprecated in 1.1.8, use Limit Conn Instead.

HttpLimitConnModule是官方module,功能与HttpLimitZoneModule类似。

encrypted-session-nginx-module是第三方模块,非直接用于防攻击,但作为加密、解密模块,可以用于设置cookie值等。

nginx_limit_access_module是第三方模块,目前还是处于beta阶段,从readme上看,它是一个封禁模块,可以根据ip等Nginx可获取到的变量(any of the variable in Nginx)进行封禁,包括一个POST接口用于维护封禁策略,但不包括攻击特征识别。

modsecurity是第三方模块,支持apache、IIS、Nginx等多种web server,其nginx版本目前还是处于beta阶段,由Trustwave SpiderLabs Research, Microsoft Security Research Center (MSRC), Yandex and community members开发。(在其document页列出一些book资源 http://www.modsecurity.org/documentation/,不过有不少是付费的。)具体功能待学习。

naxsi是第三方模块,用来防止 Xss & Sql Injection。

xss-nginx-module是第三方模块,Native cross-site scripting support in nginx 。

———————————————

2013-4-12 补充不依赖ngx_openresty包,安装官方nginx1.2.6:

安装lua:(若安装luajit,则不需要安装这个了)
cd /home/chengy/src/nginx-1.2.6/3rd/lua-5.1.5
make install INSTALL_TOP=/home/work/local/lua

安装luaJIT:
make install PREFIX=/home/work/local/luajit CC=/home/work/local/gcc4/bin/gcc
ln -sf luajit-2.0.0-beta10 /home/work/local/luajit/bin/luajit

安装lua依赖的lua-resty-memcached:(这个是lua依赖的lib库,由lua代码里的lua_package_path 和require指令引入

make install PREFIX=/home/work/local/lua-resty-memcached

nginx:
export LUAJIT_LIB=/home/work/local/luajit/lib
export LUAJIT_INC=/home/work/local/luajit/include/luajit-2.0
./configure –prefix=/home/work/local/nginx1.2.6 –with-http_ssl_module  –add-module=./3rd/lua-nginx-module-0.6.10 –add-module=./3rd/ngx_devel_kit-0.2.17/  –add-module=./3rd/echo-nginx-module-0.41/ –with-pcre=/home/chengy/src/pcre-8.32 –with-cc=/home/work/local/gcc4/bin/gcc
make
make install

vim /etc/ld.so.conf 增加 /home/work/local/luajit/lib/
sudo vim /etc/ld.so.conf

 

参考:

http://nginx.org/cn/docs/http/configuring_https_servers.html

http://blog.creke.net/762.html

One Comment

  1. wwek says:

    干货收了~~

Leave a Reply