中、小企业如何自建免费的云WAF

概述

WEB攻击是十几年来黑客攻击的主流技术,国内的大厂们早已把WAF作为安全基础设施的标配,市面上也有很多安全厂商提供了WAF产品或云WAF服务。

对于没有自己安全团队,却又饱受sql注入、xss、cc等WEB攻击的中、小企业,对WAF的需求也是非常迫切的。

目前获取WAF的途径有以下几种:

购买安全厂商的WAF产品

使用云waf服务,将自己域名的DNS服务器设为云waf厂商提供的,或者将需要接入云waf的域名cname过去

或者从网上找一些免费或开源的waf使用

自制WAF

对于收入不错的公司使用收费的产品或服务无可厚非,但是有些公司会因预算、数据私密性(云waf可以捕获所有流量的请求和响应的内容)等原因,不打算使用收费的产品或服务。

这种情况下只能使用免费的waf了,或者按业务需求自制一款适合自己的云WAF。

笔者会通过本文详细阐述如何用一周的时间自制一款简单易用的云WAF,以下为已经完成的云WAF的文档及github地址:

项目站点:https://waf.xsec.io/

Github地址:https://github.com/xsec-lab

云WAF架构设计

物理架构

根据业务场景或需求的不同,WAF也有不同的架构,比如:

以模块的形式集成到本地WEB容器中,如mod_security、Naxsi

反向代理模式

硬件产品WAF

Agent+检测云模式

本文实现的云WAF采用了反向代理模式的架构

waf可以部署一台或者多台服务器中,如果业务规模较大,一台waf的性能已经无法满足业务需求,可以在waf前面使用LVS、haproxy、nginx等搭建负载均衡,通过VIP将前端的请求分发到后端的waf中

后端的app server为提供正常业务的web server,用户的请求会先经过waf进行过滤,如果是恶意的攻击请求,则会在waf层面阻断,如果是正常的请求才会转发到后端服务器

逻辑架构

x-waf由x-waf本身以及web管理后台x-waf-admin组成,其中:

x-waf基于openresty + lua开发

waf管理后台:采用golang + xorm + macrom开发的,支持二进制的形式部署

x-waf的实现

笔者呆过的2家公司都自主研发过云waf,架构一开始就设计成了适合大规模业务系统的,安装、部署、运维都比较复杂,不方便小企业快速部署,所以在参考了github中现有的开源的几款waf后,重新设计了一款轻量级的。

x-waf的执行流程

openresty默认不会执行lua脚本,需要在nginx.conf中进行配置,如下所示:

# 指定lua文件的查找路径

lua_package_path "/usr/local/openresty/nginx/conf/x-waf/?.lua;/usr/local/lib/lua/?.lua;;";

# 定义2个lua shared dict变量分别为limit和badGuys,分配的内存大小为100M

lua_shared_dict limit 100m; lua_shared_dict badGuys 100m;

# 开启lua代码缓存功能

lua_code_cache on;

# 让nginx在init阶段执行init.lua文件中的lua代码

init_by_lua_file /usr/local/openresty/nginx/conf/x-waf/init.lua;

# 让nginx在每个http请求的access阶段执行access.lua文件中的lua代码

access_by_lua_file /usr/local/openresty/nginx/conf/x-waf/access.lua;

openresty在init阶段会根据配置文件指定的位置导入json格式的规则到全局的lua table中,不同的规则放在不同的table中,以加快正则匹配的速度

waf = require("waf") waf_rules = waf.load_rules()

waf.load_rules会根据配置文件中指定的路径加载读取所有json格式的规则,并加载到不同的table中,然后封装一个get_rule的函数,方便在每个http进来时可以直接从lua table中获取对应类型的规则:

local _M = { RULES = {} }

function _M.load_rules() _M.RULES = util.get_rules(config.config_rule_dir)

return _M.RULES end

function _M.get_rule(rule_file_name) ngx.log(ngx.DEBUG, rule_file_name)

return _M.RULES[rule_file_name] end

util.get_rules会将指定文件中的规则按规则名保存到lua table中供waf.get_rule函数在需要的时候获取规则:

