@arcjet/next
弧喷 是您AI代码中的运行时安全平台。 检测出快速注射,授权代理工具呼叫,编辑敏感数据,并阻断bots和滥用. 在行动发生前,您在应用程序内拨打实时安全构件.
这是 弧喷 SDK 为 下一个.js 请求保护 —— 说
使用它来保护HTTP路由处理器和API端点. 如果你需要保护
AI代理工具呼叫, MCP 服务器处理器, 或背景任务( 任何没有
hTTP 请求,参见 @arcjet/guard。 。 。 .
为什么是Arcjet?
您的应用程序的AI功能和代理采取真实的行动,呼叫工具,读取数据,打击API. Arcjet在代码内运行 让你实时执行每个动作的安全 然后审计发生了什么
开始
快速设置 AI 代理
- 登录 CLI :
npx @arcjet/cli auth login - 安装版本代理技能, 这样您的编码代理匹配 SDK :
npx @tanstack/intent@latest install - 告诉你的探员要保护什么 剩下的就交给他.
手动设置
- 登录 使用 CLI(或
app.arcjet.com:npx @arcjet/cli auth login npm install @arcjet/next- 设定
ARCJET_KEY=ajkey_yourkey内.env.local - 将 Arcjet 添加到您的路线中 — 见 快速开始 下级
npm 软件包 | GitHub 来源 | 完整文档 | 其他 SDKs 软件
特征
以下所有特征均可使用 @arcjet/next 请求保护.
警卫保护(工具呼叫、MCP服务器、队列)见
@arcjet/guard。 。 。 .
| 特性 | @arcjet/next | @arcjet/guard |
|---|---|---|
| 限制费率 | ✅ | ✅ |
| 快速注射检测 | ✅ | ✅ |
| 敏感信息检测 | ✅ | ✅ |
| 瓶装保护 | ✅ | —— 说 |
| 盾牌 WAF | ✅ | —— 说 |
| 电子邮件验证 | ✅ | —— 说 |
| 注册表保护 | ✅ | —— 说 |
| 请求过滤器 | ✅ | —— 说 |
| IP 分析 | ✅ | —— 说 |
| 自定义规则 | —— 说 | ✅ |
- 🔒 快速注射检测 - 探测和阻断 在到达你的LLM前立即注射.
- 🤖 瓶装保护 · 停止刮刮机、证明材料和 AI爬行者滥用你的终点.
- 🛑 限制费率 - 标志桶、固定窗口和滑动 窗口算法; 每个用户的模型 AI 符号预算.
- 🕵️ 敏感信息检测 - 块 PII,信用卡和定制模式 从进入你的AI管道.
- 🛡️ 盾牌 WAF • 防止SQL注射、XSS和其他 常见的网络攻击.
- 📧 电子邮件验证 一次性、无效和 注册时无法提供地址 .
- 📝 注册表保护 结合机器人保护, 电子邮件验证, 以及限制您注册表格的速率 .
- 🎯 请求过滤器 ——IP,路径上以表达为主的规则, 标题和自定义字段.
- 🌐 IP 分析 地理定位、ASN、VPN、代理、Tor和托管 检测包括每个请求.
- 🚅 无锡 ——设置安全头等
Content-Security-Policy(中文(简体) ).
快开始
此示例保护 Next.js AI 聊天路由: 屏蔽自动客户端 增加费用、执行每个用户代号的预算、检测敏感信息 在信息中,并阻止快速注射攻击 在他们到达模型之前.
安装 维塞尔 AI SDK,一个AI提供者,和 兰巴 敏感信息后端 :
npm install ai @ai-sdk/openai @arcjet/sensitive-info-rampart
创建一个新的 API 路由 /app/api/chat/route.ts数字 :
import { openai } from "@ai-sdk/openai";
import arcjet, {
detectBot,
detectPromptInjection,
sensitiveInfo,
shield,
tokenBucket,
} from "@arcjet/next";
import { rampart } from "@arcjet/sensitive-info-rampart";
import type { UIMessage } from "ai";
import { convertToModelMessages, isTextUIPart, streamText } from "ai";
const aj = arcjet({
key: process.env.ARCJET_KEY!, // Get your key with: npx @arcjet/cli sites get-key
// Track budgets per user — replace "userId" with any stable identifier
characteristics: ["userId"],
rules: [
// Shield protects against common web attacks e.g. SQL injection
shield({ mode: "LIVE" }),
// Block all automated clients — bots inflate AI costs
detectBot({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
allow: [], // Block all bots. See https://arcjet.com/bot-list
}),
// Enforce budgets to control AI costs. Adjust rates and limits as needed.
tokenBucket({
mode: "LIVE",
refillRate: 2_000, // Refill 2,000 tokens per hour
interval: "1h",
capacity: 5_000, // Maximum 5,000 tokens in the bucket
}),
// Block messages containing sensitive information to prevent data leaks
sensitiveInfo({
mode: "LIVE",
// Block PII types that should never appear in AI prompts.
// Remove types your app legitimately handles (e.g. EMAIL for a support bot).
deny: ["CREDIT_CARD_NUMBER", "EMAIL"],
// Detect PII on-device with the Rampart model
backend: rampart(),
}),
// Detect prompt injection attacks before they reach your AI model
detectPromptInjection({
mode: "LIVE",
}),
],
});
export async function POST(req: Request) {
const userId = "user-123"; // Replace with your session/auth lookup
const { messages }: { messages: UIMessage[] } = await req.json();
const modelMessages = await convertToModelMessages(messages);
// Estimate token cost: ~1 token per 4 characters of text (rough heuristic).
// For accurate counts use https://www.npmjs.com/package/tiktoken
const totalChars = modelMessages.reduce((sum, m) => {
const content =
typeof m.content === "string" ? m.content : JSON.stringify(m.content);
return sum + content.length;
}, 0);
const estimate = Math.ceil(totalChars / 4);
// Extract the most recent user message to scan for injection and PII.
// Pass all messages if you want to scan the full conversation.
const lastMessage: string = (messages.at(-1)?.parts ?? [])
.filter(isTextUIPart)
.map((p) => p.text)
.join(" ");
const decision = await aj.protect(req, {
userId,
requested: estimate,
sensitiveInfoValue: lastMessage,
detectPromptInjectionMessage: lastMessage,
});
if (decision.isDenied()) {
if (decision.reason.isBot()) {
return new Response("Automated clients are not permitted", {
status: 403,
});
} else if (decision.reason.isRateLimit()) {
return new Response("AI usage limit exceeded", { status: 429 });
} else if (decision.reason.isSensitiveInfo()) {
return new Response("Sensitive information detected", { status: 400 });
} else if (decision.reason.isPromptInjection()) {
return new Response(
"Prompt injection detected — please rephrase your message",
{ status: 400 },
);
} else {
return new Response("Forbidden", { status: 403 });
}
}
const result = await streamText({
model: openai("gpt-4o"),
messages: modelMessages,
});
return result.toUIMessageStreamResponse();
}
Rampart 装入本地的 ONNX 运行时间, 所以添加 @arcjet/sensitive-info-rampart, (中文(简体) ),
@huggingface/transformers,以及 onnxruntime-node 改为
serverExternalPackages 内 next.config.js 在节点运行路线
运行时间( 默认) 。 见
Bundlers和框架。 。 。 .
全文见 Arcjet Next.js SDK 文档。 。 。 .
快速注射检测
检测和阻止快速注射攻击——试图推翻你的AI
模型的指示——在他们到达你的模型之前. 传递用户消息
通过 detectPromptInjectionMessage 每个 protect() 电话.
import arcjet, { detectPromptInjection } from "@arcjet/next";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
detectPromptInjection({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
}),
],
});
export async function POST(request: Request) {
const { message } = await request.json();
const decision = await aj.protect(request, {
detectPromptInjectionMessage: message,
});
if (decision.isDenied() && decision.reason.isPromptInjection()) {
return NextResponse.json(
{ error: "Prompt injection detected — please rephrase your message" },
{ status: 400 },
);
}
// Forward to your AI model...
}
瓶装保护
Arcjet允许您配置一个 bots 列表以允许或拒绝 。 指定
allow 意味着所有其他的bots被否定。 一个空的允许列表块全部块 .
可用类别 : CATEGORY:ACADEMIC, (中文(简体) ), CATEGORY:ADVERTISING, (中文(简体) ),
CATEGORY:AI, (中文(简体) ), CATEGORY:AMAZON, (中文(简体) ), CATEGORY:APPLE, (中文(简体) ), CATEGORY:ARCHIVE, (中文(简体) ),
CATEGORY:BOTNET, (中文(简体) ), CATEGORY:FEEDFETCHER, (中文(简体) ), CATEGORY:GOOGLE, (中文(简体) ),
CATEGORY:META, (中文(简体) ), CATEGORY:MICROSOFT, (中文(简体) ), CATEGORY:MONITOR, (中文(简体) ),
CATEGORY:OPTIMIZER, (中文(简体) ), CATEGORY:PREVIEW, (中文(简体) ), CATEGORY:PROGRAMMATIC, (中文(简体) ),
CATEGORY:SEARCH_ENGINE, (中文(简体) ), CATEGORY:SLACK, (中文(简体) ), CATEGORY:SOCIAL, (中文(简体) ),
CATEGORY:TOOL, (中文(简体) ), CATEGORY:UNKNOWN, (中文(简体) ), CATEGORY:VERCEL, (中文(简体) ),
CATEGORY:WEBHOOK, (中文(简体) ), CATEGORY:YAHOO。也可以允许或拒绝
按名称排列的特定块。 。 。 .
import arcjet, { detectBot } from "@arcjet/next";
import { isSpoofedBot } from "@arcjet/inspect";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
detectBot({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
allow: [
"CATEGORY:SEARCH_ENGINE", // Google, Bing, etc
// Uncomment to allow these other common bot categories:
// "CATEGORY:MONITOR", // Uptime monitoring services
// "CATEGORY:PREVIEW", // Link previews e.g. Slack, Discord
// See the full list at https://arcjet.com/bot-list
],
}),
],
});
export async function GET(request: Request) {
const decision = await aj.protect(request);
if (decision.isDenied() && decision.reason.isBot()) {
return NextResponse.json({ error: "No bots allowed" }, { status: 403 });
}
// Verifies the authenticity of common bots using IP data.
// Verification isn't always possible, so check the results separately.
// https://docs.arcjet.com/bot-protection/reference#bot-verification
if (decision.results.some(isSpoofedBot)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
return NextResponse.json({ message: "Hello world" });
}
瓶类
波茨可以被配置为 类别 和(或) 具体 机器人名称。例如,允许搜索引擎和OpenAI 爬行者,但否认 所有其他bots:
detectBot({
mode: "LIVE",
allow: ["CATEGORY:SEARCH_ENGINE", "OPENAI_CRAWLER_SEARCH"],
});
已验证 vs spoofed bots
自称是知名爬行者(如:Googlebot)的Bots通过验证
检查他们的IP地址 与已知的IP范围 对于那个机器人。 如果一个机器人
无法校验, 它被标记为 soof 。 使用 isSpoofedBot 从
@arcjet/inspect 检查 :
import { isSpoofedBot } from "@arcjet/inspect";
if (decision.results.some(isSpoofedBot)) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
限制费率
Arcjet支持符号桶,固定窗口和滑动窗口算法.
Token桶是控制AI符号预算的理想工具——设置 capacity 改为
用户可以花费的最大代币, refillRate 以还原多少个令牌
百分比 interval,并通过 requested 内 protect()。 。 。 .
这个 interval 接受字符串( r)"1s", (中文(简体) ), "1m", (中文(简体) ), "1h", (中文(简体) ), "1d")或秒为
一个数字。 使用 characteristics 以跟踪每个用户的限制,而不是每个IP.
import arcjet, { tokenBucket } from "@arcjet/next";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
characteristics: ["userId"], // Track per user
rules: [
// Token bucket: ideal for controlling AI token costs
tokenBucket({
mode: "LIVE",
refillRate: 2_000, // Refill 2,000 tokens per hour
interval: "1h",
capacity: 5_000, // Maximum 5,000 tokens in the bucket
}),
],
});
const decision = await aj.protect(request, {
userId: "user-123",
requested: estimate, // Number of tokens to deduct
});
if (decision.isDenied() && decision.reason.isRateLimit()) {
return NextResponse.json(
{ error: "AI usage limit exceeded" },
{ status: 429 },
);
}
敏感信息检测
检测和封存请求中的 PII 内容
@arcjet/sensitive-info-rampart, (中文(简体) ),
在本地运行着Rampart命名实体识别模型,所以没有数据
离开你的环境。 通过扫描传送内容 sensitiveInfoValue 打开
每个 protect() 电话 Rampart侦测到姓名、地址、政府和
财务识别符、电子邮件地址、电话号码、IP地址和卡片
数字。 添加识别器以检测自定义模式 .
npm install @arcjet/sensitive-info-rampart
import arcjet, { sensitiveInfo } from "@arcjet/next";
import { rampart } from "@arcjet/sensitive-info-rampart";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
sensitiveInfo({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
deny: ["CREDIT_CARD_NUMBER", "EMAIL", "PHONE_NUMBER"],
backend: rampart(),
}),
],
});
export async function POST(request: Request) {
const { message } = await request.json();
const decision = await aj.protect(request, {
sensitiveInfoValue: message,
});
if (decision.isDenied() && decision.reason.isSensitiveInfo()) {
return NextResponse.json(
{ error: "Sensitive information detected" },
{ status: 400 },
);
}
}
Rampart 装入本地的 ONNX 运行时间, 所以添加 @arcjet/sensitive-info-rampart, (中文(简体) ),
@huggingface/transformers,以及 onnxruntime-node 改为
serverExternalPackages 内 next.config.js 在节点运行路线
运行时间( 默认) 。 见
Bundlers和框架。 。 。 .
盾牌 WAF
保护您的应用程序免受常见的网络攻击, 包括 OWASP 排行前10名.
import arcjet, { shield } from "@arcjet/next";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
shield({
mode: "LIVE", // Blocks requests. Use "DRY_RUN" to log only
}),
],
});
电子邮件验证
验证和验证电子邮件地址。 拒绝类型 : DISPOSABLE, (中文(简体) ), FREE, (中文(简体) ),
NO_MX_RECORDS, (中文(简体) ), NO_GRAVATAR, (中文(简体) ), INVALID。 。 。 .
import arcjet, { validateEmail } from "@arcjet/next";
import { NextResponse } from "next/server";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
validateEmail({
mode: "LIVE",
deny: ["DISPOSABLE", "INVALID", "NO_MX_RECORDS"],
}),
],
});
export async function POST(request: Request) {
const { email } = await request.json();
const decision = await aj.protect(request, { email });
if (decision.isDenied() && decision.reason.isEmail()) {
return NextResponse.json(
{ error: "Invalid email address" },
{ status: 400 },
);
}
}
请求过滤器
使用基于表达式的规则对请求属性过滤请求(IP, 页: 1.
import arcjet, { filter } from "@arcjet/next";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [
filter({
mode: "LIVE",
deny: ['ip.src == "1.2.3.4"', 'http.request.uri.path contains "/admin"'],
}),
],
});
按国家分列的区块
限制进入特定国家——有助于发放许可证、遵守或
区域推广。 这个 allow 拒绝所有未列入名单的国家:
filter({
mode: "LIVE",
// Allow only US traffic — all other countries are denied
allow: ['ip.src.country == "US"'],
});
屏蔽 VPN 和代理流量
防止匿名交通进入敏感终点——对 预防欺诈、实施地理限制并减少滥用:
filter({
mode: "LIVE",
deny: [
"ip.src.vpn", // VPN services
"ip.src.proxy", // Open proxies
"ip.src.tor", // Tor exit nodes
],
});
对于更细致的处理,使用 decision.ip 呼叫后帮助 protect()数字 :
const decision = await aj.protect(request);
if (decision.ip.isVpn() || decision.ip.isTor()) {
return NextResponse.json(
{ error: "VPN traffic not allowed" },
{ status: 403 },
);
}
见 请求过滤文件, (中文(简体) ), IP 地理定位蓝图,以及 VPN/Proxy检测蓝图 更多细节.
知识产权分析
Arcjet用IP元数据丰富了每个请求. 使用这些助手来制作 基于网络信号的政策决定:
const decision = await aj.protect(request);
if (decision.ip.isHosting()) {
// Requests from cloud/hosting providers are often automated.
// https://docs.arcjet.com/blueprints/vpn-proxy-detection
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
}
if (decision.ip.isVpn() || decision.ip.isProxy() || decision.ip.isTor()) {
// Handle VPN/proxy traffic according to your policy
}
// Access geolocation and network details
console.log(decision.ip.country, decision.ip.city, decision.ip.asn);
自定义特性
跟踪和限制任何稳定的标识符的请求——用户ID,API密钥,会话, ——而不是仅IP地址.
const aj = arcjet({
key: process.env.ARCJET_KEY!,
characteristics: ["userId"], // Declare at the SDK level
rules: [
tokenBucket({
mode: "LIVE",
refillRate: 2_000,
interval: "1h",
capacity: 5_000,
}),
],
});
// Pass the characteristic value at request time
const decision = await aj.protect(request, {
userId: "user-123", // Replace with your actual user ID
requested: estimate,
});
服务器组件和行动
@arcjet/next 出口a request() 在你没有的地方工作的助手
直接访问来源 Request 对象,例如服务器动作.
import arcjet, { shield } from "@arcjet/next";
import { request } from "@arcjet/next";
const aj = arcjet({
key: process.env.ARCJET_KEY!,
rules: [shield({ mode: "LIVE" })],
});
export async function myServerAction() {
"use server";
const req = await request();
const decision = await aj.protect(req);
if (decision.isDenied()) {
throw new Error("Forbidden");
}
// ...
}
最佳做法
见 Arcjet 最佳做法 详细指导。 密钥 建议:
创建单一客户端实例 并重新使用它与 withRule() 页:1
特定路线规则。 SDK 缓存决定和配置,因此创建
每一个请求的新实例 有效的废物.
// lib/arcjet.ts — create once, import everywhere
import arcjet, { shield } from "@arcjet/next";
export default arcjet({
key: process.env.ARCJET_KEY!,
rules: [
shield({ mode: "LIVE" }), // base rules applied to every request
],
});
// app/api/chat/route.ts — extend per-route with withRule()
import aj from "@/lib/arcjet";
import { detectBot, tokenBucket } from "@arcjet/next";
const routeAj = aj.withRule(detectBot({ mode: "LIVE", allow: [] })).withRule(
tokenBucket({
mode: "LIVE",
refillRate: 2_000,
interval: "1h",
capacity: 5_000,
}),
);
export async function POST(req: Request) {
const decision = await routeAj.protect(req, { requested: 500 });
// ...
}
其他建议:
- 打电话
protect()在路由处理器,而不是中间软件. 中间软件缺少路由 上下文,使其难以应用特定路由规则或自定义响应. - 打电话
protect()一次请求. 在中间软件和一个 处理器将工作增加一倍,并可以产生出乎意料的结果. - 开始规则
DRY_RUN模式 在切换到前观察行为LIVE。这使得您可以调取阈值而不影响真实流量. - 配置代理 如果您的应用程序运行在负载平衡器或倒置代理服务器后
因此Arcjet解决了真正的客户端IP:
arcjet({ key: process.env.ARCJET_KEY!, rules: [], proxies: ["100.100.100.100"], }); - 明确处理错误 .
protect()永不扔出 — 在错误时返回 a 一个ERROR结果。 无法通过日志和允许请求打开 :if (decision.isErrored()) { console.error("Arcjet error", decision.reason.message); // allow the request to proceed }