多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > 服務器 > 對付短信發送攻擊

對付短信發送攻擊

來源:程序員人生   發布時間:2016-06-12 08:44:54 閱讀次數:2926次

簡介

自從某公司使用短信驗證碼驗證用戶真實性以來,短信便逐步成了公司業務的標配。現在幾近每家公司的服務都包括了短信發送這1功能。而用戶要求短信時,1般還未注冊,所以這個接口是匿名接口(不需要登錄)。

因而,壞蛋們就開始搗蛋了。他們通過對軟件抓包,得到用戶的要求消息,然后摹擬用戶對服務器瘋狂的發送這1要求,消耗公司的短信資源,騷擾無辜手機用戶,乃至造成短信通道梗塞,沒法發出正常消息。弄不好還被那些被騷擾者的投訴,封掉公司的短信通道,或被各手機廠商辨認為垃圾短信從而失去營銷效果,乃至影響正常業務。

今天,我就遇到這么個搗蛋鬼。他的手里有大約上千臺肉雞(科普1下:“肉雞”是指那些可以被黑客操控的無辜者的電腦,這些電腦用戶其實不知情。之所以可以被操控,多是由于安裝了有木馬程序的軟件,或系統存在漏洞被攻破),因而摹擬消息從全國遮天蔽日而來,服務器日志瘋狂刷屏。因而,我們立刻著手處理這件事情。

解決策略

我們想到了兩種策略:

  1. 使用iptables封IP
  2. 使用nginx lua + redis

兩種方案各有好處,如果訪問量不大的話,任選其1便可,如果訪問量極高,就需要酌情斟酌了。

我們采取的策略是第2種:

  1. 使用嵌入到nginx中的lua程序對用戶要求(僅限短信)+IP進行限定,使其:
    • 在1分鐘內的要求只允許發送1次短信
    • 如果1分鐘內超過1次,但在3次之內,則不發送短信,并給與正告
    • 如果1分鐘內超過3次,則制止這個IP的任何訪問
  2. 使用redis保存這些被封IP和帶時限的接口要求,以避免nginx沒法定義全局變量。這樣,nginx就能夠實現:
    • 對IP是不是允許放行的檢查
    • 令1分鐘前的IP訪問記錄自動過期

剛開始我們也使用了第1種,將這些IP直接加入到iptables中去,從內核層面封掉這些IP,只是我們覺得解封起來比較麻煩,而且沒法與我們的軟件集成,特別是很難實現邏輯集成(區分接口,允許1分鐘1次,3次以上才封IP),遂使用nginx+redis的方案,畢竟lua程序可以寫成我想要的邏輯。

實現

假設我們發送短信的接口是 /sendSms
在nginx.conf文件的 server 段內新建1個location塊,使其匹配正則表達式 ~* sendSms ,然后開始寫代碼吧:

location ~* sendSms { default_type 'application/json; charset=UTF⑻'; lua_need_request_body on; access_by_lua ' -- 為了lua代碼可以語法高亮,這里的內容放在后1個代碼塊,請自行復制到此處便可 '; proxy_pass http://app_backend; proxy_set_header X-Forwarded-For $remote_addr; proxy_pass_header Origin; }

下面的lua代碼請復制到上文單引號內:

-- ngx.exit(ngx.OK) local cjson = require "cjson" local ip = ngx.var.remote_addr if ngx.var.http_user_agent ~= nil or ngx.var.http_user_agent == "" then local agent = string.lower(ngx.var.http_user_agent) -- ngx.say("user agent:", ngx.var.http_user_agent) -- ngx.exit(200) local sIdx = string.find(agent, "httpclient") or string.find(agent, "java") -- ngx.say("sIdx:", sIdx) if (sIdx ~= nil) then ngx.status = ngx.HTTP_FORBIDDEN local msg = "你把硬盤拿過來,我直接把數據庫給你拷貝1份吧,這樣太慢了,我都急死了" ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null})) ngx.exit(ngx.status) return end end local redis = require "resty.redis" local red = redis.new() red:set_timeout(1000) local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR ngx.say("failed to connect: ", err) return end local forbidden, err1 = red:sismember("fbdIP", ip) -- ngx.say("forbidden:", forbidden) if forbidden == 1 then ngx.status = ngx.HTTP_FORBIDDEN local msg = "你把硬盤拿過來,我直接把數據庫給你拷貝1份吧,這樣太慢了,我都急死了" ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null})) ngx.exit(ngx.status) return end local key = "ip::" .. ip -- ngx.say("key:", key) local ttl, err1 = red:ttl(key) if ttl == -1 then red:del(key) end local res, err = red:get(key) red:incr(key) if (not res) or (res == ngx.null) then --[[ local msg = "failed to get cache" ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null})) --]] red:expire(key, 55) -- 55秒內不允許同1IP超過30次訪問 ngx.exit(ngx.OK) elseif tonumber(res) < 1 then ngx.exit(ngx.OK) elseif tonumber(res) >= 1 then ngx.status = 200 -- ngx.say("redis result is string 1") -- local msg = "我們認為你有歹意要求的嫌疑,請不要使用及其程序進行訪問" local msg = "慢點,無影手很多累啊" ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null})) if tonumber(res) > 10 then red:sadd("fbdIP", ip) end ngx.exit(ngx.status) return end

