Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Upstash Redis 및 Cloudflare Workers를 사용하여 보안 API 키 생성:단계별 가이드

API 키는 서비스의 현관 열쇠와 같습니다. 이를 통해 사용자가 보안을 유지하면서 들어갈 수 있습니다. 이 블로그에서는 빠른 서버리스 데이터 스토리지를 위한 Upstash Redis와 엣지에서 요청을 처리하는 Cloudflare Workers를 사용하여 간단하고 안전한 API 키 생성기를 구축하는 과정을 안내하겠습니다. 새로운 서비스를 설정하든 기존 앱에 키를 추가하든 모든 것이 원활하고 효율적으로 실행되도록 API 키를 생성, 저장 및 검증하는 방법을 배우게 됩니다.

API 키란 무엇인가요?

API 키는 API에 액세스하려는 사용자나 애플리케이션을 식별하고 인증하는 고유 코드입니다. 개인 패스처럼 생각하십시오. 누군가 귀하의 서비스를 사용하려면 허용되었음을 증명하기 위해 이 "키"를 제시해야 합니다. API 키는 리소스에 액세스할 수 있는 사람을 제어하는 ​​데 도움이 되며 사용량을 추적하고 제한을 적용하거나 무단 액세스를 방지하는 데 자주 사용됩니다. 이는 API 액세스를 관리하고 데이터를 안전하게 유지하는 간단한 방법입니다.

우리가 구축할 것

이 가이드에서는 두 가지 핵심 기능을 제공하는 API 키 생성기를 만듭니다:

  1. 맞춤 설정으로 새 API 키 생성
  2. 메타데이터를 검색하는 동안 API 키 유효성 검사

주요 기능은 다음과 같습니다:

  • 맞춤설정 가능한 키 접두어
  • 만료일
  • 속도 제한
  • 메타데이터 저장
  • 소유자 식별

API 키 시스템을 시각화해 보겠습니다

이 다이어그램은 API 키 생성 및 검증을 위한 클라이언트, Cloudflare Worker 및 Upstash Redis 간의 상호 작용을 보여줍니다. 이러한 개요를 염두에 두고 시스템 구축을 시작하겠습니다.

Upstash Redis 및 Cloudflare Workers를 사용하여 보안 API 키 생성:단계별 가이드

전제조건

따라가려면 다음이 필요합니다:

  • Cloudflare 작업자  계정
  • 업스태시  계정
  • 로컬 컴퓨터에 설치된 Node.js

프로젝트 구조

우리 프로젝트는 다음과 같은 구조를 갖습니다:

folder-name/
├── src/
│ ├── config/
│ │ ├── generateApiKey.ts
│ │ └── schema-validation.ts
│ ├── lib/
│ │ └── ratelimit.ts
│ ├── routes/
│ │ ├── create.ts
│ │ └── verify.ts
│ ├── types/
│ │ └── api.ts
│ └── index.ts
├── package.json
└── wrangler.toml

1단계:프로젝트 설정

프로젝트를 설정하고 필요한 종속성을 설치하는 것부터 시작해 보겠습니다.

새 프로젝트 디렉토리 생성

터미널을 열고 다음 명령을 실행하세요:

mkdir keyflow
cd keyflow
npm init -y

종속성 설치

우리 프로젝트에는 몇 가지 패키지가 필요합니다:

npm install hono @upstash/redis @upstash/ratelimit @hono/zod-validator zod wrangler

@upstash/redis :서버리스 환경을 위한 Upstash Redis 클라이언트@upstash/ratelimit :Upstash Redis@hono/zod-validator용 속도 제한 라이브러리 :Honozod용 검증 미들웨어 요청 :TypeScript 우선 스키마 검증 라이브러리wrangler :작업자 개발 및 배포를 위한 Cloudflare의 CLI

Upstash Redis 설정

  1. Upstash 계정에 로그인하고 새로운 Redis 데이터베이스를 생성하세요

Upstash Redis 및 Cloudflare Workers를 사용하여 보안 API 키 생성:단계별 가이드

  1. 생성되면 "REST API" 섹션으로 이동하세요.

Upstash Redis 및 Cloudflare Workers를 사용하여 보안 API 키 생성:단계별 가이드

  1. UPSTASH_REDIS_REST_URL을 복사하세요.  및 UPSTASH_REDIS_REST_TOKEN .env에서 섹션

