構(gòu)建 Next.js 應(yīng)用時(shí)的安全保障與風(fēng)險(xiǎn)防范措施
在 Web 應(yīng)用開(kāi)發(fā)過(guò)程中,確保應(yīng)用的安全性至關(guān)重要,這不僅能保護(hù)用戶數(shù)據(jù),還能防止應(yīng)用本身遭受各種安全攻擊。Next.js 作為一款備受歡迎的 React 框架,內(nèi)置了許多安全功能和推薦做法,但開(kāi)發(fā)者仍需清楚地了解潛在的安全隱患,并采取合適的防范策略。
一、Next.js 安全問(wèn)題概述
盡管 Next.js 為構(gòu)建安全應(yīng)用提供了堅(jiān)實(shí)的基礎(chǔ),但在開(kāi)發(fā)過(guò)程中仍有許多需要注意的安全問(wèn)題和最佳實(shí)踐,確保應(yīng)用對(duì)常見(jiàn)漏洞具有足夠的防御能力。
二、服務(wù)器端渲染(SSR)與靜態(tài)站點(diǎn)生成(SSG)的安全策略
2.1 SSR 安全要點(diǎn)
在使用服務(wù)器端渲染時(shí),有幾項(xiàng)關(guān)鍵措施需要注意:
- 數(shù)據(jù)凈化:始終對(duì)用戶輸入進(jìn)行清洗,防止注入類攻擊。由于 SSR 同時(shí)面臨服務(wù)器端和客戶端的注入風(fēng)險(xiǎn),因此建議使用諸如 DOMPurify 之類的工具。例如:
import DOMPurify from "dompurify";
const sanitizedData = DOMPurify.sanitize(userInput);
- 敏感數(shù)據(jù)保護(hù):確保在服務(wù)器渲染生成的 HTML 中不暴露敏感信息,如 API 密鑰或用戶詳細(xì)信息。只將必要的數(shù)據(jù)發(fā)送到客戶端:
export async function getServerSideProps(context) {
const data = await fetchData();
return {
props: {
data: filterSensitiveData(data),
},
};
}
- 防止跨站腳本攻擊(XSS):因?yàn)榉?wù)器生成 HTML 時(shí)需要處理動(dòng)態(tài)內(nèi)容,所以一定要確保所有內(nèi)容都經(jīng)過(guò)轉(zhuǎn)義。例如:
const sanitizedData = escapeHTML(userInput);
function escapeHTML(str) {
return str.replace(/[&<>"']/g, function (match) {
return {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
}[match];
});
}
- 安全的 API 調(diào)用:利用服務(wù)器端環(huán)境變量存放 API 密鑰等敏感信息,避免泄露給客戶端代碼:
const apiKey = process.env.API_KEY;
const response = await fetch(`https://api.example.com/data?apiKey=${apiKey}`);
- CSRF 防護(hù):通過(guò) CSRF 令牌保護(hù) SSR 路由免受跨站請(qǐng)求偽造攻擊。例如:
import csrf from "csurf";
const csrfProtection = csrf({ cookie: true });
export default function handler(req, res) {
csrfProtection(req, res, () => {
// 此處放置 API 邏輯
});
}
2.2 SSG 安全注意事項(xiàng)
在靜態(tài)站點(diǎn)生成時(shí),以下措施尤為重要:
- 數(shù)據(jù)凈化:在構(gòu)建時(shí)對(duì)所有用戶生成的內(nèi)容進(jìn)行清洗,確保嵌入靜態(tài) HTML 的數(shù)據(jù)是安全的。
import DOMPurify from "dompurify";
export async function getStaticProps() {
const data = await fetchData();
const sanitizedData = DOMPurify.sanitize(data);
return {
props: {
sanitizedData,
},
};
}
- 內(nèi)容安全策略(CSP):為防范 XSS 和其它注入攻擊,必須配置一套嚴(yán)格的 CSP 策略。這對(duì)于緩存并直接提供靜態(tài)內(nèi)容的站點(diǎn)尤為關(guān)鍵:
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy",
value: "default-src 'self'; script-src 'self' 'unsafe-inline';",
},
],
},
];
},
};
- 靜態(tài)文件的安全管理:確保敏感文件不會(huì)對(duì)外公開(kāi),通過(guò)重寫或重定向來(lái)控制文件訪問(wèn):
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/private-file",
destination: "/404",
permanent: false,
},
];
},
};
- 環(huán)境變量管理:確保環(huán)境變量不會(huì)暴露給客戶端,通過(guò) getStaticProps 或 getStaticPaths 安全地在構(gòu)建過(guò)程中加載數(shù)據(jù)。
export async function getStaticProps() {
const apiKey = process.env.API_KEY;
const data = await fetchData(apiKey);
return {
props: {
data,
},
};
}
- 不可變與可緩存內(nèi)容:保證靜態(tài)資源和生成頁(yè)面具有不可變性和良好的緩存策略,防止內(nèi)容篡改。
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Cache-Control",
value: "public, max-age=31536000, immutable",
},
],
},
];
},
};
三、常規(guī)安全最佳實(shí)踐
- 設(shè)置安全頭部:利用 Helmet.js 等庫(kù)來(lái)添加 HTTP 安全頭部,增強(qiáng)安全防護(hù)。
import helmet from "helmet";
export default function handler(req, res) {
helmet()(req, res, () => {
// 此處處理 API 請(qǐng)求
});
}
- 依賴管理:定期更新第三方依賴以修補(bǔ)已知安全漏洞。
npm outdated
npm update
- 漏洞審計(jì):使用 npm audit 等工具掃描依賴,及時(shí)發(fā)現(xiàn)和修復(fù)安全問(wèn)題。
npm audit
四、跨站腳本攻擊(XSS)的防范措施
XSS 攻擊允許攻擊者在其他用戶的頁(yè)面中注入惡意腳本,Next.js 通過(guò)以下措施來(lái)降低這種風(fēng)險(xiǎn):
- JSX 自動(dòng)轉(zhuǎn)義:React 與 Next.js 默認(rèn)會(huì)將動(dòng)態(tài)數(shù)據(jù)當(dāng)作純文本處理,從而自動(dòng)防范 XSS。
const message = "<script>alert('XSS');</script>";
return <div>{message}</div>; // 最終輸出為 <script>alert('XSS');</script>
- 謹(jǐn)慎使用 dangerouslySetInnerHTML:在必須直接注入 HTML 的情況下,務(wù)必先進(jìn)行數(shù)據(jù)凈化。
import DOMPurify from "dompurify";
const rawHTML = "<p>這是一段<strong>加粗</strong>文本。</p>";
const sanitizedHTML = DOMPurify.sanitize(rawHTML);
return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
- 雙重凈化用戶輸入:無(wú)論在客戶端還是服務(wù)器端,都必須對(duì)用戶輸入進(jìn)行清洗。
import DOMPurify from "dompurify";
const handleUserInput = (input) => {
return DOMPurify.sanitize(input);
};
const userComment = handleUserInput(userInput);
return <div>{userComment}</div>;
- 服務(wù)器端數(shù)據(jù)驗(yàn)證:在 SSR 渲染過(guò)程中,同樣需要驗(yàn)證并凈化傳輸?shù)娇蛻舳说臄?shù)據(jù)。
import DOMPurify from "dompurify";
export async function getServerSideProps(context) {
const data = await fetchData();
const sanitizedData = DOMPurify.sanitize(data);
return {
props: {
data: sanitizedData,
},
};
}
- 配置嚴(yán)格的內(nèi)容安全策略(CSP):通過(guò) CSP 限制腳本加載來(lái)源,從而進(jìn)一步降低 XSS 攻擊風(fēng)險(xiǎn)。
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy",
value: "default-src 'self'; script-src 'self' 'unsafe-inline';",
},
],
},
];
},
};
- 避免客戶端模板注入:確保模板中使用的數(shù)據(jù)已經(jīng)過(guò)安全編碼,避免通過(guò)字符串拼接構(gòu)造 HTML。
const userInput = "<script>alert('XSS');</script>";
return <div>{userInput}</div>; // 自動(dòng)轉(zhuǎn)義后的安全輸出
- 保護(hù) API 接口:對(duì) API 接收到的數(shù)據(jù)進(jìn)行驗(yàn)證和凈化,防止惡意數(shù)據(jù)影響服務(wù)器渲染頁(yè)面。
import DOMPurify from "dompurify";
export default function handler(req, res) {
const sanitizedData = DOMPurify.sanitize(req.body.data);
// 處理凈化后的數(shù)據(jù)
res.status(200).json({ data: sanitizedData });
}
- 使用專門的 XSS 防護(hù)庫(kù):例如 xss、DOMPurify、sanitize-html 等都是常用工具。
import sanitizeHtml from "sanitize-html";
const cleanHtml = sanitizeHtml(dirtyHtml, {
allowedTags: ["b", "i", "em", "strong", "a"],
allowedAttributes: {
a: ["href"],
},
});
return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />;
- 輸入驗(yàn)證與編碼:對(duì)所有輸入進(jìn)行校驗(yàn)和編碼,確保應(yīng)用只呈現(xiàn)安全內(nèi)容。
const validateInput = (input) => {
// 自定義驗(yàn)證邏輯
return input.replace(/<script.*?>.*?<\/script>/gi, "");
};
const safeInput = validateInput(userInput);
return <div>{safeInput}</div>;
- 持續(xù)監(jiān)控與修補(bǔ):定期監(jiān)控應(yīng)用,更新依賴和補(bǔ)丁以防范新發(fā)現(xiàn)的漏洞。
npm audit
npm update
通過(guò)上述措施,你可以有效抵御 XSS 攻擊,保障用戶訪問(wèn)體驗(yàn)和數(shù)據(jù)安全。
五、防范跨站請(qǐng)求偽造(CSRF)攻擊
CSRF 攻擊通過(guò)誘導(dǎo)用戶執(zhí)行非預(yù)期操作來(lái)威脅已認(rèn)證的會(huì)話。為防范此類攻擊,應(yīng)采取如下措施:
- 使用 CSRF 令牌:為每個(gè)請(qǐng)求生成獨(dú)一無(wú)二且不可預(yù)測(cè)的令牌,確保請(qǐng)求來(lái)源合法。
- 配置 CSRF 中間件:例如,可以使用 csurf 和 cookie-parser 庫(kù)來(lái)實(shí)現(xiàn)中間件保護(hù):
// pages/api/csrf.js
import csrf from "csurf";
import cookieParser from "cookie-parser";
import { NextApiRequest, NextApiResponse } from "next";
const csrfProtection = csrf({ cookie: true });
export default function handler(req, res) {
cookieParser()(req, res, () => {
csrfProtection(req, res, () => {
res.status(200).json({ csrfToken: req.csrfToken() });
});
});
}
- 在表單或 API 請(qǐng)求中使用 CSRF 令牌:在前端加載令牌后,將其附加到表單或請(qǐng)求頭中:
import { useEffect, useState } from "react";
export default function MyForm() {
const [csrfToken, setCsrfToken] = useState("");
useEffect(() => {
const fetchCsrfToken = async () => {
const res = await fetch("/api/csrf");
const data = await res.json();
setCsrfToken(data.csrfToken);
};
fetchCsrfToken();
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
const res = await fetch("/api/submit", {
method: "POST",
headers: {
"Content-Type": "application/json",
"CSRF-Token": csrfToken,
},
body: JSON.stringify({ data: "example" }),
});
const result = await res.json();
console.log(result);
};
return (
<form onSubmit={handleSubmit}>
<input type="hidden" name="csrfToken" value={csrfToken} />
<button type="submit">提交</button>
</form>
);
}
- 配置安全 Cookie:確保與 CSRF 防護(hù)相關(guān)的 Cookie 具備 HttpOnly、Secure 以及 SameSite(嚴(yán)格或?qū)捤桑傩浴?/span>
res.setHeader(
"Set-Cookie",
cookie.serialize("token", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
maxAge: 3600,
sameSite: "strict",
path: "/",
}),
);
- 驗(yàn)證請(qǐng)求來(lái)源:通過(guò)檢查 Origin 和 Referrer 頭信息來(lái)確保請(qǐng)求確實(shí)來(lái)自受信任的域名。
function validateRequest(req) {
const origin = req.headers.origin;
const referrer = req.headers.referer;
const allowedOrigins = ["https://yourdomain.com"];
if (
!allowedOrigins.includes(origin) ||
!allowedOrigins.includes(referrer)
) {
throw new Error("無(wú)效的來(lái)源或引用");
}
}
export default function handler(req, res) {
try {
validateRequest(req);
// 處理合法請(qǐng)求
res.status(200).json({ message: "請(qǐng)求合法" });
} catch (error) {
res.status(403).json({ message: "禁止訪問(wèn)" });
}
}
- 保護(hù)敏感路由:確保只有經(jīng)過(guò)認(rèn)證和授權(quán)的用戶才能訪問(wèn)那些涉及數(shù)據(jù)修改或敏感操作的接口。
import { getSession } from "next-auth/client";
export default async function handler(req, res) {
const session = await getSession({ req });
if (!session) {
return res.status(401).json({ message: "未授權(quán)" });
}
// 處理經(jīng)過(guò)授權(quán)的請(qǐng)求
res.status(200).json({ message: "已授權(quán)" });
}
- 在自定義服務(wù)器中應(yīng)用 CSRF 中間件:如果使用自定義服務(wù)器(如 Express),可全局應(yīng)用該中間件:
const express = require("express");
const next = require("next");
const csrf = require("csurf");
const cookieParser = require("cookie-parser");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const csrfProtection = csrf({ cookie: true });
app.prepare().then(() => {
const server = express();
server.use(cookieParser());
server.use(csrfProtection);
server.get("/api/csrf", (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
server.all("*", (req, res) => {
return handle(req, res);
});
server.listen(3000, (err) => {
if (err) throw err;
console.log("> 服務(wù)器已啟動(dòng),訪問(wèn) http://localhost:3000");
});
});
總之,采用 CSRF 令牌、配置安全 Cookie、驗(yàn)證請(qǐng)求來(lái)源以及對(duì)敏感路由進(jìn)行保護(hù),是防范跨站請(qǐng)求偽造攻擊的有效手段。
六、認(rèn)證與授權(quán)
在構(gòu)建安全應(yīng)用時(shí),完善的認(rèn)證和授權(quán)機(jī)制是必不可少的。以下是 Next.js 中實(shí)現(xiàn)認(rèn)證和授權(quán)的一些建議:
6.1 用戶認(rèn)證
用戶認(rèn)證用于驗(yàn)證用戶身份。在 Next.js 中,可以通過(guò) NextAuth.js 等庫(kù)來(lái)實(shí)現(xiàn)。例如:
- 安裝 NextAuth.js:
npm install next-auth
- 配置認(rèn)證:在
pages/api/auth/[...nextauth].js
中設(shè)置認(rèn)證提供商和回調(diào)函數(shù)。
// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth";
import Providers from "next-auth/providers";
export default NextAuth({
providers: [
Providers.Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
// 可添加其他認(rèn)證方式
],
database: process.env.DATABASE_URL,
session: {
jwt: true,
},
callbacks: {
async session(session, token) {
session.user.id = token.id;
return session;
},
async jwt(token, user) {
if (user) {
token.id = user.id;
}
return token;
},
},
});
- 在組件中使用認(rèn)證:通過(guò)檢測(cè)用戶會(huì)話來(lái)保護(hù)頁(yè)面或組件。
import { useSession, signIn, signOut } from "next-auth/client";
export default function MyComponent() {
const [session, loading] = useSession();
if (loading) return <p>加載中...</p>;
if (!session) return <button onClick={() => signIn()}>登錄</button>;
return (
<>
<p>歡迎您, {session.user.name}</p>
<button onClick={() => signOut()}>退出</button>
</>
);
}
6.2 用戶授權(quán)
授權(quán)確保只有具備特定權(quán)限的用戶才能訪問(wèn)某些資源或執(zhí)行特定操作。
- 基于角色的訪問(wèn)控制(RBAC):為用戶定義角色,并在數(shù)據(jù)庫(kù)中保存對(duì)應(yīng)權(quán)限。
const roles = {
admin: "admin",
user: "user",
};
- 保護(hù) API 路由:在 API 中根據(jù)用戶角色判斷是否允許訪問(wèn)。
import { getSession } from "next-auth/client";
export default async function handler(req, res) {
const session = await getSession({ req });
if (!session || session.user.role !== "admin") {
return res.status(403).json({ message: "禁止訪問(wèn)" });
}
// 處理經(jīng)過(guò)授權(quán)的請(qǐng)求
res.status(200).json({ message: "訪問(wèn)成功" });
}
- 保護(hù)頁(yè)面:可使用高階組件(HOC)或自定義 hook 根據(jù)用戶角色進(jìn)行頁(yè)面權(quán)限控制。
import { useSession } from "next-auth/client";
import { useRouter } from "next/router";
import { useEffect } from "react";
const withAuth = (WrappedComponent, role) => {
return (props) => {
const [session, loading] = useSession();
const router = useRouter();
useEffect(() => {
if (!loading) {
if (!session) {
router.push("/api/auth/signin");
} else if (session.user.role !== role) {
router.push("/unauthorized");
}
}
}, [session, loading]);
if (loading || !session || session.user.role !== role) {
return <p>加載中...</p>;
}
return <WrappedComponent {...props} />;
};
};
export default withAuth;
使用該高階組件保護(hù)頁(yè)面:
import withAuth from "../path/to/withAuth";
const AdminPage = () => {
return <p>歡迎管理員</p>;
};
export default withAuth(AdminPage, "admin");
- 其他建議:始終采用 HTTPS、設(shè)置 HttpOnly 與 Secure 屬性的 Cookie、實(shí)施多重認(rèn)證(MFA)、定期審核用戶角色以及記錄審計(jì)日志,以便追蹤和分析可疑活動(dòng)。
七、內(nèi)容安全策略(CSP)的配置
CSP 利用 HTTP 頭部控制允許加載的資源類型,是防范 XSS 和數(shù)據(jù)注入的重要手段。
7.1 理解 CSP 頭部
通過(guò)設(shè)置 Content-Security-Policy
頭部,可以限定內(nèi)容加載的來(lái)源。例如:
Content-Security-Policy: default-src 'self'; script-src 'self' https://apis.google.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;
- **default-src 'self'**:默認(rèn)僅允許同源內(nèi)容加載。
- script-src 'self' https://apis.google.com:腳本僅允許同源和指定 API 來(lái)源。
- **style-src 'self' 'unsafe-inline'**:樣式允許同源和內(nèi)聯(lián)樣式(盡量避免 'unsafe-inline')。
- **img-src 'self' data:**:圖片來(lái)源限制為同源或 data URI。
7.2 在 Next.js 中配置 CSP
- 通過(guò) next.config.js 添加 CSP 頭部:
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy",
value:
"default-src 'self'; script-src 'self' https://apis.google.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;",
},
],
},
];
},
};
- 在自定義服務(wù)器中設(shè)置 CSP(例如使用 Express):
const express = require("express");
const next = require("next");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' https://apis.google.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;",
);
next();
});
server.all("*", (req, res) => {
return handle(req, res);
});
server.listen(3000, (err) => {
if (err) throw err;
console.log("> 服務(wù)器已啟動(dòng),訪問(wèn) http://localhost:3000");
});
});
7.3 CSP 的最佳實(shí)踐
- 使用 nonce 或哈希:盡量避免 'unsafe-inline',可以采用動(dòng)態(tài)生成的 nonce 或預(yù)設(shè)的哈希值來(lái)允許特定內(nèi)聯(lián)腳本或樣式。
- 避免使用通配符:不要輕易使用
*
,以免放寬安全限制。 - 報(bào)告違規(guī)行為:通過(guò)
report-uri
或report-to
指令獲取 CSP 違規(guī)報(bào)告,及時(shí)發(fā)現(xiàn)和解決問(wèn)題。 - 逐步收緊策略:可以從較寬松的策略開(kāi)始,然后逐步收緊以覆蓋所有必要的資源來(lái)源。
- 測(cè)試與監(jiān)控:定期使用工具(如 Google 的 CSP Evaluator)測(cè)試 CSP 配置,并監(jiān)控違規(guī)報(bào)告。
例如,一個(gè)較為全面的 CSP 配置如下:
// next.config.js
module.exports = {
async headers() {
return [
{
source: "/(.*)",
headers: [
{
key: "Content-Security-Policy",
value:
"default-src 'self'; script-src 'self' https://apis.google.com 'nonce-<randomNonce>'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.example.com; font-src 'self' https://fonts.gstatic.com; frame-src 'self' https://www.youtube.com; object-src 'none'; base-uri 'self'; form-action 'self'; report-uri /csp-violation-report-endpoint;",
},
],
},
];
},
};
八、訪問(wèn)速率限制
為防止暴力破解、DDoS 攻擊或資源濫用,實(shí)現(xiàn)請(qǐng)求速率限制是一種有效的防護(hù)手段。
8.1 速率限制原理
速率限制控制某個(gè)用戶在一定時(shí)間內(nèi)發(fā)出的請(qǐng)求數(shù)量,常見(jiàn)的策略有固定窗口、滑動(dòng)窗口和令牌桶算法等。
8.2 在 Next.js 中實(shí)現(xiàn)速率限制
可以借助 Express 中間件和 express-rate-limit
包來(lái)實(shí)現(xiàn),例如:
- 安裝依賴:
npm install express express-rate-limit
- 設(shè)置自定義服務(wù)器并配置中間件:
// server.js
const express = require("express");
const next = require("next");
const rateLimit = require("express-rate-limit");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
// 定義速率限制規(guī)則:15 分鐘內(nèi)每個(gè) IP 最多 100 次請(qǐng)求
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: "來(lái)自該 IP 的請(qǐng)求過(guò)多,請(qǐng)15分鐘后重試",
});
app.prepare().then(() => {
const server = express();
// 為所有請(qǐng)求應(yīng)用速率限制
server.use(limiter);
server.all("*", (req, res) => {
return handle(req, res);
});
server.listen(3000, (err) => {
if (err) throw err;
console.log("> 服務(wù)器已啟動(dòng),訪問(wèn) http://localhost:3000");
});
});
- 運(yùn)行服務(wù)器:通過(guò)
node server.js
啟動(dòng)服務(wù),并測(cè)試是否在超出限制后返回 429 狀態(tài)碼。
8.3 高級(jí)速率限制方案
對(duì)于分布式系統(tǒng),可以使用 Redis 作為存儲(chǔ)后端來(lái)追蹤請(qǐng)求數(shù):
- 安裝 Redis 相關(guān)依賴:
npm install redis rate-limit-redis
- 修改服務(wù)器配置:
// server.js
const express = require("express");
const next = require("next");
const rateLimit = require("express-rate-limit");
const RedisStore = require("rate-limit-redis");
const Redis = require("ioredis");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
const redisClient = new Redis();
const limiter = rateLimit({
store: new RedisStore({
client: redisClient,
}),
windowMs: 15 * 60 * 1000,
max: 100,
message: "來(lái)自該 IP 的請(qǐng)求過(guò)多,請(qǐng)15分鐘后重試",
});
app.prepare().then(() => {
const server = express();
server.use(limiter);
server.all("*", (req, res) => {
return handle(req, res);
});
server.listen(3000, (err) => {
if (err) throw err;
console.log("> 服務(wù)器已啟動(dòng),訪問(wèn) http://localhost:3000");
});
});
采用速率限制可以有效防止惡意攻擊、暴力破解等行為,同時(shí)建議針對(duì)不同的用戶角色、接口進(jìn)行更細(xì)粒度的控制,并配合監(jiān)控和告警機(jī)制。
九、其他安全措施
- 安全頭部設(shè)置:使用 Helmet.js 設(shè)置各種 HTTP 頭部,增強(qiáng)應(yīng)用安全。
import helmet from "helmet";
export default function handler(req, res) {
helmet()(req, res, () => {
// 處理 API 請(qǐng)求
});
}
- 依賴管理:定期更新依賴包并利用工具(如 npm audit)檢查安全漏洞。
npm outdated
npm update
npm audit
- 環(huán)境變量管理:
- 將敏感信息存放于
.env
文件(如.env.local
、.env.production
),并確保這些文件不被提交到版本控制。 - 使用
process.env.VARIABLE_NAME
獲取變量,開(kāi)發(fā)時(shí)可利用 dotenv 加載環(huán)境變量。 - 部署時(shí)確保環(huán)境變量安全配置,并定期輪換 API 密鑰或其它機(jī)密信息。
- 靜態(tài)文件訪問(wèn)控制:確保敏感的靜態(tài)文件不對(duì)外開(kāi)放訪問(wèn),可通過(guò)配置 rewrites 來(lái)控制。
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: "/api/:path*",
destination: "/:path*", // 匹配所有 API 路由
},
];
},
};
通過(guò)上述安全策略和最佳實(shí)踐,你可以構(gòu)建出既高效又能有效防御常見(jiàn)網(wǎng)絡(luò)攻擊的 Next.js 應(yīng)用,保障用戶數(shù)據(jù)安全及應(yīng)用穩(wěn)定運(yùn)行。