深入解析Web安全小總結:XSS,CSRF及其防禦


深入解析Web安全小總結:XSS,CSRF及其防禦


前言

其實, 前端的安全並沒有很多, 不過知道了, 起碼後端兄弟不會那麼累了。 本文主要討論以下幾種攻擊方式 :

  • 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 , 只不過其觸發條件比較苛刻 (需要構造表單提交頁面 , 並引導用戶點擊),所以非常少見。

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


深入解析Web安全小總結:XSS,CSRF及其防禦


<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 中的內聯事件監聽器和鏈接跳轉都能把字符串作為代碼運行, 需要對其內容進行檢查)

  1. 對於 url 鏈接(例如圖片的src屬性), 那麼直接使用 encodeURIComponent來轉義
  2. 非 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>



分享到:


相關文章: