基于Openresty实现透明部署动态口令功能

2017-10-29 541763人围观 ,发现 7 个不明物体 网络安全

*本文原创作者:chenjc,本文属FreeBuf原创奖励计划,未经许可禁止转载

今天来讲讲基于openresty来实现透明部署动态口令功能,动态口令的基础概念这里就不讲了,网上的介绍很多,下面直入正题。

企业内部系统部署方案

通过在原有的业务系统上,部署WAF来反向代理业务请求,从而实现透明部署动态口令功能。

架构图如下:

11.png

WAF在接收到用户提交的特定请求时,会获取用户密码后六位,即动态口令的值,在对动态口令进行校验后,如果正确则重写该请求,将请求中的后六位删除再转发到业务系统,如果失败则丢弃该请求并提示。

通过以上方式,无需对原系统的代码进行任何修改,即可实现部署动态口令功能的效果。

实战:

新建文件 waf_otp_rule.json 内容如下:

[
	    {
        "rule_id": "300001",
        "rule_detail": "otp",
        "rule_action": "otp",
	"rule_otp_key":{"rule_var":"ARGS_POST","rule_specific": ["password"]},
         //指定otp验证码的参数名
	"rule_otp_password": "QDM7V5TEXXHSBEUJZCOJEGPR",//OTP密钥
	"rule_log": "false",
	"rule_otp_redirect":"http://10.201.XXX.XXX/WebGoat/login?error",//验证失败重定向的地址
        "rule_matchs": [
	                {
                "rule_vars": [
                    {
                        "rule_var": "URI"

                    }
                ],
                "rule_transform": ["none"],
                "rule_operator": "rx",
                "rule_pattern": "/WebGoat/login$",
                "rule_negated": false

            },

            {
                "rule_vars": [
                    {
                        "rule_var": "ARGS_POST",
			"rule_specific": ["password"]

                    }
                ],
                "rule_transform": ["length"],
                "rule_operator": "gt",
                "rule_pattern": "0",
                "rule_negated": false
            }
        ]
  }
]

以webgoat为例,规则的意思是,先识别识别请求url是否为 /webgoat/login,如果是,判断password参数的值的长度是否大于0,如果是,则执行rule_otp_key动作,将password的值的最后六位数取出来与OTP密钥生成的key进行匹配,正确则重写password参数,将参数后六位删除,确保后端业务系统能正确识别密码。如果动态口令识别失败,则将请求重定向到rule_otp_redirect指定的地址,即webgoat的登录页面。

效果:

首先打开微信小程序,搜索运维密码,这是个开源的动态口令客户端应用,每30秒会更新一次key,详细使用方法请查看官方说明。

12.jpg

输入账号freebuf,密码123456

13.png

OTP验证失败,返回登录页面

14.png

输入账号freebuf,密码123456898926 (898926为上图的验证码),成功登录后台

15.png

以上是针对单一应用的情况,下面讲讲内部应用统一登录验证的方案

架构图如下:

16.png

实现上和单应用的情况是差不多,针对每个不同的应用,单独配置nginx的反向代理和waf的规则文件即可,相比单应用情况不同的是,如果需要针对不同的用户,不同的应用单独设置OTP密钥,那该怎么办呢?可以参考下面的规则:

[
{
"rule_id": "300002",
"rule_detail": "otp",
"rule_action": "otp",
"rule_otp_key":{"rule_var":"ARGS_POST","rule_specific": ["password"]},
//指定otp验证码的参数名
"rule_otp_password": "QDM7V5TEXXHSBEUJZCOJEGPR",//OTP密钥
"rule_log": "false",
"rule_otp_redirect":"http://10.201.XXX.XXX/WebGoat/login?error",//验证失败重定向的地址
"rule_matchs": [
{
"rule_vars": [
{
"rule_var": "URI"
}
],
"rule_transform": ["none"],
"rule_operator": "rx",
"rule_pattern": "/WebGoat/login$",
"rule_negated": false
},
{
"rule_vars": [
{
"rule_var": "ARGS_POST",
"rule_specific": ["username"]
}
],
"rule_transform": ["none"],
"rule_operator": "rx",
"rule_pattern": "^(freebuf|admin|root)$",
"rule_negated": false
}
]
},
{
"rule_vars": [
{
"rule_var": "ARGS_POST",
"rule_specific": ["password"]
}
],
"rule_transform": ["length"],
"rule_operator": "gt",
"rule_pattern": "0",
"rule_negated": false
}
]
}
]

