Skip to content

APIキー認証

必要な依存関係: io.ktor:ktor-server-auth, io.ktor:ktor-server-auth-api-key

Nativeサーバー
Ktor supports Kotlin/Native and allows you to run a server without an additional runtime or virtual machine.
のサポート: ✅

APIキー認証は、クライアントがリクエストの一部(通常はヘッダー)として秘密鍵を渡す、シンプルな認証方法です。このキーは識別子と認証メカニズムの両方の役割を果たします。

Ktorでは、ルートを保護し、クライアントのリクエストを検証するためにAPIキー認証を使用できます。

Ktorにおける認証の全般的な情報は、Ktor Serverでの認証と認可セクションで確認できます。

APIキーは機密として扱い、安全に送信する必要があります。転送中のAPIキーを保護するために、HTTPS/TLSを使用することをお勧めします。

依存関係の追加

APIキー認証を有効にするには、ビルドスクリプトに ktor-server-authktor-server-auth-api-key アーティファクトを追加します。

kotlin
implementation("io.ktor:ktor-server-auth-api-key:$ktor_version")
implementation("io.ktor:ktor-server-auth:$ktor_version")
Groovy
implementation "io.ktor:ktor-server-auth-api-key:$ktor_version"
implementation "io.ktor:ktor-server-auth:$ktor_version"
xml
<dependency>
    <groupId>io.ktor</groupId>
    <artifactId>ktor-server-auth-api-key-jvm</artifactId>
    <version>${ktor_version}</version>
</dependency>
<dependency>
    <groupId>io.ktor</groupId>
    <artifactId>ktor-server-auth</artifactId>
    <version>${ktor_version}</version>
</dependency>

APIキー認証のフロー

APIキー認証のフローは以下の通りです。

  1. クライアントが、サーバーアプリケーションの特定のルートに対して、ヘッダー(通常は X-API-Key)にAPIキーを含めてリクエストを送信します。
  2. サーバーはカスタムの検証ロジックを使用してAPIキーを検証します。
  3. キーが有効な場合、サーバーはリクエストされたコンテンツを応答します。キーが無効または欠落している場合、サーバーは 401 Unauthorized ステータスを応答します。

APIキー認証のインストール

apiKey 認証プロバイダーをインストールするには、install(Authentication) ブロック内で apiKey 関数を呼び出します。

kotlin
import io.ktor.server.application.*
import io.ktor.server.auth.*
// ...
install(Authentication) {
    apiKey {
        // APIキー認証を設定する
    }
}

オプションで、プロバイダー名を指定して、特定のルートを認証するために使用することもできます。

APIキー認証の設定

このセクションでは、apiKey 認証プロバイダーの具体的な設定について説明します。

Ktorでさまざまな認証プロバイダーを設定する方法については、認証の設定を参照してください。

ステップ 1: APIキープロバイダーの設定

apiKey 認証プロバイダーの設定は、ApiKeyAuthenticationProvider.Config クラスを通じて公開されます。以下の例では、次の設定が指定されています。

  • validate 関数はリクエストから抽出されたAPIキーを受け取り、認証に成功した場合は Principal を返し、失敗した場合は null を返します。

最小限の例を次に示します。

kotlin
data class AppPrincipal(val key: String) : Principal

install(Authentication) {
    apiKey {
        validate { keyFromHeader ->
            val expectedApiKey = "this-is-expected-key"
            keyFromHeader
                .takeIf { it == expectedApiKey }
                ?.let { AppPrincipal(it) }
        }
    }
}

キーの場所のカスタマイズ

デフォルトでは、apiKey プロバイダーは X-API-Key ヘッダーからAPIキーを探します。

headerName を使用してカスタムヘッダーを指定できます。

kotlin
apiKey("api-key-header") {
    headerName = "X-Secret-Key"
    validate { key ->
        // ...
    }
}

ステップ 2: APIキーの検証

