Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Upstash Redis と Cloudflare Workers を使用して安全な API キーを作成する:ステップバイステップ ガイド

API キーはサービスへの玄関の鍵のようなもので、安全な状態を保ちながらユーザーをアクセスさせます。このブログでは、高速なサーバーレスデータストレージとして Upstash Redis を使用し、エッジでリクエストを処理するために Cloudflare Workers を使用して、シンプルで安全な API キージェネレーターを構築する方法を説明します。新しいサービスを設定する場合でも、既存のアプリにキーを追加する場合でも、すべてをスムーズかつ効率的に実行し続けるために API キーを生成、保存、検証する方法を学びます。

API キーとは何ですか?

API キーは、API にアクセスしようとするユーザーまたはアプリケーションを識別および認証する一意のコードです。これは個人パスのようなものだと考えてください。誰かがあなたのサービスを使用したいとき、その人がアクセスを許可されていることを証明するためにこの「キー」を提示する必要があります。API キーは、リソースにアクセスできるユーザーを制御するのに役立ち、使用状況の追跡、制限の強制、または不正アクセスの防止によく使用されます。 API アクセスを管理し、データを安全に保つための簡単な方法です。

何を構築するか

このガイドでは、次の 2 つのコア機能を提供する API キー ジェネレーターを作成します。

<オル>
  • カスタム設定による新しい API キーの生成
  • メタデータを取得しながら 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 :Workers の開発と展開のための Cloudflare の CLI

    Upstash Redis をセットアップする

    <オル>
  • Upstash アカウントにログインし、新しい Redis データベースを作成します
  • Upstash Redis と Cloudflare Workers を使用して安全な API キーを作成する:ステップバイステップ ガイド

    <オル>
  • 作成したら、「REST API」セクションに移動します
  • Upstash Redis と Cloudflare Workers を使用して安全な API キーを作成する:ステップバイステップ ガイド

    <オル>
  • 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 ルートを作成しましょう。このファイルは、2 つのメイン ルートを使用して Hono アプリケーションを設定します:/keys/create  新しい API キーと /keys/verify を生成するため  3 つの個別のファイルを使用して既存のキーを検証します。

    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:導入

    Keyflow アプリケーションを Cloudflare Workers にデプロイします。

    <オル>
  • Wrangler をインストールします:

    npm install -g wrangler
  • Cloudflare で認証します:

    wrangler login
  • ワーカーをデプロイします:

    wrangler deploy
  • ステップ 7:導入

    アプリケーションの準備ができたので、Cloudflare Workers にデプロイしましょう。

    <オル>
  • Wrangler CLI がインストールされていることを確認してください。

    npm install -g wrangler
    
  • Cloudflare アカウントで認証します:

    wrangler login
    
  • ワーカーをデプロイします:

    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 キー ジェネレーターの内容を簡単にまとめます。

    <オル>
  • 安全なキーの生成 :カスタム プレフィックスや設定された長さなどのオプションを含む独自の安全なキーにより、各キーが区別され、推測が困難になります。
  • 検証と有効期限 :チェックと有効期限を追加すると、各キーが設定された期間のみ有効であることが保証され、必要に応じてアクセスを制御および制限することが容易になります。
  • メタデータとレート制限 :各キーに追加情報を保存し、レート制限を設定すると、キーの使用状況を監視し、アクティビティを追跡し、API の悪用を回避できます。
  • Upstash Redis や Cloudflare Workers などのツールを使用すると、拡張性が高く効率的に動作する、サーバーレスでグローバルに分散された鍵管理システムを簡単に構築できます。

    この基盤により、API へのアクセスを安全かつ管理しやすくするように設定され、リソースが保護され、監視が容易になるという安心感を得ることができます。


    1. Redis HGETALL –ハッシュ値に含まれるすべてのフィールド/値のペアを取得する方法

      このチュートリアルでは、キーに格納されているハッシュ値に含まれるすべてのフィールドと値のペアを取得する方法について学習します。このために、Redis HGETALLを使用します コマンド。 HGETALLコマンド このコマンドは、キーに格納されているハッシュ値に含まれるすべてのフィールドとそれに関連する値を返します。キーが存在しない場合は空のリストが返され、キーは存在するがキーに格納されている値がハッシュデータ型ではない場合はエラーが返されます。 RedisHGETALLコマンドの構文は次のとおりです。- 構文:- redis host:post> HGETALL <ke

    2. Upstash Redis Python SDK v1.0.0 の紹介 – コネクションレス、サーバーレス対応クライアント

      本日、v1.0.0 をリリースします。 upstash-redis のバージョン Pythonのパッケージ。現在、GitHub と PyPi で利用可能です。 このパッケージは、Upstash Redis 用のコネクションレス型 HTTP ベースのクライアントで、AWS Lambda、Google Cloud Functions などのサーバーレス環境、または TCP よりも HTTP が優先される環境で使用できるように設計されています。 新機能は何ですか? 新しいバージョンでは、パッケージには各コマンドの使用例を含む Python docstring が付属しています。 また、up