邱敬幃 Pardn Chiu
目錄使用 JWT 實作身分驗證安裝 jsonwebtoken創建與驗證 JWT創建 JWT驗證 JWT防範 SQL 注入使用參數化查詢避免動態拼接 SQL預備語句使用 ORM (物件-關聯映射)防範跨站腳本攻擊 (XSS)輸出轉義Content Security Policy (CSP)系列文章相關連結
標籤

實作 JWT 會員登入以及防範SQL注入與XSS攻擊

Node.js: JavaScript 的後端魔法 (8)

511次觀看 1收藏 後端開發 Nodejs JWT
在現代 Web 開發中,保障使用者身分的安全性是一個至關重要的課題。
本篇將深入討論使用 JWT 實作身分驗證和實際安全技巧,其中包括防範 SQL 注入和更詳細的 XSS 防護。

使用 JWT 實作身分驗證

JSON Web Token (JWT) 是一種常見的身分驗證機制,它提供了安全而有效的方式來管理使用者身分。以下是在 Express 中實作 JWT 身分驗證的詳細步驟:

安裝 jsonwebtoken

首先我們使用指令安裝 jsonwebtoken:

  1. npm install jsonwebtoken

創建與驗證 JWT

在身分驗證的過程中,不僅要能夠成功創建 JWT,還需要能夠確保 JWT 的內容是有效且沒有被篡改的。以下是創建和驗證 JWT 的詳細操作:

創建 JWT

/login路由中,我們創建一個包含使用者資訊的 JWT:

  1. const jwt = require("jsonwebtoken");
  2. app.post("/login", (req, res) => {
  3.     // 模擬使用者身分驗證
  4.     const user = { id: 1, name: "Pardn Chiu"};
  5.     // 創建 JWT,並設置過期時間
  6.     jwt.sign({ user }, "[SecretKey]", { expiresIn: "1h" }, (err, token) => {
  7.         if (err) {
  8.             res.status(500).json({ err: err });
  9.         } 
  10.         else {
  11.             res.json({ token });
  12.         };
  13.     });
  14. });

在這個例子中,jwt.sign函數接收三個參數:

  • { user }: 儲存在 JWT 中的使用者資訊。
  • [SecretKey]: 簽署 JWT 的密鑰,請妥善保管密鑰。
  • { expiresIn: "1h" }: Token 過期時間,範例中設定為一小時。
驗證 JWT

在路由/needAuth中,我們使用jwt.verify方法來驗證 JWT:

  1. const jwt = require("jsonwebtoken");
  2. app.get("/needAuth", getToken, (req, res) => {
  3.     //驗證token內容
  4.     jwt.verify(req.token, "[SecretKey]", (err, data) => {
  5.         if (err) {
  6.             res.sendStatus(403);
  7.         } 
  8.         else {
  9.             res.json({ message: "Success", data: data });
  10.         };
  11.     });
  12. });
  13. //取得token內容
  14. function getToken(req, res, next) {
  15.     const authorization = req.headers["authorization"];
  16.     if (typeof authorization !== "undefined") {
  17.         const token = authorization.split(" ")[1];
  18.         req.token = token;
  19.         next();
  20.     } 
  21.     else {
  22.         res.sendStatus(403);
  23.     };
  24. };

在這個例子中,jwt.verify函數接收三個參數:

  • req.token: 要驗證的 JWT。
  • [SecretKey]: 簽署 JWT 的密鑰,必須與創建 JWT 時使用的密鑰相同。
  • 回調函數:用於處理驗證結果,如果驗證成功,data 將包含 JWT 中的資訊。

這樣,我們就能確保只有在 JWT 是有效且未被篡改的情況下,才能讀取/needAuth頁面。


防範 SQL 注入

SQL 注入是一種嚴重的安全威脅,攻擊者可能通過不當處理 SQL 查詢而獲取應用程式的資料庫訊息。以下是更詳細的 SQL 注入防護技巧:

