錯誤安全
規則
- 絕不在生產環境中將堆疊追蹤、資料庫錯誤或檔案路徑洩漏給客戶端
- 使用唯一的錯誤 ID 回傳通用錯誤訊息:
{ error: "Something went wrong", errorId: "abc123" }
- 在伺服器端使用相同的關聯 ID 記錄完整的錯誤細節,以便除錯
- 使用不同的錯誤詳細程度:開發環境(完整堆疊追蹤) vs 生產環境(安全訊息 + ID)
- 使用全域錯誤處理器捕獲所有未處理的錯誤 — 絕不讓原始錯誤到達客戶端
- 在轉發給客戶端之前,對來自第三方服務的錯誤訊息進行清理
- 回傳適當的 HTTP 狀態碼:400(錯誤輸入)、401(未驗證)、403(禁止)、404(找不到)、500(伺服器錯誤)
import { randomUUID } from "node:crypto";
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
const errorId = randomUUID();
console.error({ errorId, message: err.message, stack: err.stack, url: req.url });
const statusCode = (err as any).statusCode || 500;
res.status(statusCode).json({
error: statusCode >= 500 ? "Internal server error" : err.message,
errorId,
...(process.env.NODE_ENV === "development" && { stack: err.stack }),
});
});
export default function GlobalError({ error, reset }) {
return (
<div>
<h2>Something went wrong</h2>
<p>Error ID: {error.digest}</p>
<button onClick={reset}>Try again</button>
</div>
);
}
避免
- 直接回傳
err.message — 資料庫錯誤會洩漏資料表名稱、欄位名稱和查詢結構
- 在生產環境回應中包含堆疊追蹤 — 它們會洩漏檔案路徑、依賴項和內部架構
- 對所有情況都使用通用的 500 錯誤 — 應使用適當的狀態碼,以便客戶端能正確處理錯誤
- 在沒有關聯 ID 的情況下記錄錯誤 — 這會讓生產環境的除錯幾乎不可能進行
- 靜默地吞沒錯誤 — 即使回應是通用的,也務必記錄錯誤