首頁>技術>

其實, 前端的安全並沒有很多, 不過知道了, 起碼後端兄弟不會那麼累了。

前端的攻擊方式有哪些?什麼是XSS方式? XSS攻擊有幾種型別?如何防範XSS攻擊?什麼是CSRF攻擊?如何防範CSRF攻擊?如何檢測網站是否安全?

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 才能生效 , 攻擊者往往會結合多種手段誘導使用者點選

話不多說 , 我們來舉個例子

// 後端程式碼// server.jsconst 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’)});複製程式碼如果不希望被前端拿到 cookie, 後端可以設定 httpOnly (不過這個不是 XSS 的解決方案 , 只能降低受損範圍)如何防範反射型 XSS攻擊?對字串進行編碼

對url的查詢引數進行轉義後再輸出到頁面

app.get(’/welcome’,function(req,res) {//對查詢引數進行編碼,避免反射型 XSS攻擊res.send(${encodeURIComponent(req.query.type)});})複製程式碼總結:簡單來說,前端向後端傳送GET請求資料,後端返回結果之前,必須先對url查詢引數進行編碼再輸出到頁面1.2 DOM型XSSDOM型 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, ‘<’).replace(/>/g, ‘>’);}複製程式碼DOM型 XSS攻擊中 , 取出和執行惡意程式碼由瀏覽器端完成 , 屬於前端 javaScript 自身的安全漏洞

再舉個例子//前端

複製程式碼 總結:就是不要相信使用者輸入的內容,如果非要在頁面上插入 HTML,就必須先轉義再插入。對於URL可以使用encodeURIComponent,對於非URL可以使用上面的方法 1.3 儲存型 XSS 惡意指令碼永久儲存在目標伺服器上。當瀏覽器請求資料時, 指令碼從伺服器傳回並執行,影響範圍比反射型和 DOM型 XSS更大。 儲存型XSS攻擊的原因仍然是沒有做好資料過濾;

前端提交資料到服務端時 , 沒有做好過濾;服務端在接受到資料時 , 在儲存之前 , 沒有做好過濾前端從服務端請求到資料 , 沒有過濾輸出。儲存型XSS的攻擊步驟1.攻擊者將惡意程式碼提交到目標網站的資料庫中2.使用者開啟目標網站時 , 網站服務端將惡意程式碼從資料庫取出, 拼接在 HTML中返回給瀏覽器3.使用者瀏覽器接收到響應後解析執行, 混在其中的惡意程式碼也被執行。4.惡意程式碼竊取使用者資料併發送到攻擊者的網站, 或者冒充使用者的行為, 呼叫目標網站介面執行攻擊者指定的操作這種攻擊常見於帶有使用者儲存資料的網站功能, 如論壇發帖, 商品評論, 用於私信等。

如何防範儲存型 XSS攻擊1.前端資料傳遞給伺服器之前, 先轉義/過濾(防範不了抓包修改資料的情況)2.服務端接收到資料, 在儲存到資料庫之前, 進行轉義/過濾3.前端接收到伺服器傳遞過來的資料, 在展示到頁面前, 先進行轉義/過濾來一個例子, 可能長一點 , 登入用了一下 cookie// 前端// index.html

論壇

打個招呼吧

});

//前端 login.html

登入

使用者名稱

密碼

// 後端 server.jsconst 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] 獲取cookieapp.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(/</g, ‘<’).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);複製程式碼總結:1.惡意指令碼如果未經過轉換, 儲存到了後臺。任何使用者訪問此頁面, 都會執行惡意指令碼2.說白了就是增加字串的過濾:前端輸入時過濾服務端增加時過濾前端輸出時過濾1.4 JSONP中存在的 XSS 安全問題//一個 jsonp函式大概是這樣function jsonp({ url, params, callback }) {return new Promise((resolve, reject) => {let script = document.createElement(‘script’);params = JSON.parse(JSON.stringify(params));let arrs = [];for (let key in params) {arrs.push(${key}=${params[key]});}arrs.push(callback=${callback});script.src = ${url}?${arrs.join('&')};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)})複製程式碼簡單來說就是把 jsonp中回撥函式的引數設定為惡意程式碼常見的預防操作將重要的cookie標記為http only,這樣的話Javascript 中的document.cookie語句就不能獲取到cookie了。

