Arcjet으로 애플리케이션 보안
이 문서는 Arcjet을 사용해 앱을 보호하는 방법을 설명해요. Arcjet은 몇 줄의 코드로 앱에 레이트 리밋, 봇 보호, 이메일 검증, 일반 공격 방어를 구현할 수 있는 보안 계층이에요. 현재 JS 및 Python 앱을 지원하며, 곧 다른 언어도 지원할 예정이에요.
출처: 문서
본문
Arcjet은 개발자가 단 몇 줄의 코드로 앱을 보호할 수 있게 해 주는 보안 계층이에요. 레이트 리밋, 봇 보호, 이메일 검증, 일반적인 공격에 대한 방어를 구현해 보아요.
Arcjet은 애플리케이션의 의존성으로 설치되며 에이전트나 추가 인프라가 필요 없어요. SDK는 현재 JS & Python 앱에서 사용할 수 있고, 다른 언어 지원이 곧 제공될 예정이에요.
빠른 시작 (Next.js)
Arcjet이 무엇을 할 수 있는지 빠르게 보는 방법은 예시 앱을 배포하는 것이에요. example.arcjet.com에서도 볼 수 있어요.
GitHub 저장소를 클론해요:
git clone git@github.com:arcjet/arcjet-example-nextjs-fly.git
새 Fly 앱을 설정해요:
fly launch --no-deploy
이 명령이 여러분을 위해 Dockerfile과 fly.toml을 생성해요.
Arcjet 계정을 만들고 Fly 앱에 연결해요:
fly ext arcjet create
이 명령은:
- Fly 계정에 연결된 Arcjet 계정을 만들어요.
- Fly 조직에 매핑된 Arcjet 팀을 만들어요.
- Fly 애플리케이션에 대한 Arcjet 사이트를 만들어요.
ARCJET_KEY를 Fly 애플리케이션의 시크릿으로 설정해요.
Fly에 배포해요:
fly deploy
브라우저에서 앱을 열고 기능을 시도해 보아요.
Arcjet 대시보드에서 요청 세부 사항을 검토해요:
fly ext arcjet dashboard
로컬에서 실행
Arcjet 보호는 Fly에 배포했을 때뿐 아니라 로컬에서도 실행돼요. 이 덕분에 보안 규칙을 쉽게 테스트하고 디버깅할 수 있어요.
예시를 이미 클론하고 Arcjet을 Fly 앱에 연결했다고 가정할게요(위 참고):
Arcjet 대시보드에 로그인해 앱의 ARCJET_KEY를 가져와요.
fly ext arcjet dashboard
의존성을 설치해요:
npm ci
.env.local.example을 .env.local로 이름을 바꾸고 Arcjet 키를 추가해요. 레이트 리밋 인증을 테스트하고 싶다면 Auth.js 시크릿도 추가하고 GitHub OAuth 앱을 만들어야 해요.
dev 서버를 시작해요
npm run dev
브라우저에서 http://localhost:3000을 열어요.
Arcjet으로 애플리케이션 보호
ARCJET_KEY 시크릿을 설정하면 Arcjet을 사용해 애플리케이션을 보호할 수 있어요. Arcjet은 Bun, Next.js, Node.js, SvelteKit 등 많은 웹 프레임워크를 지원해요.
Node.js 예시
이 예시는 GitHub에서도 찾을 수 있어요.
-
Arcjet SDK를 설치해요:
npm i @arcjet/node -
index.ts의 한 경로에 레이트 리밋을 추가해요:import arcjet, { tokenBucket } from "@arcjet/node"; import http from "node:http"; const aj = arcjet({ key: process.env.ARCJET_KEY!, // Set as a secret on your Fly app rules: [ // Create a token bucket rate limit. Other algorithms are supported. tokenBucket({ mode: "LIVE", // will block requests. Use "DRY_RUN" to log only characteristics: ["userId"], // track requests by a custom user ID refillRate: 5, // refill 5 tokens per interval interval: 10, // refill every 10 seconds capacity: 10, // bucket maximum capacity of 10 tokens }), ], }); const server = http.createServer(async function ( req: http.IncomingMessage, res: http.ServerResponse, ) { const userId = "user123"; // Replace with your authenticated user ID const decision = await aj.protect(req, { userId, requested: 5 }); // Deduct 5 tokens from the bucket console.log("Arcjet decision", decision); if (decision.isDenied()) { res.writeHead(429, { "Content-Type": "application/json" }); res.end( JSON.stringify({ error: "Too Many Requests", reason: decision.reason }), ); } else { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ message: "Hello world" })); } }); server.listen(8000); -
애플리케이션을 실행해요:
npx tsx --env-file .env.local index.ts
앱을 로드하고 페이지를 몇 번 새로 고쳐 레이트 리밋이 작동하는지 확인해 보아요.
Python + FastAPI 예시
이 예시는 GitHub에서도 찾을 수 있어요.
-
Arcjet SDK를 설치해요:
mkdir arcjet-fastapi cd arcjet-fastapi uv init uv add arcjet "fastapi[standard]" uvicorn -
main.py에 새 파일을 만들어요:import os from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from arcjet import ( arcjet, shield, detect_bot, token_bucket, Mode, BotCategory, ) app = FastAPI() aj = arcjet( key=os.environ["ARCJET_KEY"], # Get your key from https://app.arcjet.com rules=[ # Shield protects your app from common attacks e.g. SQL injection shield(mode=Mode.LIVE), # Create a bot detection rule detect_bot( mode=Mode.LIVE, allow=[ BotCategory.SEARCH_ENGINE, # Google, Bing, etc # Uncomment to allow these other common bot categories # See the full list at https://arcjet.com/bot-list # BotCategory.MONITOR, # Uptime monitoring services # BotCategory.PREVIEW, # Link previews e.g. Slack, Discord ], ), # Create a token bucket rate limit. Other algorithms are supported token_bucket( # Tracked by IP address by default, but this can be customized # See https://docs.arcjet.com/fingerprints # characteristics: ["ip.src"], mode=Mode.LIVE, refill_rate=5, # Refill 5 tokens per interval interval=10, # Refill every 10 seconds capacity=10, # Bucket capacity of 10 tokens ), ], ) @app.get("/") async def hello(request: Request): # Call protect() to evaluate the request against the rules decision = await aj.protect( request, requested=5, # Deduct 5 tokens from the bucket ) # Handle denied requests if decision.is_denied(): status = 429 if decision.reason.is_rate_limit() else 403 return JSONResponse( {"error": "Denied", "reason": decision.reason.to_dict()}, status_code=status, ) # Check IP metadata (VPNs, hosting, geolocation, etc) if decision.ip.is_hosting(): # Requests from hosting IPs are likely from bots, so they can usually be # blocked. However, consider your use case - if this is an API endpoint # then hosting IPs might be legitimate. # https://docs.arcjet.com/blueprints/vpn-proxy-detection return JSONResponse( {"error": "Denied from hosting IP"}, status_code=403, ) return {"message": "Hello world", "decision": decision.to_dict()} -
환경에 Arcjet 키를 넣고 애플리케이션을 실행해요:
export ARCJET_ENV=development export ARCJET_KEY=ajkey_yourkey uv run uvicorn main:app --reload
가격
Arcjet 가격 페이지를 참고해 보아요.
지원
이메일: support@arcjet.com
Discord: 참여하기.
전체 지원 정책은 Arcjet 문서를 참고해 보아요.