当我遇到了爬虫
运维同学,线上机器怎么又双叒叕挂了?
思路:
对爬虫也是相爱相杀多年,我对线上爬虫的应对出现的几个阶段:
1、分析日志,找出异常请求,封ip。
2、通过waf,针对某个uri ,进行限流(并且人机识别),控制的还是源ip,起初有点成效。
3、遇到大量单ip,触发不到人机识别,通过nginx自带的limit_req_zone进行uri限制,发现有效果,但是会误杀很多无辜。
map $request_uri $xx_limit { "~*/xxx/" "/xxx/"; default ""; } limit_req_zone $xx_limit zone=api_limit:10m rate=5r/s; 当请求路径包含/xxx/时,设置$xx_limit 为/xxx/,默认值为空,为空则limit_req_zone 不生效 limit_req zone=api_limit burst=10 delay=5; 每秒标准5个请求,burst可以让突发请求到10个,delay是当有突发请求时前五个直接请求,剩余的rate=5/s=200ms/个,消费1个,等待200ms
4、通过openresty,将请求相关数据发送到请求数据分析接口,分析之后返回结果,再由openresty判断是否放行,到目前为止立竿见影。
数据分析接口大概得思路就是:此时做一些限制已经没有用了(后端代码也无法更改),去推理人的行为,一个正常人要访问网站时会怎么操作,爬虫毕竟只是爬虫,没法跟人一样。
local http = require "resty.http" local httpc = http.new() local json = require("cjson") local uri = ngx.var.noparams_uri local headers=ngx.req.get_headers() local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr local ua=headers["User-Agent"] or '' local white_ua={"xxxx",} -- ua 白名单 local api_server="xxx:1234" local topic_id = "xxxxx" function req_fast_api() --return false 是异常请求,true是正常请求 local data = { TopicId = "xxxxxxx", From = 1710086694000, To = 1717151565000, Query = '"remote_addr:\"' .. ip .. '\""', Limit = 100 } local jsonStr = json.encode(data) local resp, err = httpc:request_uri("http://" .. api_server .. "/xxx/xxx/xxxx", { method = "POST", body = jsonStr, headers = { ["Content-Type"] = "application/json", }, }) if not resp then ngx.log(ngx.ERR, "cls 日志接口挂了。") return 500, "true" end return resp.status, resp.body end function main() -- 校验ua是否在白名单中 local outer_i for i, v in ipairs(white_ua) do if string.find(ua, v) then break else outer_i = i end end if outer_i == #white_ua then -- ua不在白名单中 ngx.header["Server"] = "xxxxx/xxx" local status, body = req_fast_api() ngx.log(ngx.ERR, ip .. "日志接口返回:" .. body .. ",且ua不在白名单中。") if body == "false" then ngx.status = ngx.HTTP_BAD_REQUEST ngx.say("xxxx") ngx.exit(ngx.HTTP_BAD_REQUEST) end end end local m = ngx.re.match(uri, "^/xxxxx/.*$", "jo") if m then main() else ngx.log(ngx.ERR, "非xxxx的忽略" .. uri) end