[TOC]
# XSS
<span style="font-family: 楷體;font-weight: 700;">跨網站指令碼(Cross-site scripting,通常簡稱為:XSS)</span> 是一種網站應用程式的安全漏洞攻擊,是代碼注入的一種。它允許惡意使用者將程式碼注入到網頁上,其他使用者在觀看網頁時就會受到影響。這類攻擊通常包含了 HTML 以及使用者端腳本語言。
XSS 分為三種:反射型,存儲型和 DOM-based
## 如何攻擊
### 反射型
反射型 XSS 只是簡單地把用戶輸入的數據 “反射” 給瀏覽器,這種攻擊方式往往需要攻擊者誘使用戶點擊一個惡意鏈接,或者提交一個表單.
### 存儲型
存儲型 XSS 會把用戶輸入的數據 "存儲" 在服務器端,當瀏覽器請求數據時,腳本從服務器上傳回并執行。這種 XSS 攻擊具有很強的穩定性。
比較常見的一個場景是攻擊者在社區或論壇上寫下一篇包含惡意 JavaScript 代碼的文章或評論,文章或評論發表后,所有訪問該文章或評論的用戶,都會在他們的瀏覽器中執行這段惡意的 JavaScript 代碼。
### 基于DOM
基于 DOM 的 XSS 攻擊是指通過惡意腳本修改頁面的 DOM 結構,是純粹發生在客戶端的攻擊。
## 如何防御
最普遍的做法是轉義輸入輸出的內容,對于引號,尖括號,斜杠進行轉義
``` js
function escape(str) {
str = str.replace(/&/g, "&");
str = str.replace(/</g, "<");
str = str.replace(/>/g, ">");
str = str.replace(/"/g, "&quto;");
str = str.replace(/'/g, "'");
str = str.replace(/`/g, "`");
str = str.replace(/\//g, "/");
return str
}
```
對于顯示富文本來說,不能通過上面的辦法來轉義所有字符,因為這樣會把需要的格式也過濾掉。這種情況通常采用白名單過濾的辦法,當然也可以通過黑名單過濾,但是考慮到需要過濾的標簽和標簽屬性實在太多,更加推薦使用白名單的方式。
``` javaScript
var xss = require("xss");
var html = xss('<h1 id="title">XSS Demo</h1><script>alert("xss");</script>');
// -> <h1>XSS Demo</h1><script>alert("xss");</script>
console.log(html);
```
以上示例使用了`js-xss`來實現。可以看到在輸出中保留了`h1`標簽且過濾了`script`標簽
```javaScript
通過 whiteList 來指定,格式為:{'標簽名': ['屬性1', '屬性2']}。
不在白名單上的標簽將被過濾,不在白名單上的屬性也會被過濾。以下是示例:
// 只允許 a 標簽,該標簽只允許 href, title, target 這三個屬性
var options = {
whiteList: {
a: ["href", "title", "target"]
}
};
// 使用以上配置后,下面的HTML
// <a href="#" onclick="hello()"><i>大家好</i></a>
// 將被過濾為
// <a href="#">大家好</a>
```
### CSP
<span style="font-family: 楷體;font-weight: 700;">內容安全策略(Content Security Policy,CSP )</span> 是一個額外的安全層,用于檢測并削弱某些特定類型的攻擊,包括跨站腳本 XSS 和數據注入攻擊等。無論是數據盜取、網站內容污染還是散發惡意軟件,這些攻擊都是主要的手段。
我們可以通過 CSP 來盡量減少 XSS 攻擊。CSP 本質上也是建立白名單,規定了瀏覽器只能夠執行特定來源的代碼。
通常可以通過 HTTP Header 中的`Content-Security-Policy`來開啟 CSP
* 只允許加載本站資源
```html
Content-Security-Policy: default-src 'self'
```
* 只允許加載 HTTPS 協議圖片
```html
Content-Security-Policy: img-src https://*
```
* 允許加載任何來源框架
``` html
Content-Security-Policy: child-src 'none'
```
也可以通過 HTML 的 meta 標簽
```html
<meta http-equiv="Content-Security-Policy" content="script-src 'self' ">
```
具體使用方式見 [https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP)
# CSRF
<span style="font-family: 楷體;font-weight: 700;">跨站請求偽造(Cross-site request forgery / one-click attack / session riding / CSRF)</span> 是一種挾制用戶在當前已登錄的 Web 應用程序上執行非本意的操作的攻擊方法。XSS 利用的是用戶對指定網站的信任,CSRF 利用的是網站對用戶網頁瀏覽器的信任。
簡單點說,CSRF 就是利用用戶的登錄態發起惡意請求(一般是利用瀏覽器自動發送 cookie 的機制)。
## 如何攻擊
假設網站中有一個通過 Get 請求提交用戶評論的接口,那么攻擊者就可以在釣魚網站中加入一個圖片,圖片的地址就是評論接口
```html
<img src="http://www.domain.com/xxx?comment='attack'"/>
```
如果接口是 Post 提交的,就相對麻煩點,需要用表單來提交接口
```html
<form action="http://www.domain.com/xxx" id="CSRF" method="post">
<input name="comment" value="attack" type="hidden">
</form>
```
這里多提一句 Cookie 的發送機制:當向 HTTP 服務器請求某個 URL 時,瀏覽器將該 URL 與客戶主機中存儲的所有 Cookie 比較,如果發現域名相匹配 Cookie,則匹配 Cookie 中包含名字/值的那一行將被包含在 HTTP 請求頭中,以保證依賴于 Cookie 的功能得以實現。
雖然一般情況下瀏覽器的跨域限制會保護我們的 cookie,但是就如上面的簡單的例子 img 標簽的 src 是不受跨域限制的,那么 domain 屬性與這個 GET 請求的 domain 相同的 Cookies 都會被自動攜帶在請求頭中。

## 如何防范
防范 CSRF 可以遵循以下幾種規則:
1. Get 請求不對數據進行修改
2. 不讓第三方網站訪問到用戶 Cookie
3. 阻止第三方網站請求接口
4. 請求時附帶驗證信息,比如驗證碼或者 token(比如利用 axios 攔截器)
### SameSite
可以對 Cookie 設置`SameSite`屬性。該屬性設置 Cookie 不隨著跨域請求發送,該屬性可以很大程度減少 CSRF 的攻擊,但是該屬性目前并不是所有瀏覽器都兼容。
### 驗證 Referer
對于需要防范 CSRF 的請求,我們可以通過驗證 Referer 來判斷該請求是否為第三方網站發起的。HTTP Referer 是 HTTP 請求頭部的一部分,瀏覽器向 Web 服務器發送請求時一般都會自動帶上 Referer 以告訴服務器我是從哪個頁面來的。
### 使用驗證碼
驗證碼被認為是對抗 CSRF 攻擊最簡潔而有效的防御方法。
CSRF 攻擊往往是在用戶不知情的情況下構造了網絡請求。而驗證碼會強制用戶必須與應用進行交互(驗證碼由服務器隨機給出),才能完成最終請求。因此通常情況下,驗證碼能夠很好地遏制 CSRF 攻擊。
但驗證碼并不是萬能的,因為出于用戶考慮,不能給網站所有的操作都加上驗證碼。因此,驗證碼只能作為防御 CSRF 的一種輔助手段,而不能作為最主要的解決方案。
### Token
服務器下發一個隨機 Token(算法不能復雜),每次發起請求時將 Token 攜帶上,服務器驗證 Token 是否有效。
客戶端可以把 token 存在 cookie(如果 token 不是用于驗證的話,就防范 CSRF 而言就不存在 cookie 中了)或者 Local / Session Storage中。
# 加密算法分析
## 好的和不好的安全算法
在保護數據和受限的用戶信息方面,不是所有加密算法都具有同等效果。有的算法追求的是速度,力求快速而準確地加密和解密大量數據。有些則故意放慢速度。加入一個儲存一百萬加密用戶記錄的數據庫被竊取了,攻擊者在嘗試各種破解方法(例如使用字典中的每個詞)攥取數據,我們自然是希望其破解的速度越慢越好。
### 好的算法
`PBKDF2`
 PBKDF2 是 *Password-Based Key Derivation Function 2* 的簡稱,它在輸入(密碼)上應用一個偽隨機函數(例如哈希、暗碼或 HMAC),此外還會加鹽。這個過程會重復多次,得出一個密鑰。
`bcrypt`
 bcrypt 是基于 Blowfish 密碼法的密鑰導出函數,在保護密鑰的過程中會加鹽,而且具有適應能力。隨著時間的推移,我們可以增加迭代次數,不斷放慢速度,抵抗暴力攻擊。
`scrypt`
 scrypt 也是一個密鑰導出函數,專門用于抵御大型硬件攻擊,因為它需要大量內存,從而放慢了計算的速度。
### 不好的算法(對密碼而言)
`MD5`
 MD5(也稱消息摘要算法,message-digest algorithm)生成一個 128 位哈希值,通常使用一個 32 位十六進制數表示。
`SHA-1`
 SHA 是 Secure HashAlgorithm(安全哈希算法)的簡稱,其生成一個 160 位(20 字節)的哈希值,通常使用一個 40 位十六進制數表示。
`SHA-2`
 SHA-2 是 SHA-1 的繼任,包含 6 個哈希函數,生成的哈希值有 224 位、256 位、384 位和 512 位(SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/244、SHA-512/256)
## 靜態數據和動態數據
靜態數據指不活動的數字數據,存儲在服務器中,例如用于存儲密碼、個人資料或其他應用所需要數據的數據庫。
動態數據指傳輸中的數據,在應用和數據庫之間來回發送,或者在網站和 API 或外部數據源之間來回傳送。
<span style="font-size: 18px;font-family:宋體;">靜態數據</span>
籌劃應對數據泄露的措施時一定要考慮最壞的情況,我們應該假定攻擊者已經獲取了數據庫的訪問權,而且最終目的是獲取敏感的數據和密碼,遇到這種情況,數據庫本身必須還有一層加密措施。
加密數據庫應該使用強加密算法,如 SHA-256,或者更好的 AES 和 RSA;弱加密算法例如 MD5 和 SHA-1 絕不能用于加密數據庫。
其次,應該遵守一些標準做法:
- 把訪問控制(用戶登錄)和數據加密分開。如果用戶名或密碼失敗,數據庫本身應該保持加密,從而提供多級保護。
- 用于加密數據庫的密鑰應該定期更新。
- 加密密鑰應該與數據分開存儲。
<span style="font-size: 18px;font-family:宋體;">動態數據</span>
動態數據是大多數 Web 和應用開發者每天都要處理的數據,實際上,這包含幾種場景:
- 用戶填寫的注冊信息,用于訪問賬戶和認證身份。
- 把個人檔案信息傳輸給 API 服務,或從中獲取。
- 應用或網站收集的其他數據,傳送給數據庫存儲起來
## 密碼攻擊媒介
攻擊者獲取賬戶訪問權的方式有多種,有些設法操縱用戶,另一些則直指應用或網站,下面介紹其中幾個:
*釣魚*
  通過惡意網站或應用騙用戶提供登錄憑據。這種攻擊方式多以電子郵件詐騙形式出現,發件人假扮成某公司職員,編造一些理由讓用戶登錄惡意網站,盜取用戶的登錄憑據,攥取賬戶訪問權。
*社會工程*
  社會工程比釣魚高一個檔次,往往通過其他通信工具實施,例如電話。攻擊者假扮網絡技術人員,說是為了維修所報故障,讓用戶提供登錄憑據。
*暴力攻擊*
  計算特定長度下所有可能的密鑰組合方式,然后逐個嘗試,直到猜對密碼為止。增加密碼長度,破解密碼的時間將呈指數級增長。如果這個方法耗時太久,攻擊者會轉而采取其他方式,例如字典攻擊。抵抗暴力攻擊的方法之一是**密鑰延伸**。
*字典攻擊*
  迭代既定的字詞表(例如字典中的所有詞),嘗試所有組合,試圖找出匹配加密密碼的值。與暴力攻擊不同的是,字典攻擊只嘗試輸入常見的字、詞或短語。**加鹽**能有效地防范這種攻擊。
*彩虹表*
  一個大型列表,包含預先計算好的哈希值(使用給定的哈希函數計算)和得出哈希值的密碼。與字典攻擊不同,彩虹表攻擊每次嘗試破解時都會應用哈希函數,讓攻擊者直接對比預先計算好的哈希值和數據庫中保存的密碼哈希值,因此整個過程更為高效,加鹽能有效地防范這種攻擊。
## 加鹽
鹽值是一種隨機數據,計算密碼的哈希值時用于加強數據,抵御多種攻擊媒介。這個隨機數據(特別長)能確保生成的哈希值是唯一的,即使多個用戶使用相同的密碼,添加唯一的鹽值后能確保得到的哈希值是唯一的。
## node 中常用的加密算法
Hash 算法加密:
```js
const content = 'password' // 加密的明文
let md5 = crypto.createHash('md5') // 參數為任意 hash 加密的方法名稱
md5.update(content)
let d = md5.digest('hex') // 加密后的值為 d
console.log(`加密的結果: ${d}`)
```
Hmac 加密算法:
HMAC 是密鑰相關的哈希運算消息認證碼(Hash-based Message Authentication Code),HMAC 運算利用哈希算法,以一個密鑰和一個消息為輸入,生成一個消息摘要作為輸出。HMAC 可以有效防止一些類似 md5 的彩虹表等攻擊,比如一些常見的密碼直接 MD5 存入數據庫的,可能被反向破解。
<br>
定義 HMAC 需要一個加密用散列函數(表示為 H,可以是 MD5 或者 SHA-1)和一個密鑰 K。我們用 B 來表示數據塊的字節數,用 L 來表示散列函數的輸出數據字節數(MD5 中 L=16,SHA-1 中 L=20)。鑒別密鑰的長度可以是小于等于數據塊字長的任何正整數值。應用程序中使用的密鑰長度若是比 B 大,則首先用使用散列函數 H 作用于它,然后用 H 輸出的 L 長度字符串作為在 HMAC 中實際使用的密鑰。一般情況下,推薦的最小密鑰 K 長度是 L 個字節。
<br>
?下面為為一個 Hmac 加密示例(采用sha1方式):(由于密鑰會通過隨機生成的 16 位數進行加密后再對明文加密,每次生成的新密鑰(token)都不同,所以最后生成的密文也不會相同,這種加密不可逆
```js
const crypto = require('crypto')
// hmac-sha1 加密
const content = 'password' // 加密的明文
let token1 = 'miyao' // 加密的密鑰
let buf = crypto.randomBytes(16) // 參數表示要生成的鹽值的大小,單位為字節
token1 = buf.toString('hex') // 密鑰加密
console.log(`生成的 token: ${token1}`)
let SecretKey = token1 // 密鑰
let Signture = crypto.createHmac('sha1', SecretKey) // 定義加密方式
Signture.update(content)
let miwen = Signture.digest().toString('base64') // 生成的密文后將再次作為明文再通過 pbkdf2 算法迭代加密
console.log(`加密的結果f: ${miwen}`)
/*
每次都不一樣
生成的 token: f89599f5039f4728feabd0361947aede
加密的結果f: qnpe9g0t/Hm+/5ErsgyUUsVksN4=
生成的 token: 575b85075f9d66b1aed48f7b84e9b26e
加密的結果f: 4PvLjzId16X+piXsixeK+cGXNeg=
*/
```
# OAuth2
OAuth2 是一個開放授權標準,它允許用戶讓第三方應用訪問該用戶在某服務的特定私有資源但是不提供賬號密碼信息給第三方應用。
## OAuth2 的四個重要角色
1、`Resource Owner`:資源擁有者
2、`Resource Server`:資源服務器
3、`Client`:第三方應用客戶端
4、`Authorization Server`:授權服務器,管理 Resource Owner,Client 和 Resource Server 的三角關系的中間層
OAuth2 解決問題的關鍵在于使用 Authorization server 提供一個訪問憑據給 Client,使得 Client 可以在不知道 Resource owner 在 Resource server 上的用戶名和密碼的情況下消費 Resource owner 的受保護資源。
我們使用的第三方登錄很多功能都遵守 OAth2 規范,如 [QQ OAuth2 API](https://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token) [微信公眾號獲取 access_token](https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183)
## 部署 OAuth2 需要完成的工作
由于 OAuth2 引入了 Authorization server 來管理 Resource Owner,Client 和 Resource Server 的三角關系,那么想要用上 OAuth2,是必須要實現以下功能的:
1. 增加一個 Authorization server,提供授權的實現,一般由 Resource server 來提供。
2. Resource server 為第三方應用程序提供注冊接口。
3. Resource server 開放相應的受保護資源的 API。
4. Client 注冊成為 Resource server 的第三方應用。
5. Client 消費這些 API。
作為資源服務提供商來說,1,2,3 這三件事情是需要完成的。
作為第三方應用程序,要完成的工作是在 4 和 5 這兩個步驟中。
其中作為 Resource owner 來說,是不用做什么的,是 OAuth2 受益的千千萬萬的最終人類用戶。
## 資源服務提供商和用戶的職責
在一般情況下,Resource server 提供 Authorization server 服務,主要提供兩類接口:
- 授權接口:接受 Client 的授權請求,引導用戶到 Resource server 完成登陸授權的過程。
- 獲取訪問令牌接口:使用授權接口提供的許可憑據來頒發 Resource owner 的訪問令牌給 Client,或者由 Client 更新過期的訪問令牌。
除此之外,還需要提供一個第三方應用程序注冊管理的服務。通常情況下會為注冊完成的第三方應用程序分配兩個成對出現的重要參數:
- client\_id:第三方應用程序的一個標識 id,這個信息通常是公開的信息,用來區分哪一個第三方應用程序。
- client\_secret:第三方應用程序的私鑰信息,這個信息是私密的信息,不允許在 OAuth2 流程中傳遞的,用于安全方面的檢測和加密。
作為 Client:在 Client 取得 client\_id 和 client\_secret 之后。使用這些信息來發起授權請求、獲取 access\_token 請求和消費受保護的資源。
## OAuth2 授權流程
1\. 消費方:把用戶帶到服務器提供方登錄
2\. 服務提供方:獲得授權
3\. 服務提供方:把用戶重定向到消費方
4\. 消費方:使用授權碼請求訪問令牌
5\. 服務提供方:核發訪問令牌
6\. 消費方:使用訪問令牌訪問受保護的資源

這其中比較重要的一個概念是 **訪問令牌**,它代表的信息是整個 OAuth2 的核心,也是前 4 步最終要得到的信息。
訪問令牌是對 **消費方可訪問的用戶的哪些信息** 這個完整權限的一個抽象
訪問令牌背后抽象的信息有哪些呢?如下 3 類信息。
1. 客戶端標識
2. 用戶標識
3. 客戶端能訪問資源所有者的哪些資源以及其相應的權限。
有了這三類信息,那么資源服務器(Resouce Server)就可以區分出來是哪個第三方應用(Client)要訪問哪個用戶(Resource Owner)的哪些資源(以及有沒有權限)。
# DDoS 攻擊與防范
DDoS 的攻擊原理,往簡單說,其實就是利用 TCP/UDP 協議規律,通過占用協議棧資源或者發起大流量擁塞,達到消耗目標機器性能或者網絡的目的。
按照攻擊對象的不同,將對攻擊原理和攻擊危害的分析分成 3 類,分別是攻擊網絡帶寬資源、系統以及應用。具體可以參考 [這篇文章](https://www.cnblogs.com/163yun/archive/2018/06/01/9121857.html)
其中,比較常見的一種攻擊是 cc 攻擊。它就是簡單粗暴地送來大量正常的請求,超出服務器的最大承受量,導致宕機。
可以看看阮一峰老師的個人博客遭受 DDoS 攻擊時 [采取的措施](https://www.cnblogs.com/southx/p/9414695.html)

# 參考鏈接
[xss 與 csrf](https://github.com/dwqs/blog/issues/68)
[oauth2](https://www.cnblogs.com/linianhui/p/oauth2-authorization.html)
[token](https://juejin.im/post/58da720b570c350058ecd40f)
[node 中常用的加密算法](https://www.cnblogs.com/laogai/p/4664917.html)
[http://louiszhai.github.io/2016/03/05/xss/#Content-Security-Policy](http://louiszhai.github.io/2016/03/05/xss/#Content-Security-Policy)
[https://content-security-policy.com/](https://content-security-policy.com/)
[https://www.cnblogs.com/southx/p/9414695.html](https://www.cnblogs.com/southx/p/9414695.html)
[https://www.cnblogs.com/163yun/archive/2018/06/01/9121857.html](https://www.cnblogs.com/163yun/archive/2018/06/01/9121857.html)
- 序言 & 更新日志
- H5
- Canvas
- 序言
- Part1-直線、矩形、多邊形
- Part2-曲線圖形
- Part3-線條操作
- Part4-文本操作
- Part5-圖像操作
- Part6-變形操作
- Part7-像素操作
- Part8-漸變與陰影
- Part9-路徑與狀態
- Part10-物理動畫
- Part11-邊界檢測
- Part12-碰撞檢測
- Part13-用戶交互
- Part14-高級動畫
- CSS
- SCSS
- codePen
- 速查表
- 面試題
- 《CSS Secrets》
- SVG
- 移動端適配
- 濾鏡(filter)的使用
- JS
- 基礎概念
- 作用域、作用域鏈、閉包
- this
- 原型與繼承
- 數組、字符串、Map、Set方法整理
- 垃圾回收機制
- DOM
- BOM
- 事件循環
- 嚴格模式
- 正則表達式
- ES6部分
- 設計模式
- AJAX
- 模塊化
- 讀冴羽博客筆記
- 第一部分總結-深入JS系列
- 第二部分總結-專題系列
- 第三部分總結-ES6系列
- 網絡請求中的數據類型
- 事件
- 表單
- 函數式編程
- Tips
- JS-Coding
- Framework
- Vue
- 書寫規范
- 基礎
- vue-router & vuex
- 深入淺出 Vue
- 響應式原理及其他
- new Vue 發生了什么
- 組件化
- 編譯流程
- Vue Router
- Vuex
- 前端路由的簡單實現
- React
- 基礎
- 書寫規范
- Redux & react-router
- immutable.js
- CSS 管理
- React 16新特性-Fiber 與 Hook
- 《深入淺出React和Redux》筆記
- 前半部分
- 后半部分
- react-transition-group
- Vue 與 React 的對比
- 工程化與架構
- Hybird
- React Native
- 新手上路
- 內置組件
- 常用插件
- 問題記錄
- Echarts
- 基礎
- Electron
- 序言
- 配置 Electron 開發環境 & 基礎概念
- React + TypeScript 仿 Antd
- TypeScript 基礎
- React + ts
- 樣式設計
- 組件測試
- 圖標解決方案
- Storybook 的使用
- Input 組件
- 在線 mock server
- 打包與發布
- Algorithm
- 排序算法及常見問題
- 劍指 offer
- 動態規劃
- DataStruct
- 概述
- 樹
- 鏈表
- Network
- Performance
- Webpack
- PWA
- Browser
- Safety
- 微信小程序
- mpvue 課程實戰記錄
- 服務器
- 操作系統基礎知識
- Linux
- Nginx
- redis
- node.js
- 基礎及原生模塊
- express框架
- node.js操作數據庫
- 《深入淺出 node.js》筆記
- 前半部分
- 后半部分
- 數據庫
- SQL
- 面試題收集
- 智力題
- 面試題精選1
- 面試題精選2
- 問答篇
- 2025面試題收集
- Other
- markdown 書寫
- Git
- LaTex 常用命令
- Bugs