Cloudflare 작업자 구성

wrangler.toml 만들기  다음 내용으로 프로젝트 루트에 파일을 저장하세요:

name = "keyflow"
main = "src/index.ts"
compatibility_date = "2023-05-18"
 
[vars]
UPSTASH_REDIS_REST_URL = "your-redis-url"
UPSTASH_REDIS_REST_TOKEN = "your-redis-token"

"your-redis-url" 바꾸기  및 "your-redis-token"  Upstash에서 복사한 값으로 변경하세요.

2단계:API 유형 정의

API 요청 및 응답을 위한 TypeScript 인터페이스를 정의하는 것부터 시작해 보겠습니다. 이러한 유형은 애플리케이션 전체에서 유형 안전성을 유지하는 데 도움이 됩니다. 새 파일 src/types/api.ts 만들기 :

export type CreateKeyRequest = {
 apiId: string;
 prefix?: string;
 byteLength?: number;
 ownerId?: string;
 name: string;
 meta?: Record<string, unknown>;
 expires?: number;
 ratelimit?: {
 type: "fast" | "consistent";
 limit: number;
 refillRate: number;
 refillInterval: number;
 };
};
 
export type CreateKeyResponse = {
 key: string;
 keyId: string;
};
 
export type VerifyKeyRequest = {
 key: string;
};
 
export type VerifyKeyResponse = {
 valid: boolean;
 ownerId?: string;
 meta?: Record<string, unknown>;
 expires?: number;
 ratelimit?: {
 limit: number;
 remaining: number;
 reset: number;
 };
};
 
export type Env = {
 UPSTASH_REDIS_REST_URL: string;
 UPSTASH_REDIS_REST_TOKEN: string;
};
 

3단계:API 키 생성 구현

이제 API 키를 생성하는 유틸리티 함수를 만들어 보겠습니다. 새 파일 src/config/generateApiKey.ts 만들기 :

export function generateApiKey(
 prefix: string | undefined,
 byteLength: number,
): string {
 const randomBytes = crypto.getRandomValues(new Uint8Array(byteLength));
 const key = btoa(String.fromCharCode(...new Uint8Array(randomBytes)))
 .replace(/\+/g, "-")
 .replace(/\//g, "_")
 .replace(/=/g, "");
 return prefix ? `${prefix}_${key}` : key;
}
 

이 함수는 암호화된 무작위 바이트를 사용하여 무작위 API 키를 생성하고 이를 base64로 인코딩하여 URL로부터 안전하게 만듭니다.

4단계:속도 제한 구현

src/lib/ratelimit.ts 파일 만들기 :

import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis/cloudflare";
import type { Context, Next } from "hono";
import { env } from "hono/adapter";
import type { Env } from "../types/api";
 
// Middleware for rate limiting
export async function rateLimitMiddleware(c: Context, next: Next) {
 const { UPSTASH_REDIS_REST_TOKEN, UPSTASH_REDIS_REST_URL } = env<Env>(c);
 
 const redis = new Redis({
 url: UPSTASH_REDIS_REST_URL,
 token: UPSTASH_REDIS_REST_TOKEN,
 });
 
 const ratelimit = new Ratelimit({
 redis: redis,
 limiter: Ratelimit.slidingWindow(5, "30 s"),
 });
 
 const ip = c.req.header("CF-Connecting-IP") || "127.0.0.1";
 const { success, limit, remaining, reset } = await ratelimit.limit(ip);
 
 if (!success) {
 return c.json({ error: "Rate limit exceeded" }, 429);
 }
 
 c.header("X-RateLimit-Limit", limit.toString());
 c.header("X-RateLimit-Remaining", remaining.toString());
 c.header("X-RateLimit-Reset", reset.toString());
 
 await next();
}

5단계:API 경로 생성

기본 애플리케이션 파일을 설정하고 API 경로를 생성해 보겠습니다. 이 파일은 두 가지 주요 경로(/keys/create)로 Hono 애플리케이션을 설정합니다.  새로운 API 키 및 /keys/verify 생성용  세 개의 별도 파일로 기존 키를 검증합니다.

1. API 키 경로 생성 구현

src/routes/create.ts 파일 만들기 :

import { zValidator } from "@hono/zod-validator"
import { Redis } from "@upstash/redis/cloudflare"
import { Hono } from "hono"
import { generateApiKey } from "../config/generateApiKey"
import { createApiKeySchema } from "../config/schema-validation"
import type { CreateKeyRequest, CreateKeyResponse, Env } from "../types/api"
 
const create = new Hono<{
 Bindings: Env
}>()
 
create.post(
 "/create",
 zValidator("json", createApiKeySchema, (result, c) => {
 if (!result.success) {
 return c.text("Invalid!", 400)
 }
 }),
 async (c) => {
 // Initialize Redis client
 const { UPSTASH_REDIS_REST_TOKEN, UPSTASH_REDIS_REST_URL } = c.env
 const redis = new Redis({
 url: UPSTASH_REDIS_REST_URL,
 token: UPSTASH_REDIS_REST_TOKEN,
 })
 
 const body = await c.req.json<CreateKeyRequest>()
 
 // Generate unique identifier and API key
 const keyId = crypto.randomUUID()
 const key = generateApiKey(body.prefix, body.byteLength || 16)
 
 const keyData = {
 ...body,
 key,
 keyId,
 createdAt: Date.now(),
 }
 
 const encodedKey = encodeURIComponent(key)
 
 try {
 // Store key data and lookup reference in Redis
 await redis.set(`key:${keyId}`, JSON.stringify(keyData))
 await redis.set(`lookup:${encodedKey}`, keyId)
 
 return c.json<CreateKeyResponse>({ key, keyId })
 } catch (error) {
 console.error("Error in /keys/create:", error)
 return c.json({ error: "Internal Server Error" }, 500)
 }
 }
)
 
export default create

2. API 키 경로 확인 구현

src/routes/verify.ts 파일 만들기 :

import { zValidator } from "@hono/zod-validator";
import { Redis } from "@upstash/redis/cloudflare";
import { Hono } from "hono";
import { verifyApiKeySchema } from "../config/schema-validation";
import type {
 CreateKeyRequest,
 Env,
 VerifyKeyRequest,
 VerifyKeyResponse,
} from "../types/api";
 
// Initialize Hono app with environment bindings
const verify = new Hono<{ Bindings: Env }>();
 
// Define POST route for verifying an API key
verify.post(
 "/verify",
 // Validate request body against schema
 zValidator("json", verifyApiKeySchema, (result, c) => {
 if (!result.success) {
 return c.text("Invalid!", 400); // Return 400 if validation fails
 }
 }),
 async (c) => {
 // Set up Redis with environment variables
 const { UPSTASH_REDIS_REST_TOKEN, UPSTASH_REDIS_REST_URL } = c.env;
 const redis = new Redis({
 url: UPSTASH_REDIS_REST_URL,
 token: UPSTASH_REDIS_REST_TOKEN,
 });
 
 const body = await c.req.json<VerifyKeyRequest>();
 if (!body.key) {
 return c.json({ error: "key is required" }, 400); // Require key in the request body
 }
 
 const encodedKey = encodeURIComponent(body.key);
 const keyId = await redis.get<string>(`lookup:${encodedKey}`); // Retrieve key ID using encoded key
 
 if (!keyId) {
 return c.json<VerifyKeyResponse>({ valid: false }); // Key not found
 }
 
 const keyDataString = await redis.get<string>(`key:${keyId}`); // Retrieve key data by key ID
 
 if (!keyDataString || typeof keyDataString !== "string") {
 return c.json<VerifyKeyResponse>({ valid: false }); // Key data missing or invalid
 }
 
 let keyData: CreateKeyRequest & {
 key: string;
 keyId: string;
 createdAt: number;
 };
 
 try {
 keyData = JSON.parse(keyDataString); // Parse key data
 } catch (parseError) {
 // Handle parse error by deleting invalid data
 console.error("Key data parse error:", parseError);
 await Promise.all([
 redis.del(`key:${keyId}`),
 redis.del(`lookup:${encodedKey}`),
 ]);
 return c.json(
 {
 error: "Invalid key data in storage",
 details: parseError instanceof Error ? parseError.message : "Unknown parse error",
 valid: false,
 },
 500,
 );
 }
 
 // Check if key has expired
 if (keyData.expires && keyData.expires < Date.now()) {
 await Promise.all([
 redis.del(`key:${keyId}`),
 redis.del(`lookup:${encodedKey}`),
 ]);
 return c.json<VerifyKeyResponse>({ valid: false });
 }
 
 // Formulate response with validation status and metadata
 const response: VerifyKeyResponse = {
 valid: true,
 ownerId: keyData.ownerId,
 meta: keyData.meta,
 expires: keyData.expires,
 };
 
 if (keyData.ratelimit) {
 response.ratelimit = {
 limit: keyData.ratelimit.limit,
 remaining: keyData.ratelimit.limit,
 reset: Date.now() + keyData.ratelimit.refillInterval,
 };
 }
 
 return c.json(response); // Return verification response
 },
);
 
export default verify;
 

3. 기본 파일 index.ts 구현

메인 파일 src/index.ts 구현 create.ts 가져오기 , verify.tsrateLimitMiddleWare

import { Hono } from "hono";
import { rateLimitMiddleware } from "./lib/ratelimit";
import create from "./routes/create";
import verify from "./routes/verify";
import type { Env } from "./types/api";
 
const app = new Hono<{
 Bindings: Env;
}>().basePath("/keys");
 
app.use("*", rateLimitMiddleware);
 
// add the create file route and verify file route
app.route("/", create);
app.route("/", verify);
 
export default app;
 

6단계:배포

Cloudflare Workers에 Keyflow 애플리케이션을 배포하세요:

  1. 랭글러 설치:

    npm install -g wrangler
  2. Cloudflare로 인증:

    wrangler login
  3. 작업자 배포:

    wrangler deploy

7단계:배포

이제 애플리케이션이 준비되었으므로 Cloudflare Workers에 배포해 보겠습니다.

  1. Wrangler CLI가 설치되어 있는지 확인하세요:

    npm install -g wrangler
    
  2. Cloudflare 계정으로 인증하세요:

    wrangler login
    
  3. 작업자 배포:

    wrangler deploy
    

8단계:API 테스트

새로 배포된 API를 테스트해 보겠습니다.

새 API 키 생성

curl -X POST https://keyflow.<your-subdomain>.workers.dev/keys/create \
 -H "Content-Type: application/json" \
 -d '{
 "apiId": "my-api",
 "prefix": "prod",
 "name": "Production API Key",
 "expires": 1735689600000,
 "meta": {
 "environment": "production",
 "team": "backend"
 }
 }'