只允許使用者輸入我們期望的回撥引數

let { callback} = req.query;if(callback === ‘show’) {callback = ‘show’;} else {res.end(‘error’)}複製程式碼過濾或移除特殊的Html標籤

//safe.html 顯示頁面

轉賬使用者:餘額:

收款人

金額

釣魚連線

//釣魚頁面

你的錢不安全了~

返回檢視餘額

//惡意站點// fake1.html

//後端 server1.jsconst express = require(‘express’);const app = express();const path = require(‘path’);const bodyParser = require(‘body-parser’);const cookieParser = require(‘cookie-parser’);

//將引數轉換成物件app.use(bodyParser.urlencoded({ extended: true }));//req.cookie[xxx] 獲取cookieapp.use(cookieParser());

//設定路徑app.use(express.static(path.join(__dirname, ‘src’)));

//使用者列表let userList = [{ username: ‘hw’, password: ‘123456’, account: 1000 }, { username: ‘loki’, password: ‘loki’, account: 100000 }];

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 });}

});

//獲取資訊app.get(’/api/userinfo’, (req, res) => {let info = session[req.cookies[SESSION_ID]];if (info) {//使用者已經登入let username = info.user.username;res.json({ code: 0, info: { username : username, account: info.user.account} });} else {res.json({ code: 1, error: ‘user not logged in.’ });}})

//轉賬前, 先驗證 tokenapp.post(’/api/transfer3’, (req, res) => {let info = session[req.cookies[SESSION_ID]];if(info) {// 使用者已經登入let { payee , amount , token } = req.body;console.log(token);console.log(‘my_token_’ + req.cookies[SESSION_ID]);// 校驗 tokenif(token === ‘my_token_’ + req.cookies[SESSION_ID] && Number(amount)) {// token 正確let username = info.user.username;userList.forEach(user => {if(user.username === username) {user.account -= amount;}if(user.username === payee) {user.account += amount;}});res.json({ code : 0})}} else {res.json({code : 1, error : ‘user not logged in’})}})

app.listen(3001, () => {console.log(‘Server is running at port 3001’)})

//後端 server2.jsconst express = require(‘express’);const app = express();const path = require(‘path’);const bodyParser = require(‘body-parser’);const cookieParser = require(‘cookie-parser’);

//將引數轉換成物件app.use(bodyParser.urlencoded({ extended: true }));//req.cookie[xxx] 獲取cookieapp.use(cookieParser());

//設定路徑app.use(express.static(path.join(__dirname, ‘dest’)));

app.listen(3002, () => {console.log(‘Server is running at port 3002’)})複製程式碼總結:在登入之後,進入 safe.html 轉賬頁面在轉賬頁面如果點選 釣魚連線, 將被攻擊, 第三方網站將由於攜帶 使用者cookie, 所有可以繞開驗證, 冒用被攻擊者的身份, 做一些攻擊者指定的操作。4.Samesite Cookie屬性為了從源頭上解決這個問題, Google 起草了一份草案來改進 HTTP協議 , 為 Set-Cookie 響應頭新增 Samesite 屬性 , 它用來表明這個 Cookie 是個 “同站Cookie” , 同站 Cookie只能作為第一方 Cookie, 不能作為第三方 Cookie, Samesite 有兩個屬性值 , 分別是 Strict 和 Lax。

部署簡單, 並能有效防禦 CSRF 攻擊 , 但是存在相容性問題

Samesite=StrictSamesite=Strict 被稱為是嚴格模式 , 表明這個 Cookie 在任何情況都不可能作為第三方的 Cookie, 有能力阻止所有 CSRF攻擊。此時 , 我們在B 站點下發起對 A 站點的任何請求, A站點的 Cookie 都不會包含在 cookie請求頭中。

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 「python介面自動化」:正則用例引數化