web server
框架 使用 Express
網址封包傳送,很多資料會透露在客端瀏覽器,可以從Header的地方去更改資訊。
Tools - npm - Helmet
npm i helmet --save
Helmet - 9 中介軟體函數組成:
csp
會設定Content-Security-Policy
標頭,以防範跨網站 Scripting 攻擊和其他跨網站注入。hidePoweredBy
會移除X-Powered-By
標頭。hsts
會設定Strict-Transport-Security
標頭,以施行安全的 (HTTP over SSL/TLS) 伺服器連線。ieNoOpen
會設定X-Download-Options
(適用於 IE8+)。noCache
會設定Cache-Control 和 Pragma
標頭,以停用用戶端快取。noSniff
會設定X-Content-Type-Options
,以阻止瀏覽器對脫離所宣告內容類型的回應進行 MIME 探查。frameguard
會設定X-Frame-Options
標頭,以提供 clickjacking 保護。xssFilter
會設定X-XSS-Protection
,以便在最新的 Web 瀏覽器中啟用跨網站 Scripting (XSS) 過濾器。
停用 X-Powered-By 標頭
app.disable('x-powered-by');
攻擊者可能使用這個標頭(依預設,會啟用),來偵測執行 Express 的應用程式,然後啟動特定目標的攻擊。
設定 Cookie 安全選項
npm i cookie-session
設定 Cookie 選項來加強安全:
- secure - 確保瀏覽器只透過 HTTPS 傳送 Cookie。
- httpOnly - 確保只透過 HTTP(S) 傳送 Cookie,而不透過用戶端 JavaScript 傳送,如此有助於防範跨網站 Scripting 攻擊。
- domain - 指出 Cookie 的網域;用來與發出 URL 要求之伺服器的網域相互比較。如果相符,接著會檢查路徑屬性。
- path - 指出 Cookie 的路徑;用來與要求路徑相互比較。如果此項與網域相符,則會傳送要求中的 Cookie。
- expires - 用來設定持續性 Cookie 的到期日。
var session = require('cookie-session');
var express = require('express');
var app = express();
var expiryDate = new Date( Date.now() + 60 * 60 * 1000 ); // 1 hour
app.use(session({
name: 'session',
keys: ['key1', 'key2'],
cookie: { secure: true,
httpOnly: true,
domain: 'example.com',
path: 'foo/bar',
expires: expiryDate
}
})
);
csrf 使用
一種資料儲存在session使用 對於csrf token的看法,使用cookie去驗證這筆資料的確定性。
- server side 儲存 cookie 驗證
- client side SPA 使用 Double Submit Cookie 核心概念是:「攻擊者的沒辦法讀寫目標網站的 cookie,所以 request 的 csrf token 會跟 cookie 內的不一樣」
分享文章: TechBridge 技術共筆部落格 - 讓我們來談談 CSRF
JWT 使用
JWT - JSON Web Token,來驗證身份的一種方式與習慣。
由三段加密組成
Header.Payload.Signature
- Header:含 Token 的種類及產生簽章(signature)要使用的雜湊演算法
- Payload:帶有欲存放的資訊(例如用戶資訊)
- Signature:編譯後的 Header、Payload 與密鑰透過雜湊演算法所產生
Header
Header 定義 Token 種類(typ)及雜湊演算法(alg)資訊的 JSON。
{
"alg": "HS256",
"typ": "JWT"
}
經過 Bash64 編譯
DQp7DQogICJhbGciOiAiSFMyNTYiLCANCiAgInR5cCI6ICJKV1QiDQp9
Payload
Payload 定義使用者和相關的資訊。exp 設定 Token 到期的時間、iat 設定 Token 簽發時間。
{
"_id": "01",
"name": "Hifounder",
"exp": 1300819380
}
經過 Bash64 編譯
ew0KICAiX2lkIjogIjAxIiwgDQogICJuYW1lIjogIkhpZm91bmRlciIsDQogICJleHAiOiAxMzAwODE5MzgwDQp9
不要將隱私資訊存放在 Payload 中,被轉換成 Base64 編碼後,能夠被輕易的轉換回來 ,不應該把用戶密碼等重要資料存在 Payload 中
Signature
Signature 將編碼過的 Header、Payload 與自定義密鑰,透過設定的雜湊演算法方式所產生的。 由於密鑰並非公開,因此伺服器端在拿到 Token 後,能透過解碼,驗證對方身份。 secret 是在 伺服器端才擁有的,所以無人得知,
// SHA-256
sha256(base64(header) + "." + base64(payload), "secret")
經過 SHA-256 編譯
b423b2e0eddb4f993b1b764d793ae47eee62762bd3f9d951262ffa0fa766b2b9
完整的JWT
DQp7DQogICJhbGciOiAiSFMyNTYiLCANCiAgInR5cCI6ICJKV1QiDQp9.ew0KICAiX2lkIjogIjAxIiwgDQogICJuYW1lIjogIkhpZm91bmRlciIsDQogICJleHAiOiAxMzAwODE5MzgwDQp9.b423b2e0eddb4f993b1b764d793ae47eee62762bd3f9d951262ffa0fa766b2b9
傳送給客端瀏覽器存放在,可存放在localStorage中,客戶端發送Request放在Header當中傳遞,Authorization
// JWT 是一種 Bearer Token (客端)
Authorization: 'Bearer ' + token
// Node Express (伺服器端)
const token = req.header('Authorization').replace('Bearer ', '')
console.log(token)