在原有规则的基础上,新加一个判断条件,当检测到用户为freebuf,admin,root时,才匹配该规则。将该规则复制,修改rule_otp_password的值,^(freebuf|admin|root)$为其他用户,即可为每个用户单独分配不同的密钥。针对其他应用也是同理,即可实现每个不同用户在每个不同应用上的密钥都是不同的。以上方法适合中小企业,也就是人数不多,应用也不多的情况。下面来讲讲在线上有大量用户情况下的部署情况,同理适用有大量用户的内部部署的情况。

线上系统部署实现方案:

线上部署的情况需要新加两条规则,如下:

[
{
"rule_id": "300004",
"rule_detail": "otp",
"rule_action": "otp_check",
"rule_otp_value":[{"rule_var":"ARGS","rule_specific":["username"]}]
"rule_otp_key":[{"rule_var":"REQUEST_COOKIES","rule_specific":["JSESSIONID"]}]
//OTP二维码页面权限验证
"rule_log": "false",
"rule_matchs": [
{
"rule_vars": [
{
"rule_var": "URI"
}
],
"rule_transform": ["none"],
"rule_operator": "rx",
"rule_pattern": "/WebGoat/login$",
"rule_negated": false
},
{
"rule_vars": [
{
"rule_var": "ARGS_RESP_HEADER",
"rule_specific": ["Location"]
}
],
"rule_transform": ["none"],
"rule_operator": "rx",
"rule_pattern": "welcome.mvc$",
"rule_negated": false
}
]
}
]

如果用户登录成功,以JSESSIONID为键,username为值,存入缓存中。

[
{
"rule_id": "300003",
"rule_detail": "otp",
"rule_action": "otp_qrcode",
"rule_otp_qrkey":[{"rule_var":"REQUEST_COOKIES","rule_specific":["JSESSIONID"]}]
//指定otp验证码的参数名
"rule_log": "false",
"rule_matchs": [
{
"rule_vars": [
{
"rule_var": "URI"
}
],
"rule_transform": ["none"],
"rule_operator": "rx",
"rule_pattern": "/otp/qrcode",
"rule_negated": false
}
]

这条规则为开启OTP密钥二维码页面,访问该请求时,会先校验JSESSIONID是否存在,如不存在拒绝访问,如果存在,查询JSESSIONID的值,即username的值是否存在redis服务器,如果不存在,生成随机OTP密钥,显示密钥二维码在页面上,并以用户名为键,密钥字符串为值存入redis服务器。如果存在,则将用户名对应的密钥取出,生成二维码显示在页面上。

最后是开启全局规则配置选项”otp_redis_login_check”:”true”。

处理流程图如下:

17.png

从图中可以看出,在开启了otp_redis_login_check选项后,只有访问了OTP二维码生成页面的用户才会开启OTP登录验证功能,那么就可以在线上环境中,增加一个”开启动态口令”按钮,当用户点击后重定向到二维码页面,用户通过微信小程序”运维密码”(开源,真正使用建议独立部署一套)完成OTP的配置,简单方便。动态口令功能不仅限于在登录的场景下使用,也可以在任意重要操作中,比如资金交易时输入资金交易密码,修改原有密码等业务场景中,新加业务场景只需新增一条规则即可。

以上是针对线上的情况,针对内部有大量用户的情况,方法跟上述差不多,比如先邮件通知,限期让大家登陆应用后访问OTP二维码页面,也可以配置规则,在登录成功时强制重定向到OTP二维码页面,等限期结束后,关闭otp_redis_login_check选项,强制所有登录走OTP,之后有没配置的,或者新员工,可以通过后台系统单独添加。

总结:

相比于网站后端开发OTP功能的方式,该方法的优点在于适用任何后端语言,无论后端是基于java,.NET,python还是其他语言都没有影响,无需对网站代码进行修改,即可实现快速部署。当需要新加验证场景时,只需新加规则即可,后端无需重新开发。当不需要该功能时,只需将该功能模块关闭,或者在网络架构中移除waf即可,不会对原有的前后端造成影响。

功能还在完善中,有不足的地方欢迎指出:)。邮箱jx-sec#outlook.com,对waf开发这块感兴趣的欢迎交流。文章为一个系列,主要讲WAF开发中各种有意思的新功能和思路,其他文章可以通过下面链接查看

基于Openresty实现业务安全防护     http://www.freebuf.com/vuls/150571.html

*本文原创作者:chenjc,本文属FreeBuf原创奖励计划,未经许可禁止转载

发表评论

已有 7 条评论

取消
Loading...

填写个人信息

姓名
电话
邮箱
公司
行业
职位
css.php