最近在看nodejs的相關東西扒磁,下面總結下幾種安全問題:
Crypto
Node.js 的crypto模塊封裝了諸多的加密功能, 包括 OpenSSL 的哈希弹砚、HMAC福稳、加密愧杯、解密、簽名和驗證函數(shù)等.
Node.js 的加密貌似有點問題, 某些算法算出來跟別的語言 (比如 Python) 不一樣. 具體情況還在整理中 (時間不定), 歡迎補充.
加密是如何保證用戶密碼的安全性?
在客戶端加密, 是增加傳輸?shù)倪^程中被第三方嗅探到密碼后破解的成本. 對于游戲, 在客戶端加密是防止外掛/破解等. 在服務端加密 (如 md5) 是避免管理數(shù)據(jù)庫的 DBA 或者攻擊者攻擊數(shù)據(jù)庫之后直接拿到明文密碼, 從而提高安全性.
TLS/SSL
早期的網(wǎng)絡傳輸協(xié)議由于只在大學內使用, 所以是默認互相信任的. 所以傳統(tǒng)的網(wǎng)絡通信可以說是沒有考慮網(wǎng)絡安全的. 早年的瀏覽器大廠網(wǎng)景公司為了應對這個情況設計了 SSL (Secure Socket Layer), SSL 的主要用途是:
認證用戶和服務器, 確保數(shù)據(jù)發(fā)送到正確的客戶機和服務器;
加密數(shù)據(jù)以防止數(shù)據(jù)中途被竊取;
維護數(shù)據(jù)的完整性, 確保數(shù)據(jù)在傳輸過程中不被改變.
存在三個特性:
機密性:SSL協(xié)議使用密鑰加密通信數(shù)據(jù)
可靠性:服務器和客戶都會被認證, 客戶的認證是可選的
完整性:SSL協(xié)議會對傳送的數(shù)據(jù)進行完整性檢查
1999年, SSL 因為應用廣泛, 已經(jīng)成為互聯(lián)網(wǎng)上的事實標準. IETF 就在那年把 SSL 標準化/強化. 標準化之后的名稱改為傳輸層安全協(xié)議 (Transport Layer Security, TLS). 很多相關的文章都把這兩者并列稱呼 (TLS/SSL), 因為這兩者可以視作同一個東西的不同階段.
HTTPS
在網(wǎng)絡上, 每個網(wǎng)站都在各自的服務器上, 想要確保你訪問的是一個正確的網(wǎng)站, 并且訪問到這個網(wǎng)站正確的數(shù)據(jù) (沒有被劫持/篡改), 除了需要傳輸安全之外, 還需要安全的認證, 認證不能由目標網(wǎng)站進行, 否則惡意/釣魚網(wǎng)站也可以自己說自己是對的, 所以為了能在網(wǎng)絡上維護網(wǎng)絡之間的基本信任, 早期的大廠們合力推動了一項名為 PKI 的基礎設施, 通過第三方來認證網(wǎng)站.
公鑰基礎設施 (Public Key Infrastructure, PKI) 是一種遵循標準的, 利用公鑰加密技術為電子商務的開展提供一套安全基礎平臺的技術和規(guī)范. 其基礎建置包含認證中心 (Certification Authority, CA) 收擦、注冊中心 (Register Authority, RA) 贮配、目錄服務 (Directory Service, DS) 服務器.
由 RA 統(tǒng)籌、審核用戶的證書申請, 將證書申請送至 CA 處理后發(fā)出證書, 并將證書公告至 DS 中. 在使用證書的過程中, 除了對證書的信任關系與證書本身的正確性做檢查外, 并透過產生和發(fā)布證書廢止列表 (Certificate Revocation List, CRL) 對證書的狀態(tài)做確認檢查, 了解證書是否因某種原因而遭廢棄. 證書就像是個人的身分證, 其內容包括證書序號塞赂、用戶名稱泪勒、公開金鑰 (Public Key) 、證書有效期限等.
在 TLS/SLL 中你可以使用 OpenSSL 來生成 TLS/SSL 傳輸時用來認證的 public/private key. 不過這個 public/private key 是自己生成的, 而通過 PKI 基礎設施可以獲得權威的第三方證書 (key) 從而加密 HTTP 傳輸安全. 目前博客圈子里比較流行的是Let's Encrypt 簽發(fā)免費的 HTTPS 證書.
需要注意的是, 如果 PKI 受到攻擊, 那么 HTTPS 也一樣不安全. 可以參見HTTPS 劫持 - 知乎討論中的情況, 證書由 CA 機構簽發(fā), 一般瀏覽器遇到非權威的 CA 機構是會告警的 (參見12306), 但是如果你在某些特殊的情況下信任了某個未知機構/證書, 那么也可能被劫持.
此外有的 CA 機構以郵件方式認證, 那么當某個網(wǎng)站的郵件服務收到攻擊/滲透, 那么攻擊者也可能以此從 CA 機構獲取權威的正確的證書.
XSS
跨站腳本 (Cross-Site Scripting, XSS) 是一種代碼注入方式, 為了與 CSS 區(qū)分所以被稱作 XSS. 早期常見于網(wǎng)絡論壇, 起因是網(wǎng)站沒有對用戶的輸入進行嚴格的限制, 使得攻擊者可以將腳本上傳到帖子讓其他人瀏覽到有惡意腳本的頁面, 其注入方式很簡單包括但不限于 JavaScript / VBScript / CSS / Flash 等.
當其他用戶瀏覽到這些網(wǎng)頁時, 就會執(zhí)行這些惡意腳本, 對用戶進行 Cookie 竊取/會話劫持/釣魚欺騙等各種攻擊. 其原理, 如使用 js 腳本收集當前用戶環(huán)境的信息 (Cookie 等), 然后通過 img.src, Ajax, onclick/onload/onerror 事件等方式將用戶數(shù)據(jù)傳遞到攻擊者的服務器上. 釣魚欺騙則常見于使用腳本進行視覺欺騙, 構建假的惡意的 Button 覆蓋/替換真實的場景等情況 (該情況在用戶上傳 CSS 的時候也可能出現(xiàn), 如早起淘寶網(wǎng)店裝修, 使用 CSS 拼接假的評分數(shù)據(jù)等覆蓋在真的評分數(shù)據(jù)上誤導用戶).
過濾 Html 標簽能否防止 XSS? 請列舉不能的情況?
用戶除了上傳
alert('xss');
還可以使用圖片 url 等方式來上傳腳本進行攻擊
還可以使用各種方式來回避檢查, 例如空格, 回車, Tab
還可以通過各種編碼轉換 (URL 編碼, Unicode 編碼, HTML 編碼, ESCAPE 等) 來繞過檢查
CSP 策略
在百般無奈, 沒有統(tǒng)一解決方案的情況下, 廠商們推出了 CSP 策略.
以 Node.js 為例, 計算腳本的 hashes 值:
const crypto = require('crypto');
function getHashByCode(code, algorithm = 'sha256') {
return algorithm + '-' + crypto.createHash(algorithm).update(code, 'utf8').digest("base64");
}
getHashByCode('console.log("hello world");'); // 'sha256-wxWy1+9LmiuOeDwtQyZNmWpT0jqCUikqaqVlJdtdh/0='
設置 CSP 頭:
content-security-policy: script-src 'sha256-wxWy1+9LmiuOeDwtQyZNmWpT0jqCUikqaqVlJdtdh/0='
策略指令可以參見CSP Policy Directives以及阮一峰的博文,屈大神的博文
CSRF
跨站請求偽造 (Cross-Site Request Forgery, CSRF,https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet) 是一種偽造跨站請求的攻擊方式. 例如利用你在 A 站 (攻擊目標) 的 cookie / 權限等, 在 B 站 (惡意/釣魚網(wǎng)站) 拼裝 A 站的請求.
比如 Q 君是某論壇管理員. 已知這個論壇 A 刪除的接口是 post 到某個地址, 并指定一個帖子的 id. 那么我可以在自己的博客 B 上組織一個 CSRF 請求. 然后誘使 Q 君來訪問我的博客. 就可以在 Q 君不知情的情況下刪除掉我想刪的某個帖子.
釣魚方式包括但不限于公開網(wǎng)站 (xss), 攻擊者的惡意網(wǎng)站, email 郵件, 微博, 微信, 短信等及時消息.
同源策略是最早用于防止 CSRF 的一種方式, 即關于跨站請求 (Cross-Site Request) 只有在同源/信任的情況下才可以請求. 但是如果一個網(wǎng)站群, 在互相信任的情況下, 某個網(wǎng)站出現(xiàn)了問題:
...
以上情況下, 如果c.public.com上沒有預防 xss 等情況, 使得攻擊者可以基于此站對其他信任的網(wǎng)站發(fā)起 CSRF 攻擊.
另外同源策略主要是瀏覽器來進行驗證的, 并且不同瀏覽器的實現(xiàn)又各自不同, 所以在某些瀏覽器上可以直接繞過, 而且也可以直接通過短信等方式直接繞過瀏覽器.
預防:
A 站 (預防站) 檢查 http 請求的 header 確認其 origin
檢查 CSRF token
1.同源檢查
通過檢查來過濾簡單的 CSRF 攻擊, 主要檢查一下兩個 header:
Origin Header
Referer Header
2.CSRF token
簡單來說, 對需要預防的請求, 通過特別的算法生成 token 存在 session 中, 然后將 token 隱藏在正確的界面表單中, 正式請求時帶上該 token 在服務端驗證, 避免跨站請求.
中間人攻擊
中間人 (Man-in-the-middle attack, MITM) 是指攻擊者與通訊的兩端分別創(chuàng)建獨立的聯(lián)系, 并交換其所收到的數(shù)據(jù), 使通訊的兩端認為他們正在通過一個私密的連接與對方直接對話, 但事實上整個會話都被攻擊者完全控制. 在中間人攻擊中, 攻擊者可以攔截通訊雙方的通話并插入新的內容.
目前比較常見的是在公共場所放置精心準備的免費 wifi, 劫持/監(jiān)控通過該 wifi 的流量. 或者攻擊路由器, 連上你家 wifi 攻破你家 wifi 之后在上面劫持流量等.
對于通信過程中的 MITM, 常見的方案是通過 PKI / TLS 預防, 及時是通過存在第三方中間人的 wifi 你通過 HTTPS 訪問的頁面依舊是安全的. 而 HTTP 協(xié)議是明文傳輸, 則沒有任何防護可言.
不常見的還有強力的互相認證, 你確認他之后, 他也確認你一下; 延遲測試, 統(tǒng)計傳輸時間, 如果通訊延遲過高則認為可能存在第三方中間人; 等等.
SQL/NoSQL 注入
注入攻擊是指當所執(zhí)行的一些操作中有部分由用戶傳入時, 用戶可以將其惡意邏輯注入到操作中. 當你使用 eval, new Function 等方式執(zhí)行的字符串中有用戶輸入的部分時, 就可能被注入攻擊. 上文中的 XSS 就屬于一種注入攻擊. 前面的章節(jié)中也提到過 Node.js 的 child_process.exec 由于調用 bash 解析, 如果執(zhí)行的命令中有部分屬于用戶輸入, 也可能被注入攻擊.
SQL
Sql 注入是網(wǎng)站常見的一種注入攻擊方式. 其原因主要是由于登錄時需要驗證用戶名/密碼, 其執(zhí)行 sql 類似:
SELECT*FROMusersWHEREusernae='myName'ANDpassword='mySecret';
其中的用戶名和密碼屬于用戶輸入的部分, 那么在未做檢查的情況下, 用戶可能拼接惡意的字符串來達到其某種目的, 例如上傳密碼為'; DROP TABLE users; --使得最終執(zhí)行的內容為:
SELECT*FROMusersWHEREusernae='myName'ANDpassword='';DROPTABLEusers;--';
其能實現(xiàn)的功能, 包括但不限于刪除數(shù)據(jù) (經(jīng)濟損失), 篡改數(shù)據(jù) (密碼等), 竊取數(shù)據(jù) (網(wǎng)站管理權限, 用戶數(shù)據(jù)) 等. 防治手段常見于:
給表名/字段名加前綴 (避免被猜到)
報錯隱藏表信息 (避免被看到, 12306 早起就出現(xiàn)過的問題)
過濾可以拼接 SQL 的關鍵字符
對用戶輸入進行轉義
驗證用戶輸入的類型 (避免 limit, order by 等注入)
等...
NoSQL
看個簡單的情況:
let{user, pass, age}=ctx.query;
db.collection.find({
user, pass,
$where:`this.age >=${age}`
})
那么這里的 age 就可以注入了. 另外 GET/POST 還可以傳遞深層結構 (比如 ?name[0]=alan 傳遞上來), 通過 qs 之類的模塊解析后導致注入, 如cnodejs 遭遇 mongodb 注入.