function _M.get_rules(rules_path)

local rule_files = _M.get_rule_files(rules_path)

if rule_files == {} then return nil end

for rule_name, rule_file in pairs(rule_files) do local t_rule = {}

local file_rule_name = io.open(rule_file)

local json_rules = file_rule_name:read("*a") file_rule_name:close()

local table_rules = cjson.decode(json_rules)

if table_rules ~= nil then

for _, table_name in pairs(table_rules) do table.insert(t_rule, table_name["RuleItem"]) end end _M.RULE_TABLE[rule_name] = t_rule end

return(_M.RULE_TABLE) end

每个请求进来时,waf会按ip白名单、ip黑名单、user_agent、是否cc攻击、url白名单、url黑名单、是否cc攻击、cookies、get和post参数的顺序进行过滤,如果匹配到其中任一种就会进行相应的处理(输出提示或跳转后),之后就不会继续判断是否为其他类型的攻击了。

function _M.check()

if _M.white_ip_check() then elseif _M.black_ip_check() then elseif _M.user_agent_attack_check() then elseif _M.white_url_check() then elseif _M.url_attack_check() then elseif _M.cc_attack_check() then elseif _M.cookie_attack_check() then elseif _M.url_args_attack_check() then elseif _M.post_attack_check() then else return end

end

对每个请求的每种参数类型的判断都是先获取到参数内容,然后再循环与该类参数的正则规则进行匹配,如果匹配到则认为是攻击请求,以下为对post参数进行过滤的函数:

-- deny post function _M.post_attack_check()

if config.config_post_check == "on" then ngx.req.read_body() local POST_RULES = _M.get_rule('post.rule')

for _, rule in pairs(POST_RULES) do local POST_ARGS = ngx.req.get_post_args() or {}

for _, v in pairs(POST_ARGS) do local post_data = "" if type(v) == "table" then post_data = table.concat(v, ", ")

else post_data = v

end if rule ~= "" and rulematch(post_data, rule, "jo") then util.log_record('Deny_USER_POST_DATA', post_data, "-", rule)

if config.config_waf_enable == "on" then util.waf_output()

return true end end end end end return false

end

waf管理后台x-waf-admin的实现

waf的规则是以json格式的字符串,人工维护起来容量出错,另外云waf会有多台waf同时工作,如果人工做waf的后端主机的管理、规则同步与主机配置的同步等这些运维工作的话,非常容易出错或者疏漏,所以有必要提供一个自动化管理、同步配置的管理后台。

waf管理后台的功能需求

方便部署,启动前只需做简单的配置即可,第一次启动时,x-waf-admin会在mysql中生成默认管理员以及默认的waf规则;

用户管理,支持管理员账户的增、改、删;

waf规则管理,支持waf规则的增、改、删除以及策略同步到所有waf服务器的功能;

后端站点管理,支持接入waf的站点的增、改、删除,以及单独同步或全部同步接入的后端站点的功能。

程序结构

为了方便部署,x-waf-admin没有采用python、php等需要搭建运行环境或依赖第3方包的语言,而是用可以直接编译为可执行文件的go语言写的,具体的技术栈为go语言 + macron + xorm。

项目结构如下:

hartnett at hartnett-notebook in /data/code/golang/src/xsec-waf/x-waf-admin (master●) $ tree -L 2 ├── conf │ └── app.ini ├── models │ ├── models.go │ ├── rules.go │ ├── site.go │ └── user.go ├── modules │ └── util ├── public │ ├── css ├── README.md ├── routers │ ├── admin.go │ ├── index.go │ ├── rules.go │ ├── site.go │ └── user.go ├── server ├── server.go ├── setting │ └── setting.go └── templates

conf为配置文件目录

models目录下为orm文件

modules为功能模块组件

public和templates分别为静态资源及模板文件所在的目录

routers目录下的为各路由文件

setting目录下为配置文件处理的文件

server.go为程序入口

规则管理功能的实现

用户管理、后端站点管理与规则管理功能的实现大同小异,都是类似flask、martini、tornado、django等MTV WEB框架的应用,为了减少篇幅,本文只写后端站点管理功能如何实现,完整的代码请参见github。

