前言
其實, 前端的安全並沒有很多, 不過知道了, 起碼後端兄弟不會那麼累了。 本文主要討論以下幾種攻擊方式 :
- XSS方式
- CSRF方式
- 點擊劫持
希望大家在閱讀完文本之後, 能夠很好地回答以下的幾個問題:
- 前端的攻擊方式有哪些?
- 什麼是XSS方式? XSS攻擊有幾種類型?如何防範XSS攻擊?
- 什麼是CSRF攻擊?如何防範CSRF攻擊?
- 如何檢測網站是否安全?
1. XSS方式
XSS(Cross-Site-Scripting),跨站腳本攻擊是一種代碼注入攻擊。攻擊者在目標網站上注入惡意代碼,當被攻擊者登錄網站時就會執行這些惡意代碼, 這些腳本可以讀取 cookie ,session tokens , 或者其他敏感的網站消息,對用戶進行釣魚欺詐,甚至發起蠕蟲攻擊等。
XSS的本質:惡意代碼未經過濾,與網站正常的代碼混在一起;瀏覽器無法分辨哪些腳本是可信的,導致惡意腳本被執行。由於直接在用戶的終端執行,惡意代碼能夠直接獲取用戶的信息,利用這些信息冒充用戶向網站發起攻擊
XSS的分類:
- 存儲型
- 反射型
- DOM 型
1.1 反射型XSS
當用戶點擊一個惡意鏈接, 或者提交一個表單, 或者進入一個惡意網站時, 注入腳本進入被攻擊者的網站。Web 服務器將注入腳本,比如一個錯誤信息, 搜索結果等 , 未進行過濾直接返回到用戶的瀏覽器上。
反射型XSS的攻擊步驟:
- 1.攻擊者構造出特殊的URL, 其中包含惡意代碼
- 2.用戶打開帶有惡意代碼的URL時, 網站服務端將惡意代碼從 URL中取出, 拼接在 HTML中返回給瀏覽器
- 3.用戶瀏覽器接收到響應後解析執行, 混在其中的惡意代碼也被執行。
- 4.惡意代碼竊取用戶數據併發送到攻擊者的網站, 或者冒充用戶的行為, 調用目標網站接口執行攻擊者指定的操作。
反射型 XSS 漏洞常見於 URL 傳遞參數的功能 , 如網站搜索 , 跳轉等。由於需要用戶主動打開惡意的 URL 才能生效 , 攻擊者往往會結合多種手段誘導用戶點擊
POST 的內容也可以觸發反射型 XSS , 只不過其觸發條件比較苛刻 (需要構造表單提交頁面 , 並引導用戶點擊),所以非常少見。
話不多說 , 我們來舉個例子
<code>//前端代碼
// index.html
// 由於必須誘導用戶點擊,所以可能是這樣的
<button>
//這是一張很好看的圖片
/<button>
// 後端代碼
// server.js
const express = require('express');
const app = express();
app.use(express.static(path.join(__dirname)))
app.get('/welcome',function(req, res) {
// 把惡意代碼當做字符串, 傳輸回瀏覽器
res.send(`${req.query.type}`);
res.end();
})
app.listen(3000, ()=> {
console.log('server is running at port 4000')
});
複製代碼/<code>
如果不希望被前端拿到 cookie, 後端可以設置 httpOnly (不過這個不是 XSS 的解決方案 , 只能降低受損範圍)
如何防範反射型 XSS攻擊?
對字符串進行編碼 對url的查詢參數進行轉義後再輸出到頁面
<code>app.get('/welcome',function(req,res) {
//對查詢參數進行編碼,避免反射型 XSS攻擊
res.send(`${encodeURIComponent(req.query.type)}`);
})
複製代碼/<code>
總結:簡單來說,前端向後端發送GET請求數據,後端返回結果之前,必須先對url查詢參數進行編碼再輸出到頁面
1.2 DOM型XSS
DOM型 XSS 攻擊 , 實際上就是前端 javaScript 代碼不夠嚴謹 , 把不可信的內容插入到了頁面 。在使用 .innerHTML , .outerHTML , appendChild , document.write()等 API 時要特別小心 , 不要把不可信的數據作為為 HTML 插入到頁面上 , 儘量使用 .innerText , .textContent , setAttribute()等。
DOM型XSS 的攻擊步驟:
- 1.攻擊者構造出特殊數據 , 其中包含惡意代碼
- 2.用戶瀏覽器執行惡意代碼
- 惡意代碼竊取用戶數據併發送到攻擊者的網站 , 或者冒充用戶的行為 , 調用目標網站接口執行攻擊者指定的操作。
如何防範 DOM型XSS攻擊
防範 DOM 型 XSS攻擊的核心就是對輸入內容進行轉義 (DOM 中的內聯事件監聽器和鏈接跳轉都能把字符串作為代碼運行, 需要對其內容進行檢查)
- 對於 url 鏈接(例如圖片的src屬性), 那麼直接使用 encodeURIComponent來轉義
- 非 url , 我們可以這樣進行編碼 function encodeHtml(str) { return str.replace(/"/g, '"') .replace(/'/g, ''') .replace(//g, '>'); } 複製代碼
DOM型 XSS攻擊中 , 取出和執行惡意代碼由瀏覽器端完成 , 屬於前端 javaScript 自身的安全漏洞
再舉個例子
<code>//前端
複製代碼/<code>
總結:就是不要相信用戶輸入的內容,如果非要在頁面上插入 HTML,就必須先轉義再插入。對於URL可以使用encodeURIComponent,對於非URL可以使用上面的方法
1.3 存儲型 XSS
惡意腳本永久存儲在目標服務器上。當瀏覽器請求數據時, 腳本從服務器傳回並執行,影響範圍比反射型和 DOM型 XSS更大。 存儲型XSS攻擊的原因仍然是沒有做好數據過濾;
- 前端提交數據到服務端時 , 沒有做好過濾;
- 服務端在接受到數據時 , 在存儲之前 , 沒有做好過濾
- 前端從服務端請求到數據 , 沒有過濾輸出。
存儲型XSS的攻擊步驟
- 1.攻擊者將惡意代碼提交到目標網站的數據庫中
- 2.用戶打開目標網站時 , 網站服務端將惡意代碼從數據庫取出, 拼接在 HTML中返回給瀏覽器
- 3.用戶瀏覽器接收到響應後解析執行, 混在其中的惡意代碼也被執行。
- 4.惡意代碼竊取用戶數據併發送到攻擊者的網站, 或者冒充用戶的行為, 調用目標網站接口執行攻擊者指定的操作
這種攻擊常見於帶有用戶保存數據的網站功能, 如論壇發帖, 商品評論, 用於私信等。
如何防範存儲型 XSS攻擊
- 1.前端數據傳遞給服務器之前, 先轉義/過濾(防範不了抓包修改數據的情況)
- 2.服務端接收到數據, 在存儲到數據庫之前, 進行轉義/過濾
- 3.前端接收到服務器傳遞過來的數據, 在展示到頁面前, 先進行轉義/過濾
來一個例子, 可能長一點 , 登錄用了一下 cookie
<code>// 前端
// index.html
論壇
//前端 login.html
登錄
// 後端 server.js
const express = require('express');
const app = express();
const path = require('path');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
//設置路徑
app.use(express.static(path.join(__dirname, 'src')));
app.use(express.static(path.join(__dirname, '../')));
//將參數轉換成對象
app.use(bodyParser.urlencoded({ extended: true }));
//req.cookie[xxx] 獲取cookie
app.use(cookieParser());
//用戶列表
let userList = [{ username: 'zs', password: '123456' }, { username: 'star', password: 'star' }];
let SESSION_ID = 'connect.sid';
let session = {};
//登錄接口
app.post('/api/login', (req, res) => {
let { username, password } = req.body;
let user = userList.find(item => item.username === username && item.password === password);
if (user) {
//用戶登錄後,給一個標識(cookie登錄)
const cardId = Math.random() + Date.now();
session[cardId] = { user };
res.cookie(SESSION_ID, cardId);
res.json({ code: 0 });
} else {
res.json({ code: 1, error: `${username} does not exist or password mismatch` });
}
});
//1.反射型XSS攻擊: http://localhost:3000/error?type=
app.get('/error', function (req, res) {
res.send(`${req.query.type}`); //拿到 url 上的 type 參數,並返回給前端
});
app.get('/welcome', function (req, res) {
//對查詢參數進行編碼,避免XSS攻擊
res.send(`${encodeURIComponent(req.query.type)}`);
//對type查詢參數進行編碼,即可解決當前的XSS攻擊(可重啟服務查看)
// res.send(`${encodeURIComponent(req.query.type)}`);
});
//安全的評論列表
let comments2 = [
{ username: 'zs', content: '我是zs' },
{ username: 'hw', content: '我是hw' },
{ username: 'star', content: '大家好,我是Star' },
]
app.get('/getComments2', function (req, res) {
res.json({ code: 0, comments: comments2 });
});
function encodeHtml(str) {
return str.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/ .replace(/>/g, '>');
}
app.post('/addComment2', function (req, res) {
//cardId (req.cookies[SESSION_ID])要派上用場啦~
let info = session[req.cookies[SESSION_ID]];
if (info) {
//用戶已經登錄
let username = info.user.username;
// 服務器接收到數據後 , 在存儲到數據庫之前 , 進行轉義/過濾
comments2.push({ username, content: encodeHtml(req.body.comment) });
res.json({ code: 0, comments: comments2 });
} else {
res.json({ code: 1, error: 'user not logged in.' });
}
});
app.listen(3000);
複製代碼/<code>
總結:
- 1.惡意腳本如果未經過轉換, 存儲到了後臺。任何用戶訪問此頁面, 都會執行惡意腳本
- 2.說白了就是增加字符串的過濾: 前端輸入時過濾 服務端增加時過濾 前端輸出時過濾
1.4 JSONP中存在的 XSS 安全問題
<code> //一個 jsonp函數大概是這樣
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let/> params = JSON.parse(JSON.stringify(params));
let arrs = [];
for (let key in params) {
arrs.push(`${key}=${params[key]}`);
}
arrs.push(`callback=${callback}`);
/> document.body.appendChild(script);
console.log(callback)
window[callback] = function (data) {
resolve(data);
document.body.removeChild(script);
}
})
}
//如果我這樣調用 ,就會 XSS安全問題
jsonp({
url: 'http://localhost:3000/say',
params: {
wd: 'I Love you'
},
callback: 'alert(1)'
}).then(data => {
alert(data.username)
console.log(data)
})
複製代碼/<code>
簡單來說就是把 jsonp中回調函數的參數設置為惡意代碼
常見的預防操作
- 將重要的cookie標記為http only,這樣的話Javascript 中的document.cookie語句就不能獲取到cookie了。
- 只允許用戶輸入我們期望的回調參數 let { callback} = req.query; if(callback === 'show') { callback = 'show'; } else { res.end('error') } 複製代碼
- 過濾或移除特殊的Html標籤
//safe.html 顯示頁面
轉賬
用戶:
餘額:
<button>
/<button>
閱讀更多 Echa攻城獅 的文章