使用參數化查詢

使用參數來查詢是防範 SQL 注入的有效方法。這樣可以確保用戶輸入不會被直接插入 SQL 查詢:

  1. const mysql = require("mysql");
  2. const db = mysql.createConnection({
  3.     host: "[HOST]",
  4.     user: "[ACCOUNT]",
  5.     password: "[PASSWORD]",
  6.     database: "[DATABASE]"
  7. });
  8. app.get("/user/:id", async (req, res) => {
  9.     const id = req.params.id;
  10.     db.query(`
  11.     SELECT * FROM users 
  12.     WHERE id = ?`, [
  13.         id
  14.     ], (err, r) => {
  15.         // 處理結果
  16.     });
  17. });

避免動態拼接 SQL

不要使用動態拼接 SQL 字符串,這可能會讓應用程式容易受到注入攻擊。而是使用預處理語句或 ORM (物件-關聯映射)來建構查詢。

預備語句

使用參數化查詢範例中,?是預處理語句的參數占位符,而不會將id直接插入 SQL 查詢字符串。

使用 ORM (物件-關聯映射)

ORM 是一種將應用程式中的物件與資料庫中的表格進行映射的技術。這樣的映射允許你使用物件導向的方式來操作資料庫,而不需要直接處理 SQL 查詢。

使用 ORM 可以大大減少動態拼接 SQL 字符串的風險,因為 ORM 通常會處理與資料庫的交互。以下是使用 Sequelize (一個 Node.js 中的 ORM) 的簡單例子:

  1. const Sequelize = require("sequelize");
  2. const sequelize = new Sequelize(
  3.     "[DATABASE]", 
  4.     "[ACCOUNT]", 
  5.     "[password]", {
  6.         host: "[HOST]",
  7.         dialect: "mysql"
  8.     }
  9. );
  10. // 定義 User 模型
  11. const User = sequelize.define("user", {
  12.     id: {
  13.         type: Sequelize.INTEGER,
  14.         primaryKey: true,
  15.         autoIncrement: true
  16.     },
  17.     name: Sequelize.STRING,
  18. });
  19. app.get("/user/:id", async (req, res) => {
  20.     const id = req.params.id;
  21.     try {
  22.         const user = await User.findByPk(id);
  23.         res.json(user);
  24.     } catch (err) {
  25.         res.status(500).json({ error: err });
  26.     }
  27. });

在這個例子中,User 模型代表了資料庫中的users表格,而不需要直接使用 SQL 查詢。


防範跨站腳本攻擊 (XSS)

跨站腳本攻擊是另一種威脅,攻擊者可能通過在網頁上插入惡意腳本,竊取用戶信息。以下是更詳細的 XSS 防護技巧:

輸出轉義

輸出轉義是一種機制,它將用戶輸入的特殊字元轉換為其對應的 HTML 實體,從而防止這些字元被解釋為 HTML 或 JavaScript 代碼。這樣可以防止潛在的 XSS 攻擊。

在使用模板引擎時,通常會自動處理輸出轉義。模板引擎會將用戶輸入的字元轉換為安全的 HTML 實體,確保其不會執行任何惡意的腳本。

Content Security Policy (CSP)

雖然輸出轉義是一個有效的防禦手段,但還有其他進階的安全機制可以增加對抗 XSS 攻擊的強度。其中之一是 Content Security Policy (CSP)。

CSP 是一個 HTTP 標頭,它可以告訴瀏覽器僅執行特定來源的資源,從而有效地防止不信任的腳本的執行。在 Express 中,你可以添加 CSP 標頭:

  1. app.use((req, res, next) => {
  2.     res.setHeader("Content-Security-Policy", "script-src \'self\'");
  3.     next();
  4. });

上方範例中,script-src \'self\'表示只允許執行來自相同來源的腳本。這有助於防止執行來自其他來源的惡意腳本。


系列文章


相關連結