后端站点管理的ORM实现

先用xorm定义site的struct,然后再提供增、改、删、查看等方法,这些方法会被routers模块中的site文件调用:

// 因篇幅太长,省略部分代码,详细代码请查看github

// debuglevel: debug, info, notice, warn, error, crit, alert, emerg

// ssl: on, off

type Site struct { Id int64 SiteName string `xorm:"unique"` Port int BackendAddr []string Ssl string `xorm:"varchar(10) notnull default 'off'"` DebugLevel string `xorm:"varchar(10) notnull default 'error'"` LastChange time.Time `xorm:"updated"` Version int `xorm:"version"` // 乐观锁

}

func ListSite() (sites []Site, err error) { sites = make([]Site, 0) err = Engine.Find(&sites)

log.Println(err, sites)

return sites, err }

func NewSite(siteName string, Port int, BackendAddr []string, SSL string, DebugLevel string) (err error) {

if SSL == "" { SSL = "off" }

if DebugLevel == "" { DebugLevel = "error" } _, err = Engine.Insert(&Site{SiteName: siteName, Port: Port, BackendAddr: BackendAddr, Ssl: SSL, DebugLevel: DebugLevel})

return err }

后端站点管理的路由实现

首先import相应的包,然后分别编写以下处理器:

增加站点的get与post请求的处理器(NewSite、DoNewSite)

修改站点的get与post请求的处理器(EditSite、DoEditSite)

根据ID删除站点的get处理器(DelSite)

同步站点配置的处理器(SyncSite)

同步站点配置的API的处理器以及根据ID同步站点配置的API的处理器(SyncSiteApi、SyncSiteById)

// 因篇幅太长,省略部分代码,详细代码请查看github

func NewSite(ctx *macaron.Context, sess session.Store, x csrf.CSRF) { if sess.Get("uid") != "" { ctx.Data["csrf_token"] = x.GetToken() ctx.HTML(200, "newSite") } else { ctx.Redirect("/login/") } }

func DoNewSite(ctx *macaron.Context, sess session.Store) {

if sess.Get("uid") != nil {

log.Println(sess.Get("uid")) siteName := ctx.Req.Form.Get("sitename") port := ctx.Req.Form.Get("port") Port, _ := strconv.Atoi(port) backaddr := ctx.Req.Form.Get("backendaddr") backendaddr := strings.Split(backaddr, "\r\n") BackendAddr := make([]string, 0)

for _, v := range backendaddr {

if v == "" {

continue } v = strings.TrimSpace(v) BackendAddr = append(BackendAddr, v) } ssl := ctx.Req.Form.Get("ssl") debugLevel := ctx.Req.Form.Get("debuglevel")

log.Println(siteName, BackendAddr, ssl, debugLevel) models.NewSite(siteName, Port, BackendAddr, ssl, debugLevel) ctx.Redirect("/admin/site/list/") } else { ctx.Redirect("/login/") } }

model的初始化

大家一定注意到了,虽然用了mysql,但是没有要求在使用前手工去导入建表或插入初始化值的sql脚本,这是为神马呢?

因为我们使用了ORM,ORM会帮我们自动完成上面所说的操作,如下代码所示:

// 因篇幅太长,省略部分代码,详细代码请查看github

var ( Engine *xorm.Engine err error )

func init() {

// 从conf/app.ini获取数据库的配置信息 sec := setting.Cfg.Section("database")

// 连接数据库 Engine, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", sec.Key("USER").String(), sec.Key("PASSWD").String(), sec.Key("HOST").String(), sec.Key("NAME").String()))

if err != nil {

log.Panicf("Faild to connect to database, err:%v", err) }

// 新建site、user和rules表 Engine.Sync2(new(Site)) Engine.Sync2(new(User)) Engine.Sync2(new(Rules))

// 如果user表为空,则新建一个默认账户, ret, err := Engine.IsTableEmpty(new(User))

if err == nil && ret {

log.Printf("create new user:%v, password:%v\n", "admin", "x@xsec.io") NewUser("admin", "x@xsec.io") }

// 如果规则为空,则插入默认的初始化规则 ret, err = Engine.IsTableEmpty(new(Rules))

if err == nil && ret {

log.Println("Insert default waf rules") Engine.Exec(DefaultRules) } }

