API作為資料通訊的大門,我們應該重視它的安全性,即便有些API可以很輕鬆地去進行調用,但不代表應該讓所有API都不受限制地被存取調用。
API可以做很多事,資料請求、處理敏感資訊、CRUD資料庫,甚至可以透過API控制電子設備,因此在APP開發上,針對API做嚴格的身份驗證及管理就會顯得格外重要。
Authentication & Authorization
當收到API請求時,通常會先進行 Authentication以及Authorization 身份驗證以及授權,成功授權才可以進行請求,
- Authentication(驗證): 顧名思義就是驗證使用者的身份是否正確,可透過username, password等方式進行驗證,這些資訊可在使用者的請求資訊中取得。
- Authorization(授權):當身份驗證成功,就能對進行授權,回傳資料給使用者。
1. 透過JWT實現授權
JWT (Json Web Token),通常在使用者成功登入後(驗證帳號密碼),會透過JWT產生一組具有時效性的Token給使用者(可儲存於cookies或localStorage),之後使用者在操作一些功能時必須將token一並附上才可使用。
生成JWT 程式範例
1 | const jwt = require('jsonwebtoken'); |
當使用者欲發送API請求,需要將登入後拿到的token一併附上,server會透過此token進行驗證,並給予授權。
驗證JWT 程式範例
1 | // 替換成你的密鑰 |
2. 細粒度存取控制Fine-Grained Access Control
定義角色和權限
定義每個角色可以做什麼事情,像是Admin可以進入控制台介面,User則不行。
RBAC (Role-Based Access Control)
RBAC是一種基於角色的訪問控制機制,每個角色都被授予一些權限,根據用戶的角色來決定對系統資源的訪問權限。在這種模式下,訪問權限會被賦予角色,用戶會被指派到一個或多個角色,這樣就能獲得相應的訪問權限。
1 | // 假設的用戶資料和角色 |
ABAC (Attribute-Based Access Control)
ABAC能夠提供基於多種條件的細粒度訪問控制,當需要根據用戶的具體屬性(年齡、位置、工作角色等)來細化訪問權限,通常會使用於更複雜或是需要極度安全性的場景,例如軍事系統、政府等。
3. API Gateway提升安全性
你可以把API網關想像成所有API的前門,所有請求都必須先通過前門才碰得到後面的API,舉個例子,假如每個API都有一樣的身份驗證middleware,這樣只需要在API gateway加入身份驗證,如此一來所有請求都會先進行身份驗證後,再將請求轉發到相應的API route。

API網關有以下用途:
增加安全性:防止未經授權的請求、濫用,或是DDoS攻擊。
Rate Limiting / Throttling:速率控制和節流,防止API過度使用,或是確保使用者之間的公平使用,可使用
express-rate-limit套件或是其他平台的服務,例如AWS API Gateway,Google Apigee等。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const rateLimit = require('express-rate-limit');
// 創建 rate limit 中間件
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分鐘的時間窗口
max: 100, // 在時間窗口內每個IP可以發起的最大請求數
standardHeaders: true, // 返回 rate limit 信息於 `RateLimit-*` headers
legacyHeaders: false, // 禁用 `X-RateLimit-*` headers
message: '請求過於頻繁,請在一段時間後再試。' // 當 rate limit 被觸發時返回的錯誤信息
});
// 使用這個中間件於你的應用中,例如
// app.use(limiter);
// 你也可以只對特定的路由使用這個中間件
// app.use('/api/', limiter);資料驗證:驗證傳入的資料是否符合格式標準,亦可預防SQL Injection等攻擊。
4. DOMPurify
當我們設計一個Input的元件給客戶端做輸入時,也有機率遭受到攻擊,因為input可以被寫入程式碼,例如:<script>alert(false)</script>,這時我們可以使用DOMPurify套件來清除輸入的資料,避免XSS攻擊。
安裝
1 | // 先執行命令進行安裝 |
使用
假如我們有一個input,取得使用者資訊
1 | <input type="text" id="userName"> |
將資訊傳送給後端時先透過DOMPurify處理過
1 | const input = document.getElementById('userName'); |
這樣就可以清除程式碼的注入,多提升了一點安全性。
5. 透過SSL/TLS加密傳輸資料
到ZeroSSL網站為你的網域申請SSL憑證,申請成功會拿到certificate.crt以及private.key兩個憑證檔案,自行將其放入伺服器資料夾(我是放到 /etc/ssl 目錄底下 ),再透過nginx.conf設定SSL憑證路徑,保存後執行Nginx即可。
1 | server { |
- ssl_protocols : 指定TLS版本亦可提升安全性,在此指定1.2跟1.3版本的連線,因為這些是目前被認為是安全的協議版本,較舊的協議版本(如 SSL v2、SSL v3、TLS v1.0)因已知的安全漏洞而不建議使用。
- ssl_ciphers : 這個指令用來定義哪些加密套件(cipher suites)被允許用於 SSL/TLS 連線。加密套件定義了數據加密、驗證和消息完整性檢查的算法。
除了像SSL這種加密傳輸的方式以外,也有一些靜態加密的方法
- bcrypt : 加密套件,在存入資料庫之前先對資料進加密。
- MySQL : InnoDB表空間加密,在資料寫入磁碟前被加密,從磁碟讀取時解密。
- MongoDB : 加密儲存引擎,也是在儲存資料之前先將其加密。
總結
提升API的安全性除了能夠保障你的數位資產,同時也能夠保障用戶對你的信任,在開發API前不妨連同安全性也一起考慮進去,以防範各種網路威脅。