序言
我也是很頭疼,我這個小破網站居然有人攻擊。還特麼的連續攻擊。本來吧小網站我覺得就是自己寫寫部落格,然後大家給我評論下。互幫互助,團結友愛。結果吧,我被教育了。挺慘痛的回憶。
然後就是個人站長的流量問題。
一、網際網路站點要點
1、搜尋引擎
這個很重要,就是,一定要服務端渲染。
沒有別的原因,你的網站如果是面向搜尋引擎的你必須是服務端渲染。不然是沒法被很好的搜尋,提升排名的。
我個人網站比較隱私,就沒搞那些東西了。不過我下一次必須要嘗試下服務端渲染,不然目前為止經驗尚淺啊。
2、流量問題
這裡說的流量問題和搜尋之類沒有什麼關係,就是單純的記住,別把圖片放上去。太吃流量了。你只是個小破站,就別搞那麼多了。
我為了圖片訪問速度和伺服器資源節省,把圖片放在了oss儲存上面,但是我想說。還是很貴的。
3、錢(圖片問題)
我給大家算一筆帳
目前伺服器(1核1G1M)是aliyun的,三年300,很便宜是吧,但是這是搞特價,正常一年需要一千。
如果你的網站開發的時候優化做的還可以,那麼首屏流量能控制在1M左右。這樣其實很快。
但是主要是圖片這塊。你如果把圖片放自己伺服器,那麼頻寬就不能很低,不然卡死。
如果放oss,那麼流量是按訪問量計算的。具體好像是1G2毛,總之挺貴的。你要是圖片很多,注意,隨便頁面切換,重新整理單人就能給你刷走上百流量。人數稍微多點……,之前知道一個公司。半年,流量費用20萬(主要用於視訊監控)。
所以個人博主們,自己的小破站圖片少放。
圖片最好是做好防盜鏈。不然流量也會很痛苦。
二、防禦機制舉例
這裡我先copy下阿里在eggjs方面的防禦,主要大家就是遇到這些了。
- XSS 攻擊:對 Web 頁面注入指令碼,使用 JavaScript 竊取使用者資訊,誘導使用者操作。
- CSRF 攻擊:偽造使用者請求向網站發起惡意請求。
- 釣魚攻擊:利用網站的跳轉連結或者圖片製造釣魚陷阱。
- HTTP引數汙染:利用對引數格式驗證的不完善,對伺服器進行引數注入攻擊。
- 遠端程式碼執行:使用者通過瀏覽器提交執行命令,由於伺服器端沒有針對執行函式做過濾,導致在沒有指定絕對路徑的情況下就執行命令。
我這次遇到的攻擊是CSRF方面的攻擊。
三、網站防禦的過程
1、監控你的介面訪問
後端:記錄下每個介面每次呼叫的的記錄。
前端:記錄下每個路由的變化之後的訪問記錄
這兩個是很簡單的記錄方式,但是能讓你最直觀的感受到,網站訪問的量是多少。還有就是監控你的網站是否惡意呼叫介面,惡意訪問等等。很直觀,很用好。
本身我是沒有增加該項功能的。但是經過慘痛教訓之後,我痛定思痛。增加了對應的記錄。
通過介面呼叫的分析,得出對方是軟體重新整理頁面不斷惡意給我傳送評論
2、一輪交鋒,限制每個ip的傳送頻率(失敗)
這裡我貼下我中介軟體的程式碼,上面的說實在沒什麼意義,隨便寫一下中介軟體,資料庫記錄下資料即可。很簡單。
這裡我增加了redis,如果用資料庫效能不合適,並且不需要這麼笨重。快取比較不錯。至於為什麼不存在全域性變數。這個我得和大家科普下,不管是nodejs還是java等後端。是存在多執行緒或者多程式的。那麼一旦負載的時候,系統切換另一個程式,那麼就訪問不到資料了。
下面是限制訪問的中介軟體程式碼。
'use strict';
module.exports = (options, app) => {
return async function(ctx, next) {
try {
const ip = ctx.request.header['x-forwarded-for'];
const req_url = ctx.request.url;
// 得到最終url地址
const url = req_url.indexOf('?') === -1 ?
req_url :
req_url.substring(0, req_url.indexOf('?'));
// 儲存當前介面訪問資訊,後續有統計
ctx.service.interface.apiVisit.add({
ip,
url,
});
// 當前時間
const time = new Date().getTime();
// 獲取這個使用者該介面上次呼叫時間
const usr_api = await app.redis.get(ip + url);
// 攔截的介面
const intercept_api = options.api[url];
// 引數獲取完畢,設定最新引數
// 設定介面最新呼叫時間
await app.redis.set(ip + url, new Date().getTime());
if (intercept_api) {
if (time - usr_api > 5000) {
await next();
} else {
ctx.body = ctx.helper.result('', -1, '限制訪問' + intercept_api / 1000 + '秒', 0);
}
} else {
await next();
}
} catch (e) {
ctx.logger.warn(e);
}
};
};複製程式碼
配置部分config.js
// 給'interfaceRestriction'傳入引數
config.interfaceRestriction = {
// api封禁
api: {
'/dream-admin/noauth/blog/discuss/add': 1000 * 8,
},
// ip封禁,
ip: [
// '127.0.0.1',
],
};複製程式碼
上面的ip封禁沒有寫。暫時用不到
功能還可以擴充,比如限制ip,限制某個介面的訪問頻率等等。
3、第二輪(增加驗證碼)交鋒(勝利)
上面失敗的主要原因在於,ip實際上是可以偽造的。而且ip偽造成本很低。所以,你通過ip來限制對方這種爬蟲的惡意攻擊效果並不好,這隻能防止那些惡意灌水的人罷了。爬蟲分分鐘幾十上百萬給你攻擊過來,效果不好。
後來我在想怎麼才合適。
本身攻擊屬於csrf,但是通過csrf的方式來防禦對方沒有用。因為csrf本身是一種基於口令的方式。通過爬蟲工具,很容易可以竊取你的口令。
最後我就想著實在沒什麼好辦法就增加驗證碼了。
最後,用了 svg-captcha包。
下面是使用方式,比較簡單:
controller程式碼:
const { ctx, app } = this;
try {
// 生成算數驗證碼
const verify = svgCaptcha.createMathExpr({
size: 4,
ignoreChars: '0o1i',
noise: 5,
color: true,
background: '#7E7D78',
});
// 以ip為單位儲存驗證碼
const ip = ctx.request.header['x-forwarded-for'];
await app.redis.set(ip, verify.text);
ctx.response.type = 'image/svg+xml'; // 知道你個返回的型別
ctx.body = verify.data;
} catch (e) {
ctx.body = ctx.helper.result('', -1, e);
}複製程式碼
最後在建立評論的地方,增加驗證即可。
四、總結
1、所有的一切都是為了增加對方破譯的難度
2、不要對外開放建立資料的介面(萬惡之源)
3、做好資料監控,防範於未然。並且讓你能儘快的發現問題
4、如果你對外開放介面了,那麼請做好防小人的準備。(驗證碼真的是一個好東西)
5、所有驗證碼不如手機驗證碼來的可靠。
其實最後下來還是比較老套的一些東西。只是吧,好不容易勝利了。得發發動態。