配置路由

当ORM、路由处理相关的代码写完后就可以在程序入口中配置路由了,将URL与路由处理的控制器对应起来,如下所示:

// 因篇幅太长,省略部分代码,详细代码请查看github

m.Group("/admin", func() {

m.Get("/index/", routers.Admin)

m.Group("/site/", func() {

m.Get("", routers.Admin)

m.Get("/list/", routers.Admin)

m.Get("/new/", routers.NewSite)

m.Post("/new/", csrf.Validate, routers.DoNewSite)

m.Get("/edit/:id", routers.EditSite)

m.Post("/edit/:id", csrf.Validate, routers.DoEditSite)

m.Get("/del/:id", routers.DelSite)

m.Get("/sync/", routers.SyncSite)

m.Get("/sync/:id", routers.SyncSiteById)

m.Get("/json/", routers.SiteJSON)

}) })

m.Group("/api", func() {

m.Get("/site/sync/", routers.SyncSiteApi)

m.Get("/rule/sync/", routers.SyncRuleApi) })

log.Printf("xsec waf admin %s", setting.AppVer)

log.Printf("Run mode %s", strings.Title(macaron.Env))

log.Printf("Server is running on %s", fmt.Sprintf("0.0.0.0:%v", setting.HTTPPort))

log.Println(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%v", setting.HTTPPort), m))

互动问题

从前有座山,山里有个庙,庙里有个灰帽子小明同学,他有次通过一些不可描述的手段,得到了某个网站的反弹Shell,虽是root权限,但利用方法不稳定。

此时小明发现服务器的内网网卡上跑着一个有root权限的redis,但加了密码,只见小明虎躯一震,顿时有了思路:留个webshell,以后通过webshell来执行redis反弹shell的exp。

但当他看完nginx的配置后又菊花一紧,因为这个站点只跑了lua的web应用:

# 省略部分配置内容

http { include mime.types; default_type application/octet-stream; lua_package_path "/data0/www/a.b.com/?.lua/?.lua;/usr/local/lib/lua/?.lua;;"; lua_code_cache off; init_by_lua_file /data0/www/a.b.com/init.lua; sendfile on; keepalive_timeout 65; server { listen 80; server_name a.b.com; location /api/ { content_by_lua_file /data0/www/a.b.com/web.lua; } } }

请听题

▼▼▼

树上7只猴,地上1只猴,小明如何留一个lua的webshell,请大家贴一下思路及代码。

时间: 2024-12-01 15:55:07

中、小企业如何自建免费的云WAF的相关文章

美国网速将提升至300M中 小企业将受益

12月1日消息,据<福布斯>报道,美国很多中小企业享受着比以前更快的网速,然而美国平均网速在工业化国家中的排名却比较低.新报告显示,美国平均网速为15.3Mbps,排名第十六,远低于排名领先的韩国(29Mbps)和挪威(21.3Mbps).     得益于企业对更大带宽需求的上升,美国预计最终会赶上来并可能超过世界其他地方.企业需要更好地服务客户.降低运营成本和提高效率,将推动网速快速提高.Forrester Research技术实务部门基础设施和运营首席分析师安德烈·凯德尼斯称:"

云计算技术如何帮助小企业成长

作为一个小企业主,你应该看到云计算可以让公司快速接入高端技术的价值.而在以前,只有那些拥有大量IT资源的公司才能使用这些技术.云计算带来的一大好消息是,如今你可以白手起家,即使没有多少前期资本投入,也可以随着业务的增长,以同样的速度扩大你的 IT 资源.如果你有计划将你的业务从一个小公司发展到企业层面,那么这篇文章将会帮助你了解云计算如何帮助你实现这个目标.从哪里开始?云计算有三个高级别层次:基础设施即服务(IaaS),平台即服务(PaaS)和应用程序/软件即服务(SaaS).除非你的公司是一个

小企业外包建站要注意的一些事

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 小企业的企业站一般都是外包给建站公司来做的,这样会达到利益最大化,那么外包建站要注意哪些问题呢?先撇开此话题看个案例普及点知识. 最近为了打发时搞了个wordpress博客,主要是介绍杨柳青年画和当地人文的.按照职业习惯搞了一下简单的竞争分析,这里只说说搜索引擎这块,很简单,一个非常不热门的词--杨柳青年画. 首页自然排名前四里就一个企业站,

一封普通美国人的来信:马云你是中美小企业之间最好的“导游”

马云请美国中小企业"重新发现中国"的喊话还在持续引发回响.参加阿里巴巴主办的"美国中小企业论坛"之2后,一位美国"群众"给马云写来激情洋溢的信,称马云为美国企业最好的导游:"一个曾经为到访中国的美国游客提供免费导游的人,如今为所有人带来了最好的'导游'--阐释摆在我们面前的中国机遇有多么巨大." 当地时间6月21日,马云在阿里巴巴"美国中小企业论坛"发表"重新发现中国"主题演讲. 超过3

谷歌停止向小企业提供免费Google Apps

网易科技讯 12月7日消息,据路透社报道,谷歌将停止向小企业免费提供商业版Google Apps软件,这是该公司寻求扩张核心广告业务以外收入的最新举措.谷歌周四宣布,10人或10人以下的企业如使用商业版Google Apps软件,如今将需要为每人每年付费50美元.Google Apps包含基于网络的电子邮件.文档处理.电子表格和演示工具.该公司在官方博客中表示,该变动将使得谷歌能够向企业客户提供更加稳定的服务.谷歌说,"企业的增长使得它很快就不适合使用基础版本, 它们想要获得诸如全天候客户支持.

小企业如何从百度推广的重重包围中巧妙借力突围

  一直以来,百度推广就是个颇具争议性的话题.一方面,百度通过这种付费排名服务在一定程度上影响了搜索结果的客观性和公正性,在引起关于搜索引擎道德争议的同时,却使它从中获得了非常庞大的推广收入,让百度成为中国乃至全球互联网企业盈利的"典范";而另一方面,百度推广也确实让许多的企业从中受益,通过付费获得一些热门关键词的靠前排名,获得了实实在在的订单,从而在行业的竞争之中得到了优势. 但这也使得企业在从事网络推广和营销时逐渐适应了"百度模式",陷入了一个烧钱的循环之中,使

谷歌停止向小企业免费提供商业版Google Apps

谷歌停止向小企业免费提供商业版GoogleApps 硅谷网12月8日消息 据国外媒体报道,谷歌表示,该公司将停止向企业机构免费提供商业版GoogleApps.谷歌此举是为了通过以前免费提供的服务创收. 此举表明谷歌对服务小型企业的重视.GoogleApps的竞争对手是微软Office和Exchange. 谷歌负责GoogleApps的高级副总裁桑达·皮采(SundarPichai)表示,该公司希望向使用免费版GoogleApps的小型企业提供专门的客户服务,目前只有付费客户才能获得这样的服务.他

阿里巴巴要建中国首个“小企业商业信用体系”

设10亿诚信保障金,你敢批发我敢保障,首创批发市场"先行赔付" 昨日,新华社与阿里巴巴公司联合启动"诚信中国年"系列活动,1万家小企业在线予以响应,亮出小企业向往"诚信"的活动标志.据介绍,"诚信中国年"行动,将由万家企业建言<小企业诚信公约>."诚信中国年"发起企业亮出"诚信"标志表诚信宣言."APEC诚信与小企业发展论坛"."中国诚信小企业&

阿里巴巴首建小企业商业信用体系

本报讯 3月15日,作为双方战略合作的具体内容之一,国家通讯社新华社与国内最大的电子商务平台.小企业代言人阿里巴巴公司联合启动 "诚信中国年"系列活动,1万家小企业在线予以响应,亮出小企业向往"诚信"的活动标志. 据介绍,"诚信中国年"行动,将由万家企业建言<小企业诚信公约>."诚信中国年"发起企业亮出"诚信 "标志表诚信宣言."APEC诚信与小企业发展论坛"."中