検証ロジックはアプリケーションの要件によって異なります。一般的なアプローチは以下の通りです。

静的なキーの比較

単純なケースでは、定義済みのキーと比較できます。

kotlin
apiKey {
    validate { keyFromHeader ->
        val expectedApiKey = environment.config.property("api.key").getString()
        keyFromHeader
            .takeIf { it == expectedApiKey }
            ?.let { AppPrincipal(it) }
    }
}

機密性の高いAPIキーは、ソースコードではなく設定ファイルや環境変数に保存してください。

データベースでの検索

複数のAPIキーがある場合は、データベースに対して検証します。

kotlin
apiKey {
    validate { keyFromHeader ->
        // データベースでキーを検索する
        val user = database.findUserByApiKey(keyFromHeader)
        user?.let { UserIdPrincipal(it.username) }
    }
}

複数の検証基準

複雑な検証ロジックを実装できます。

kotlin
apiKey {
    validate { keyFromHeader ->
        val apiKey = database.findApiKey(keyFromHeader)

        // キーが存在し、アクティブで、期限切れでないかを確認する
        if (apiKey != null &&
            apiKey.isActive &&
            apiKey.expiresAt > Clock.System.now()
        ) {
            UserIdPrincipal(apiKey.userId)
        } else {
            null
        }
    }
}

ステップ 3: チャレンジの設定

challenge 関数を使用して、認証が失敗したときに送信されるレスポンスをカスタマイズできます。

kotlin
apiKey {
    validate { key ->
        // 検証ロジック
    }
    challenge { defaultScheme, realm ->
        call.respond(
            HttpStatusCode.Unauthorized,
            "Invalid or missing API key"
        )
    }
}

ステップ 4: 特定のリソースの保護

apiKey プロバイダーを設定した後、authenticate 関数を使用してアプリケーション内の特定のリソースを保護できます。認証に成功した場合、ルートハンドラー内で call.principal 関数を使用して認証済みのプリンシパルを取得できます。

kotlin
routing {
    authenticate {
        get("/") {
            val principal = call.principal<AppPrincipal>()!!
            call.respondText("Hello, authenticated client! Your key: ${principal.key}")
        }
    }
}

APIキー認証の例

APIキー認証の完全な最小構成の例は以下の通りです。

kotlin
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

data class AppPrincipal(val key: String) : Principal

fun Application.module() {
    val expectedApiKey = "this-is-expected-key"

    install(Authentication) {
        apiKey {
            validate { keyFromHeader ->
                keyFromHeader
                    .takeIf { it == expectedApiKey }
                    ?.let { AppPrincipal(it) }
            }
        }
    }

    routing {
        authenticate {
            get("/") {
                val principal = call.principal<AppPrincipal>()!!
                call.respondText("Key: ${principal.key}")
            }
        }
    }
}

ベストプラクティス

APIキー認証を実装する際は、以下のベストプラクティスを考慮してください。

  1. HTTPSを使用する: 傍受を防ぐため、APIキーは常にHTTPS経由で送信してください。
  2. 安全に保存する: APIキーをソースコードにハードコードしないでください。環境変数または安全な設定管理を使用してください。
  3. キーのローテーション: APIキーを定期的にローテーションするためのメカニズムを実装してください。
  4. レート制限: 悪用を防ぐために、APIキー認証をレート制限(Rate limiting)と組み合わせてください。
  5. ロギング: セキュリティ監視のために認証の失敗をログに記録しますが、実際のAPIキーは決してログに記録しないでください。
  6. キーの形式: APIキーには暗号学的に安全なランダム文字列(例:UUIDやBase64エンコードされたランダムバイト)を使用してください。
  7. 複数のキー: アプリケーションや目的ごとに、ユーザーあたり複数のAPIキーをサポートすることを検討してください。
  8. 有効期限: セキュリティを強化するために、キーの有効期限を実装してください。