適用對象:資訊安全初學者、想了解網站後端運作原理的技術人員
閱讀時間:約 25 分鐘
前言:為什麼資安人員需要懂後端開發?
在資訊安全的領域中,「攻擊」與「防禦」是一體兩面。如果你不了解一個網站是怎麼被建造出來的,就很難理解它可能在哪裡被攻破。後端程式碼是網站的心臟——它處理使用者的請求、連接資料庫、管理身份驗證,也是絕大多數漏洞的發生地。
這篇文章將帶你走過一個網站後端從無到有的完整流程,在每一個開發階段嵌入資安思維,讓你不只學會「怎麼蓋」,更學會「怎麼蓋得安全」。
第一章:網站後端的基本架構
1.1 什麼是後端?
當你在瀏覽器輸入網址、按下登入按鈕、或送出一筆訂單,這些操作會發送一個「請求(Request)」到伺服器。後端就是負責接收這些請求、處理商業邏輯、存取資料庫,最後回傳「回應(Response)」的那一整套系統。
一個典型的後端系統包含以下核心元件:
Web 伺服器(Web Server):接收 HTTP/HTTPS 請求的第一道關卡,常見的有 Nginx 和 Apache。它負責處理靜態檔案、SSL 終止(SSL Termination)、反向代理(Reverse Proxy),以及將動態請求轉發到應用伺服器。
應用伺服器(Application Server):執行你撰寫的商業邏輯程式碼。根據你選擇的語言和框架不同,可能是 Node.js 的 Express、Python 的 Django/Flask、Java 的 Spring Boot、Go 的 Gin,或是 PHP 的 Laravel。
資料庫(Database):持久化儲存資料的地方。關聯式資料庫如 PostgreSQL 和 MySQL 適合結構化數據;NoSQL 資料庫如 MongoDB 和 Redis 適合彈性結構或快取場景。
快取層(Cache Layer):Redis 或 Memcached,用來暫存頻繁存取的資料,減少資料庫壓力、加速回應速度。
訊息佇列(Message Queue):RabbitMQ 或 Kafka,用於非同步處理任務,例如寄送 Email、產生報表等不需要即時回應的工作。
1.2 一個請求的完整旅程
理解一個 HTTP 請求從發出到收到回應的完整路徑,是資安分析的基礎。以下是一個使用者登入操作的典型流程:
- 使用者在瀏覽器填入帳密,點擊登入,瀏覽器發送
POST /api/login請求。 - 請求經過 DNS 解析後抵達伺服器的 Nginx。
- Nginx 透過反向代理將請求轉發至應用伺服器(例如 Express)。
- 應用伺服器的路由模組將請求導向
loginController。 - Controller 呼叫 Service 層,Service 層使用 ORM 向資料庫查詢使用者資料。
- 比對密碼雜湊值(Password Hash),驗證成功後產生 JWT Token。
- 回應帶著 Token 沿路返回瀏覽器,瀏覽器將 Token 存入記憶體或 Cookie。
在這個流程中,每一個環節都可能存在資安風險——從 DNS 劫持、中間人攻擊、SQL Injection、到 Token 洩漏,理解流程才能準確定位威脅。
第二章:建立網站的完整開發流程
2.1 階段一:需求分析與威脅建模
在寫任何一行程式碼之前,專業的開發流程會從需求分析開始。對資安人員而言,這個階段最重要的工作是「威脅建模(Threat Modeling)」。
STRIDE 模型 是微軟提出的經典威脅分類框架:
- Spoofing(偽冒):攻擊者能否假冒其他使用者?
- Tampering(竄改):傳輸中的資料能否被修改?
- Repudiation(否認):使用者能否否認自己做過的操作?
- Information Disclosure(資訊洩漏):敏感資料是否可能被未授權存取?
- Denial of Service(阻斷服務):系統能否被癱瘓?
- Elevation of Privilege(權限提升):一般使用者能否取得管理員權限?
在需求分析階段就進行威脅建模,能讓安全機制融入設計而非事後修補。
2.2 階段二:技術選型
技術選型直接影響後續的安全特性和維護成本。以下是主流後端框架的安全特性比較:
Node.js + Express:生態豐富、開發速度快,但安全功能需自行整合。需搭配 Helmet(HTTP 安全標頭)、express-rate-limit(速率限制)、csurf(CSRF 防護)等中介軟體。適合快速原型和 API 開發。
Python + Django:內建安全功能最完善的框架之一。CSRF 保護、SQL Injection 防禦、XSS 過濾、Clickjacking 防護、密碼雜湊等全部開箱即用。ORM 預設使用參數化查詢。適合需要強安全性的企業應用。
Java + Spring Boot:搭配 Spring Security 能實現企業級身份驗證與授權。支援 OAuth 2.0、LDAP、SAML 等多種認證協定。適合大型企業系統。
Go + Gin:編譯式語言,記憶體安全性較高,效能優秀。但安全功能需自行建構,適合微服務架構。
PHP + Laravel:內建 CSRF Token、Eloquent ORM 參數化查詢、Bcrypt 密碼雜湊。中小型專案的常見選擇。
選型時的資安考量重點:框架是否積極維護、安全漏洞的修補速度、社群生態的安全套件是否成熟、預設設定是否「安全優先(Secure by Default)」。
2.3 階段三:環境建置
現代開發環境建置通常包含以下工具鏈:
版本控制:Git + GitHub/GitLab。資安重點是確保 .gitignore 排除所有機密檔案,永遠不要把 API 金鑰、資料庫密碼、私鑰推送到版本庫。使用工具如 git-secrets 或 truffleHog 掃描意外提交的敏感資訊。
容器化:Docker + Docker Compose。將應用及其依賴打包成獨立容器,確保開發、測試、生產環境一致。資安重點是使用最小化基礎映像(如 Alpine Linux)、不以 root 身份執行容器、定期掃描映像漏洞。
環境變數管理:使用 .env 檔案管理機密資訊,搭配工具如 dotenv 載入。進階方案使用 HashiCorp Vault 或 AWS Secrets Manager 集中管理密鑰。
以下是一個安全的 Docker Compose 開發環境範例:
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DB_HOST=db
- DB_PORT=5432
env_file:
- .env # 機密資訊放這裡,不進版本控制
depends_on:
- db
- redis
networks:
- backend
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
networks:
- backend
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
networks:
- backend
networks:
backend:
driver: bridge
volumes:
pgdata:
secrets:
db_password:
file: ./secrets/db_password.txt
2.4 階段四:程式碼開發
這是最核心的階段,也是漏洞最容易產生的地方。以下用 Node.js + Express 為例,展示一個帶有安全考量的基礎架構:
// app.js - 應用程式進入點
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const cors = require('cors');
const hpp = require('hpp');
const mongoSanitize = require('express-mongo-sanitize');
const app = express();
// === 安全性中介軟體 ===
// Helmet:自動設定多個安全相關的 HTTP 標頭
// 包含 X-Content-Type-Options, X-Frame-Options,
// Content-Security-Policy 等
app.use(helmet());
// CORS:限制哪些來源可以存取你的 API
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://yourdomain.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
maxAge: 86400 // 預檢請求快取 24 小時
}));
// 速率限制:防止暴力破解和 DDoS
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 分鐘
max: 100, // 每個 IP 限制 100 次請求
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many requests, please try again later.' }
});
app.use('/api/', limiter);
// 針對登入端點設定更嚴格的速率限制
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 15 分鐘內只能嘗試 5 次登入
message: { error: 'Too many login attempts.' }
});
app.use('/api/auth/login', loginLimiter);
// 解析 JSON 請求主體,限制大小防止 payload 攻擊
app.use(express.json({ limit: '10kb' }));
// 防止 HTTP 參數污染攻擊
app.use(hpp());
// 防止 NoSQL 注入(過濾 gt,ne 等運算子)
app.use(mongoSanitize());
// === 路由 ===
app.use('/api/auth', require('./routes/auth'));
app.use('/api/users', require('./routes/users'));
app.use('/api/posts', require('./routes/posts'));
// === 全域錯誤處理 ===
// 重點:永遠不要在錯誤回應中洩漏系統內部資訊
app.use((err, req, res, next) => {
console.error(err.stack); // 內部記錄完整錯誤
// 對外只回傳安全的錯誤訊息
res.status(err.statusCode || 500).json({
status: 'error',
message: process.env.NODE_ENV === 'production'
? 'Something went wrong'
: err.message // 開發環境才顯示詳細錯誤
});
});
module.exports = app;
2.5 階段五:測試
安全測試應該是開發流程的固定環節,而非部署前的臨時檢查。
單元測試(Unit Test):測試個別函式的邏輯正確性。使用 Jest、Mocha、pytest 等框架。
整合測試(Integration Test):測試多個模組協同運作,例如 API 端點是否正確存取資料庫。
安全測試(Security Test):使用自動化工具掃描已知漏洞,包含靜態應用安全測試(SAST,如 SonarQube、Semgrep)和動態應用安全測試(DAST,如 OWASP ZAP)。
依賴漏洞掃描:使用 npm audit、pip audit、Snyk 或 Dependabot 定期檢查第三方套件的已知漏洞。
2.6 階段六:CI/CD 與部署
持續整合/持續部署(CI/CD)流程中應嵌入安全檢查:
# .github/workflows/security.yml
name: Security Pipeline
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# 檢查是否有意外提交的密鑰
- name: Secret Scanning
uses: trufflesecurity/trufflehog@main
# 依賴漏洞檢查
- name: Dependency Audit
run: npm audit --audit-level=high
# 靜態程式碼分析
- name: SAST Scan
uses: returntocorp/semgrep-action@v1
with:
config: p/owasp-top-ten
# Docker 映像漏洞掃描
- name: Container Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
severity: 'CRITICAL,HIGH'
第三章:框架的使用與 MVC 架構模式
3.1 MVC 架構
絕大多數後端框架採用 MVC(Model-View-Controller)或其變體作為基本架構模式:
Model(模型):定義資料結構和資料庫互動邏輯。使用 ORM(Object-Relational Mapping)工具如 Sequelize、TypeORM、Django ORM、SQLAlchemy 等,避免直接撰寫 SQL 語句,從根本上防止 SQL Injection。
View(視圖):在純 API 後端中,View 通常就是 JSON 回應格式的定義。在全端框架中則是 HTML 模板引擎(如 EJS、Pug、Jinja2)。
Controller(控制器):接收請求、呼叫 Service/Model 處理邏輯、回傳回應。Controller 應盡量保持「瘦」,把商業邏輯委派給 Service 層。
3.2 安全的專案結構範例
project/
├── src/
│ ├── config/ # 環境設定(資料庫連線、JWT 密鑰等)
│ ├── middleware/ # 自定義中介軟體
│ │ ├── auth.js # JWT 驗證
│ │ ├── validate.js # 輸入驗證
│ │ ├── authorize.js # 角色授權(RBAC)
│ │ └── sanitize.js # 輸入消毒
│ ├── models/ # 資料模型定義
│ ├── controllers/ # 請求處理邏輯
│ ├── services/ # 商業邏輯層
│ ├── routes/ # 路由定義
│ ├── utils/ # 工具函式
│ │ ├── logger.js # 安全日誌
│ │ ├── crypto.js # 加密工具
│ │ └── validator.js # 驗證規則
│ └── app.js # 應用程式進入點
├── tests/
│ ├── unit/
│ ├── integration/
│ └── security/
├── .env.example # 環境變數範本(不含實際值)
├── .gitignore
├── docker-compose.yml
└── package.json
3.3 中介軟體鏈(Middleware Chain)
中介軟體是後端框架的核心概念。每個請求進入應用後,會依序經過一連串的中介軟體處理。理解這條鏈的順序對資安至關重要:
請求進入
↓
[1] Helmet(安全標頭)
↓
[2] CORS(跨來源控制)
↓
[3] Rate Limiter(速率限制)
↓
[4] Body Parser(解析請求主體)
↓
[5] Input Sanitization(輸入消毒)
↓
[6] Authentication(身份驗證)
↓
[7] Authorization(權限檢查)
↓
[8] Input Validation(輸入驗證)
↓
[9] Controller / Business Logic
↓
[10] Error Handler(錯誤處理)
↓
回應送出
順序很重要——你應該在驗證身份之前就擋掉超過速率限制的請求,在進入商業邏輯之前就完成輸入消毒和驗證。
第四章:資安必備的網站架構元件
4.1 身份驗證(Authentication)
身份驗證回答的問題是「你是誰?」。以下是安全的密碼處理和 JWT 實作:
// services/auth.service.js
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
class AuthService {
// 安全的密碼雜湊
async hashPassword(password) {
// bcrypt 的 salt rounds 建議至少 12
// 每增加 1,計算時間約翻倍
const saltRounds = 12;
return await bcrypt.hash(password, saltRounds);
}
// 密碼驗證(使用 timing-safe comparison)
async verifyPassword(inputPassword, storedHash) {
return await bcrypt.compare(inputPassword, storedHash);
}
// 產生 JWT Token(Access Token + Refresh Token)
generateTokens(userId) {
const accessToken = jwt.sign(
{ sub: userId, type: 'access' },
process.env.JWT_ACCESS_SECRET,
{ expiresIn: '15m' } // Access Token 壽命短
);
const refreshToken = jwt.sign(
{ sub: userId, type: 'refresh' },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' } // Refresh Token 壽命長
);
return { accessToken, refreshToken };
}
// 驗證 JWT Token
verifyAccessToken(token) {
try {
const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET);
if (decoded.type !== 'access') {
throw new Error('Invalid token type');
}
return decoded;
} catch (error) {
throw new Error('Invalid or expired token');
}
}
}
module.exports = new AuthService();
密碼政策建議:最小長度 8 至 12 字元、支援全 Unicode 字元集、檢查是否為已洩漏的密碼(使用 Have I Been Pwned API)、不要強制定期更換密碼(NIST SP 800-63B 建議)。
4.2 授權(Authorization)與 RBAC
授權回答的問題是「你能做什麼?」。角色存取控制(Role-Based Access Control)是最常見的授權模型:
// middleware/authorize.js
const authorize = (...allowedRoles) => {
return (req, res, next) => {
// 前提:auth middleware 已經把使用者資訊放到 req.user
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
if (!allowedRoles.includes(req.user.role)) {
// 重要:403 vs 401 的差異
// 401 = 未驗證身份
// 403 = 已驗證但無權限
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
};
// 使用範例
router.get('/admin/users',
authenticate, // 先驗證身份
authorize('admin', 'superadmin'),// 再檢查權限
userController.getAllUsers // 最後執行邏輯
);
router.delete('/admin/users/:id',
authenticate,
authorize('superadmin'), // 只有超級管理員能刪除使用者
userController.deleteUser
);
4.3 輸入驗證與消毒(Input Validation & Sanitization)
永遠不要信任使用者輸入——這是後端開發最重要的安全原則。
// middleware/validate.js
const { body, param, query, validationResult } = require('express-validator');
const xss = require('xss');
// 自定義消毒函式
const sanitizeInput = (value) => {
if (typeof value !== 'string') return value;
return xss(value.trim());
};
// 註冊表單的驗證規則
const registerValidation = [
body('email')
.isEmail().withMessage('Please provide a valid email')
.normalizeEmail() // 標準化 email 格式
.customSanitizer(sanitizeInput), // XSS 消毒
body('password')
.isLength({ min: 8, max: 128 })
.withMessage('Password must be 8-128 characters')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
.withMessage('Must contain uppercase, lowercase, and number'),
body('username')
.isLength({ min: 3, max: 30 })
.matches(/^[a-zA-Z0-9_]+$/) // 只允許字母、數字、底線
.withMessage('Username can only contain letters, numbers, and underscores')
.customSanitizer(sanitizeInput),
// 統一的驗證結果處理
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
status: 'fail',
errors: errors.array().map(e => ({
field: e.path,
message: e.msg
// 注意:不要回傳使用者的輸入值,防止反射型 XSS
}))
});
}
next();
}
];
module.exports = { registerValidation };
4.4 SQL Injection 防禦
SQL Injection 長年高居 OWASP Top 10,防禦的核心是「參數化查詢」:
// ❌ 危險:字串拼接 SQL(永遠不要這樣做)
const query = `SELECT * FROM users WHERE email = '{userInput}'`;
// 攻擊者輸入: ' OR '1'='1' --
// 結果: SELECT * FROM users WHERE email = '' OR '1'='1' --'
// 整個使用者表被洩漏
// ✅ 安全:參數化查詢
const query = 'SELECT * FROM users WHERE email =1';
const result = await pool.query(query, [userInput]);
// ✅ 更安全:使用 ORM
// Sequelize 範例
const user = await User.findOne({
where: { email: userInput } // ORM 自動處理參數化
});
// Prisma 範例
const user = await prisma.user.findUnique({
where: { email: userInput }
});
4.5 跨站腳本攻擊(XSS)防禦
// Content Security Policy(CSP)是防禦 XSS 的強力工具
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"], // 預設只允許同源資源
scriptSrc: ["'self'"], // JavaScript 只能從同源載入
styleSrc: ["'self'", "'unsafe-inline'"], // 樣式允許行內(視需求調整)
imgSrc: ["'self'", "data:", "https:"], // 圖片來源
connectSrc: ["'self'"], // API 請求目標
fontSrc: ["'self'"],
objectSrc: ["'none'"], // 禁止 Flash 等外掛
frameSrc: ["'none'"], // 禁止 iframe 嵌入
baseUri: ["'self'"], // 限制 <base> 標籤
formAction: ["'self'"], // 表單只能提交到同源
}
}));
// 設定 Cookie 安全屬性
const sessionConfig = {
httpOnly: true, // JavaScript 無法存取此 Cookie
secure: true, // 只透過 HTTPS 傳送
sameSite: 'Strict', // 防止 CSRF
maxAge: 3600000, // 1 小時過期
path: '/',
domain: '.yourdomain.com'
};
4.6 安全的 HTTP 標頭
// 除了 Helmet 自動設定的標頭,還應手動加入:
// Strict-Transport-Security:強制使用 HTTPS
app.use(helmet.hsts({
maxAge: 31536000, // 1 年
includeSubDomains: true, // 包含所有子網域
preload: true // 加入瀏覽器預載清單
}));
// X-Content-Type-Options:防止 MIME 嗅探
// X-Frame-Options:防止 Clickjacking
// X-XSS-Protection:啟用瀏覽器 XSS 過濾器
// 以上三者 Helmet 預設已設定
// Referrer-Policy:控制 Referer 標頭資訊洩漏
app.use(helmet.referrerPolicy({
policy: 'strict-origin-when-cross-origin'
}));
// Permissions-Policy:限制瀏覽器功能存取
app.use((req, res, next) => {
res.setHeader('Permissions-Policy',
'camera=(), microphone=(), geolocation=(), payment=(self)');
next();
});
4.7 安全的日誌系統
日誌是資安事件調查的關鍵,但不當的日誌記錄本身也可能造成資訊洩漏:
// utils/logger.js
const winston = require('winston');
// 敏感資訊過濾器
const sensitiveFields = ['password', 'token', 'authorization',
'cookie', 'credit_card', 'ssn', 'secret'];
const maskSensitiveData = winston.format((info) => {
if (typeof info.message === 'string') {
sensitiveFields.forEach(field => {
const regex = new RegExp(`"{field}"\\s*:\\s*"[^"]*"`, 'gi');
info.message = info.message.replace(regex, `"{field}":"[REDACTED]"`);
});
}
return info;
});
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
maskSensitiveData(),
winston.format.json()
),
transports: [
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 10
}),
new winston.transports.File({
filename: 'logs/combined.log',
maxsize: 5242880,
maxFiles: 30
})
]
});
// 安全事件專用日誌
logger.security = (event, details) => {
logger.warn({
type: 'SECURITY_EVENT',
event,
...details,
timestamp: new Date().toISOString()
});
};
// 使用範例
logger.security('LOGIN_FAILED', {
ip: req.ip,
email: req.body.email, // 記錄嘗試的帳號(不記錄密碼)
userAgent: req.headers['user-agent'],
reason: 'Invalid credentials'
});
module.exports = logger;
4.8 HTTPS 與 TLS 設定
所有正式環境的網站都必須使用 HTTPS。以下是 Nginx 的安全 TLS 設定:
server {
listen 443 ssl http2;
server_name yourdomain.com;
# TLS 憑證(建議使用 Let's Encrypt 免費憑證)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# 只允許安全的 TLS 版本
ssl_protocols TLSv1.2 TLSv1.3;
# 優先使用伺服器端的加密套件
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
# OCSP Stapling(加速 TLS 握手)
ssl_stapling on;
ssl_stapling_verify on;
# 反向代理設定
location /api/ {
proxy_pass http://app:3000;
proxy_set_header Host host;
proxy_set_header X-Real-IPremote_addr;
proxy_set_header X-Forwarded-For proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Protoscheme;
# 隱藏後端伺服器資訊
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
}
}
# HTTP 自動重導向到 HTTPS
server {
listen 80;
server_name yourdomain.com;
return 301 https://server_namerequest_uri;
}
4.9 資料庫安全
// config/database.js
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
// 連線安全設定
ssl: {
rejectUnauthorized: true, // 驗證伺服器憑證
ca: fs.readFileSync('/path/to/ca-cert.pem')
},
// 連線池設定(防止連線耗盡攻擊)
max: 20, // 最大連線數
idleTimeoutMillis: 30000, // 閒置連線超時
connectionTimeoutMillis: 5000, // 連線建立超時
});
module.exports = pool;
資料庫安全清單:使用最小權限原則(應用程式帳號不應有 DROP、ALTER 權限)、啟用連線加密(SSL/TLS)、定期備份並測試還原流程、啟用查詢日誌和慢查詢監控、敏感欄位加密儲存(如信用卡號)。
4.10 API 安全設計
// 安全的 API 版本管理
app.use('/api/v1', v1Router); // 版本化路由
// API Key 驗證(用於服務間通訊)
const validateApiKey = (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
// 使用 timing-safe comparison 防止計時攻擊
const validKey = Buffer.from(process.env.API_KEY);
const providedKey = Buffer.from(apiKey);
if (validKey.length !== providedKey.length ||
!crypto.timingSafeEqual(validKey, providedKey)) {
return res.status(401).json({ error: 'Invalid API key' });
}
next();
};
// 請求大小限制
app.use(express.json({ limit: '10kb' })); // JSON 主體
app.use(express.urlencoded({ limit: '10kb', extended: true }));
// 檔案上傳安全
const multer = require('multer');
const upload = multer({
limits: {
fileSize: 5 * 1024 * 1024, // 5MB 上限
files: 1 // 單次只允許 1 個檔案
},
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (!allowedTypes.includes(file.mimetype)) {
return cb(new Error('Invalid file type'), false);
}
cb(null, true);
}
});
第五章:完整安全架構總覽
5.1 縱深防禦(Defense in Depth)
安全的網站架構不依賴單一防線,而是像洋蔥一樣層層包裹。從外到內的完整防禦層次如下:
┌─────────────────────────────┐
│ CDN / DDoS Protection │ ← Cloudflare / AWS Shield
│ (第一層:網路邊緣防護) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ WAF (Web Application │ ← ModSecurity / AWS WAF
│ Firewall) │
│ (第二層:應用層防火牆) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Load Balancer + SSL │ ← Nginx / HAProxy
│ Termination │
│ (第三層:負載均衡與加密終止) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Application Server │ ← Express / Django / Spring
│ + Security Middleware │
│ (第四層:應用邏輯與中介軟體) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Database + Encryption │ ← PostgreSQL + AES-256
│ (第五層:資料儲存與加密) │
└─────────────────────────────┘
5.2 OWASP Top 10 對照檢查表
以下對照 OWASP 2021 Top 10 風險,列出本文涵蓋的防禦措施:
| 排名 | 風險類別 | 本文對應防禦 |
|---|---|---|
| A01 | Broken Access Control | RBAC 授權中介軟體(4.2 節) |
| A02 | Cryptographic Failures | bcrypt 密碼雜湊、TLS 設定(4.1、4.8 節) |
| A03 | Injection | 參數化查詢、ORM、輸入消毒(4.3、4.4 節) |
| A04 | Insecure Design | 威脅建模、STRIDE(2.1 節) |
| A05 | Security Misconfiguration | Helmet 安全標頭、最小權限原則(4.5、4.6 節) |
| A06 | Vulnerable Components | npm audit、Dependabot(2.5 節) |
| A07 | Auth Failures | JWT 雙 Token 機制、速率限制(4.1 節) |
| A08 | Data Integrity Failures | CI/CD 安全管線(2.6 節) |
| A09 | Logging Failures | Winston 安全日誌、敏感資料遮蔽(4.7 節) |
| A10 | SSRF | 輸入驗證、白名單控制(4.3、4.10 節) |
第六章:部署與維運安全
6.1 生產環境安全清單
在按下部署按鈕之前,請逐項確認:
伺服器層面:作業系統已更新至最新安全補丁、關閉所有不必要的端口(只開放 80、443)、SSH 禁用密碼登入且僅允許金鑰認證、設定防火牆規則(iptables / ufw)、啟用自動安全更新。
應用層面:NODE_ENV=production(或對應的生產模式設定)、移除所有偵錯端點和測試帳號、錯誤訊息不洩漏堆疊追蹤、所有密鑰使用環境變數或密鑰管理服務、Content Security Policy 已正確設定。
資料庫層面:預設管理員密碼已更改、遠端存取已限制為特定 IP、備份已設定且定期測試還原、啟用慢查詢日誌。
監控層面:設定錯誤率告警(Error Rate Alert)、設定異常流量告警、設定伺服器資源使用率告警、日誌集中收集(ELK Stack / Grafana Loki)。
6.2 事件回應計畫
即使做了所有防護,安全事件仍然可能發生。預先準備事件回應計畫(Incident Response Plan)至關重要:定義事件嚴重等級分類標準、建立通報流程與負責人清單、準備好隔離受影響系統的操作手冊、定期演練(Tabletop Exercise)以驗證流程可行性。
結語
後端安全不是一個功能(Feature),而是一種思維方式(Mindset)。它應該滲透在開發流程的每一個環節——從需求分析時的威脅建模,到撰寫程式碼時的輸入驗證,再到部署後的持續監控。
給資安新手的建議是:不要試圖一次學會所有東西。先從理解 OWASP Top 10 開始,然後挑選一個框架動手建一個小專案,在實作中逐步加入安全機制。每修復一個漏洞、每攔截一次攻擊,你對後端安全的理解都會更深一層。
安全是一場持續的旅程,而非終點。