API 키 확인

curl -X POST https://keyflow.<your-subdomain>.workers.dev/keys/verify \
 -H "Content-Type: application/json" \
 -d '{
 "key": "prod_AbC123XyZ..."
 }'

<your-subdomain> 바꾸기  Cloudflare Workers 하위 도메인 및 prod_AbC123XyZ...  생성 엔드포인트에서 생성된 실제 키를 사용합니다.

결론

API 키 생성기를 만드는 것은 애플리케이션의 API를 보호하고 서비스에 액세스할 수 있는 사람을 관리하는 핵심 단계입니다. API 키를 생성, 검증, 관리할 수 있는 시스템을 구축함으로써 데이터를 안전하게 정리하는 데 도움이 되는 강력한 액세스 제어 계층을 추가할 수 있습니다.

다음은 견고한 API 키 생성기에 들어가는 내용에 대한 간략한 요약입니다:

  1. 보안 키 생성 :맞춤 접두사 또는 길이 설정과 같은 옵션이 포함된 고유하고 안전한 키는 각 키를 구별하고 추측하기 어렵게 만듭니다.
  2. 확인 및 만료 :확인 및 만료 날짜를 추가하면 각 키가 설정된 시간 동안만 유효한지 확인하여 필요에 따라 액세스를 쉽게 제어하고 제한할 수 있습니다.
  3. 메타데이터 및 속도 제한 :각 키와 함께 추가 정보를 저장하고 속도 제한을 설정하면 키 사용을 모니터링하고 활동을 추적하며 API 남용을 방지할 수 있습니다.

Upstash Redis 및 Cloudflare Workers와 같은 도구를 사용하면 확장성이 뛰어나고 효율적으로 작동하는 서버리스 및 전 세계적으로 분산된 키 관리 시스템을 쉽게 구축할 수 있습니다.

이러한 기반을 통해 API에 대한 액세스를 안전하고 관리 가능하게 유지함으로써 리소스가 보호되고 쉽게 모니터링할 수 있다는 확신을 갖게 됩니다.