解釋

  • 前面對key的檢查不止使用了get() ,還使用了 ttl(),是由于redis的過期回收策略使用的是1種近似LRU算法,致使1定幾率的不刪除,所以使用ttl進行檢查。

條件

在大家讀到這篇文章的時候,我要順便說1下這個方案適用的條件。如果沒有這些條件,那這個方案對你就不可用,固然,解決思路或許可以有些幫助,如果你善于動手的話,很快也能弄好自己的解決方法。我們的系統滿足以下幾個條件:

  1. 固然是使用nginx做前端代理的web架構了
  2. 使用支持lua的nginx。直接從nginx官網下載依照的nginx是不支持的,需要額外下載lua代碼,并編譯到nginx中去。或直接使用openresty

建議

由于之前redis有個可以拿到root權限的漏洞,所以:

  1. 務必要對redis設置訪問密碼
  2. 最好將redis服務的綁定IP限定在內網IP上

redis 的key千萬不能被污染,否則正經常使用戶的IP會被誤傷封禁。

工具

在解決這個問題的時候,我們也是用廢了很多腦細胞的,為了節省你的腦細胞,我就免費讓你看看。

怎樣得知壞蛋在攻擊我的服務器呢?

我的辦法是:
通過將日志內所有對/sendSms接口的調用IP進行統計,找到那些調用次數比較多的,比如大于10次的。用這個命令就行了:

grep "POST /sendSms" logs/ikuaiyue.log | awk '{print $6}' | sed s/IP:// | sort | uniq -c | awk '{print $1 "\t" $2}' | sort -n

然后就會看到這樣的結果:

1 115.205.13.179 2 117.136.40.20 2 117.136.94.44 2 117.59.39.22 122 223.104.10.28

第1列是此IP的調用次數,第2列你懂。
好了,現在知道改怎樣辦了吧?

順便說1下,我們的日志是這個模樣滴:

[2016-05-30 01:25:20.451] [INFO] normal - IP:117.174.26.32 POST /sendSms [2016-05-30 01:26:17.918] [INFO] normal - IP:117.174.26.32 POST /sendSms ...

略微解釋下上面的命令:

grep "POST /sendSms" logs/ikuaiyue.log | awk '{print $6}' #按空格分割后的第6列(即IP:117.174.26.32) | sed s/IP:// #刪除字符"IP:" | sort #排序 | uniq -c #去重,并記下重復數,相當與做了個統計操作 #此時重復數字為第1列,IP被放在了第2列. #但此時格式上有個問題:首列數字是右對齊的 | awk '{print $1 "\t" $2}' #為了消除其右對齊,重新打印以便,并以tab分隔 | sort -n #以首列為根據排序。-n表示當作數字來排列,默許是當作字符串的

當你按我的方法設置好了nginx后,怎樣知道有無起作用呢?

把剛剛那個命令改改,只輸出最后3000行(具體數字看你的業務繁忙程度了)用做統計:

tail -n3000 logs/ikuaiyue.log | grep "POST /sendSms" | awk '{print $6}' | sed s/IP:// | sort | uniq -c | awk '{print $1 "\t" $2}' | sort -n
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 在线a亚洲视频播放在线观看 | 久久极品| 欧美久久xxxxxx影院 | 最近中文字幕国语免费高清6 | 日本欧美一区二区三区视频 | 国产精品视频视频久久 | 亚洲品质自拍视频 | 校园春色欧美激情 | 国产免费播放一区二区三区 | 日本xxxx护士hd | 精品一区二区三区高清免费观看 | 亚洲高清在线视频 | 无限国产资源 | 在线观看av网站永久 | 亚洲h视频在线观看 | 男女视频在线观看免费 | 亚洲品质自拍视频 | 黑人操大逼 | 亚洲伊人久久大香线蕉在观 | 国产精品亚洲一区二区三区 | 日韩欧美国产中文 | 国产成人永久免费视频 | 国产中文久久精品 | 美女又黄又免费 | 亚洲国产精品久久久久网站 | 国产亚洲美女精品久久久久 | 一级作爱视频 | 黄色免费观看视频网站 | 国产国语一级a毛片高清视频 | 欧美日韩不卡视频一区二区三区 | 最猛黑人xxxⅹ黑人猛交 | 女人18毛片a级18毛多水真多 | 三人性free欧美高清 | 久久亚洲精品一区成人 | 欧美熟妇下面毛毛黑森林 | 亚洲精品国产成人99久久 | 亚洲邪恶天堂影院在线观看 | 久久精品8 | 亚洲国产精品尤物yw在线观看 | 日本番囗 | 羞羞人成午夜爽爽影院 |