Stripe API キー管理を AWS Parameter Store で安全・低コストに実装する方法

Stripe を使った決済システムを構築する際、多くの開発者が API キーの管理方法がわからず、とりあえず動作する方法で実装してしまいがちです。しかし、セキュリティ事故が起きてからでは手遅れになってしまいます。

決済システムは企業の信頼性に直結する重要な部分であり、適切なセキュリティ対策なしに運用することは大きなリスクを伴います。一方で、正しい方法さえ知っていれば、セキュアな実装は思っているより簡単に実現できます。

今回は AWS 環境で Stripe 決済を安全に実装するために、よくある危険なパターンから、コスト効率の良い正しい対策まで、具体的なコードとともに解説していきます。

API キー漏洩の実例

API キーの流出がどれほど深刻な問題かを理解するために、実際に発生した事例を見てみましょう。

GitHub のセキュリティアドバイザリには「Leaked Stripe API Key in Public Code Repository」という報告があります。これによると、パブリックコードリポジトリで Stripe API キーが流出した事例では、即座にキーの無効化と新しいキーの生成、コードベースの徹底的な監査が必要となったと記録されています。

さらに、バグバウンティハンターによる発見事例では、JavaScript ファイル内に埋め込まれた Stripe の本番 API キーにより、攻撃者が内部の決済詳細、すべての顧客の ID とメールアドレス、内部の機密ファイルにアクセスできる状態になった事例も報告されています。

よくある危険なAPIキー管理パターン

どのような実装が危険なのかを理解することで、なぜ専用の管理サービスが必要なのかが明確になります。以下の3つのパターンは、一見手軽に見えても重大なセキュリティリスクを抱えています。

パターン1:ソースコードに直接記述

最も危険なパターンが、API キーをソースコードに直接記述することです。この方法にはいくつかの重要な問題があります。

const stripe = new Stripe('sk_live_実際のAPIキー', {
  apiVersion: '2023-10-16',
});

まず、Git リポジトリにコミットした瞬間、API キーが永続的に記録されてしまいます。後で削除しても、過去のコミット履歴には残り続けるため、リポジトリへのアクセス権限を持つ全ての開発者が API キーを確認できてしまいます。

特にパブリックリポジトリの場合、世界中の誰もが API キーにアクセス可能な状態になります。GitHub などでは API キー のパターンを自動検出してアラートを送信する機能もありますが、発見が遅れれば深刻な被害につながりかねません。

また、コードレビューやペアプログラミングの際にも API キーが露出してしまうため、本来アクセス権限を持たない開発者にも機密情報が共有されてしまいます。

パターン2:.envファイルでの管理

.envファイルでの API キー管理は、現在最も一般的な方法の一つですが、実はいくつかの落とし穴があります。

# .env
STRIPE_SECRET_KEY=sk_live_実際のAPIキー
STRIPE_PUBLISHABLE_KEY=pk_live_実際の公開キー
DATABASE_URL=postgresql://user:password@localhost:5432/myapp

多くのプロジェクトでは、.envファイル自体を .gitignore に追加し、代わりに .env.example をコミットする運用を行っています。これは一見安全に見えますが、実際の開発現場では様々な問題が発生します。

最も頻繁に起こるのが、開発者による .env ファイルの誤コミットです。「すべての開発者が少なくとも一度は、意図しないものを Git に誤ってコミットしている」という指摘があるように、新しいプロジェクトメンバーが .gitignore の存在に気づかなかったり、Git の設定ミスにより .env ファイルがリポジトリに含まれてしまうケースが後を絶ちません。実際に GitHub コミュニティでは「.env ファイルを編集し忘れて API を流出させてしまった」という相談が投稿されていたりもします。

Docker 環境での問題も深刻です。2023年のドイツの研究者による大規模調査では、Docker Hub の337,171個のイメージを分析した結果、約8.5%が機密データを含んでいることが判明しました。この調査では28,621個の Docker イメージで52,107個の有効な秘密鍵と3,158個の異なる API シークレットの露出が発見され、その中には Stripe などの金融サービスの API キーも含まれていました。

パターン3:Lambda環境変数への直接設定

AWS Lambda の環境変数に直接 API キーを設定する方法は、クラウド環境では一般的に見かけますが、実は大きなセキュリティホールとなっています。

問題となるのは、Lambda の環境変数が AWS コンソール上で平文表示されることです。AWS の IAM 権限で lambda:GetFunction が許可されていれば、誰でも環境変数の内容を確認できてしまいます。多くの組織では、開発者に Lambda 関数の設定変更権限を与えているため、結果的に多数の人が API キーにアクセス可能な状態になります。

この問題を避けるためには、環境変数ではなく、暗号化された状態で機密情報を管理できる専用サービスを使用する必要があります。

なぜ多くの開発者が API キー管理で困るのか

API キー管理が適切に行われない根本的な理由は、利用するインフラや SaaS ごとに最適な実装方法がわからないことです。

まず、機能開発に比べてセキュリティ対策の情報は断片的で、体系的な解説が少ないことが挙げられます。Stripe の公式ドキュメントは決済処理の実装方法は詳しく説明されていますが、AWS、Google Cloud、Azure など各クラウド環境での安全な API キー管理については、物理的にすべてのパターンを網羅できないため、一般論的な案内に留まっています。

AWS 環境での機密情報管理:Parameter Store vs Secrets Manager

AWS には機密情報を管理するための主要なサービスが2つあります。多くの開発者が迷うポイントでもあるので、詳しく比較してみましょう。

Parameter Store は月額無料から利用でき、API キーのような比較的静的な値の保存に適しています。一方、Secrets Manager は月額 $0.40 からで、データベースのパスワードなど定期的にローテーションが必要な認証情報の管理に特化しています。

Stripe の API キーは一度設定すれば頻繁に変更することはないため、Parameter Store を選択することで大幅にコストを抑えられます。実際の運用コストを計算してみると、月間100万リクエストの場合、Parameter Store なら約 $5、Secrets Manager だと約 $50.40 になります。年間で考えると $544 の差額です。

中小企業やスタートアップにとって、この金額差は決して無視できません。

Parameter Store を使った安全な Stripe 実装

それでは実際に、Parameter Store を使用したセキュアな実装方法を見ていきましょう。

ステップ1: API キーの安全な保存

まず、AWS CLI を使って API キーを Parameter Store に保存します。

# 本番環境のAPIキー保存
aws ssm put-parameter \
  --name "/myapp/stripe/api-key" \
  --value "sk_live_実際のStripeキー" \
  --type "SecureString" \
  --description "Stripe API Key for Production"

ここで重要なのは SecureString を指定することです。これにより、API キーは AWS KMS で自動的に暗号化されて保存されます。

ステップ2: AWS CDK でのインフラ構築

次に、CDK を使って Lambda 関数と必要な権限設定を行います。

import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as ssm from 'aws-cdk-lib/aws-ssm';
import { Construct } from 'constructs';
import * as nodejs from 'aws-cdk-lib/aws-lambda-nodejs';

export class StripePaymentStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdk.StackProps) {
    super(scope, id, props);

    const parameterName = '/myapp/stripe/api-key';

    const stripeApiKeyParam = ssm.StringParameter.fromStringParameterName(
      this,
      'StripeApiKeyParam',
      parameterName
    );

    const stripePaymentFunction = new nodejs.NodejsFunction(this, 'StripePaymentFunction', {
      runtime: lambda.Runtime.NODEJS_22_X,
      entry: 'src/handlers/stripe-payment.ts',
      environment: {
        STRIPE_API_KEY_PARAM_NAME: parameterName,
      },
      timeout: cdk.Duration.seconds(30),
    });

    // この一行で必要最小限の権限が自動設定される
    stripeApiKeyParam.grantRead(stripePaymentFunction);
  }
}

CDK の grantRead() メソッドが優秀で、Lambda 関数に必要最小限の IAM 権限を自動的に付与してくれます。手動で IAM ポリシーを書く必要がないため、設定ミスによるセキュリティホールを防げます。

ステップ3: Lambda 関数での安全なAPIキー取得

最後に、Lambda 関数内で Parameter Store から API キーを取得する実装を行います。

import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm';
import Stripe from 'stripe';

const ssmClient = new SSMClient({ region: process.env.AWS_REGION });
let stripeClient: Stripe | null = null;

async function getStripeApiKey(): Promise<string> {
  const parameterName = process.env.STRIPE_API_KEY_PARAM_NAME;
  
  if (!parameterName) {
    throw new Error('Parameter name not configured');
  }

  const command = new GetParameterCommand({
    Name: parameterName,
    WithDecryption: true,
  });

  const response = await ssmClient.send(command);
  
  if (!response.Parameter?.Value) {
    throw new Error('API key not found');
  }

  return response.Parameter.Value;
}

async function getStripeClient(): Promise<Stripe> {
  if (stripeClient) {
    return stripeClient;
  }

  const apiKey = await getStripeApiKey();
  stripeClient = new Stripe(apiKey, {
    apiVersion: '2023-10-16',
    typescript: true,
  });

  return stripeClient;
}

export const handler = async (event: any) => {
  try {
    const { amount } = JSON.parse(event.body);
    
    const stripe = await getStripeClient();
    
    const paymentIntent = await stripe.paymentIntents.create({
      amount,
      currency: 'jpy',
      payment_method_types: ['card'],
    });

    return {
      statusCode: 200,
      body: JSON.stringify({
        clientSecret: paymentIntent.client_secret,
      }),
    };
  } catch (error) {
    console.error('Payment error:', error);
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'Payment processing failed' }),
    };
  }
};

この実装のポイントは、Stripe クライアントをグローバル変数でキャッシュしていることです。Lambda の実行コンテナが再利用される場合、API キーの取得処理をスキップできるため、レスポンス時間の短縮につながります。

運用で気をつけるべきポイント

実装が完了しても、運用面で注意すべき点がいくつかあります。

エラーハンドリングでは、機密情報がログに出力されないよう注意が必要です。Parameter Store のレスポンスをデバッグする際などは、API キーがログに残らないようにマスクする処理を追加するようにしましょう。

API キーのローテーションも重要な運用作業です。Stripe でキーを再生成した際は、以下のコマンドで Parameter Store を更新するだけで済みます。

aws ssm put-parameter \
  --name "/myapp/stripe/api-key" \
  --value "新しいAPIキー" \
  --type "SecureString" \
  --overwrite

Lambda 関数は次回実行時に自動的に新しいキーを取得するため、アプリケーションの再デプロイは不要です。

API キーの権限制限による多層防御戦略

Parameter Store を使った安全な保存に加えて、Stripe の Restricted API Key(制限付き API キー)を活用することで、さらに強固なセキュリティ体制を構築できます。これは万が一 API キーが流出した場合でも、被害を最小限に抑える重要な防御策です。

制限付き API キーとは

制限付きキーを使用することで、特にサードパーティにアクセスを許可する際に、必要なリソースへの最小限のアクセスのみを許可し、キーのリスクを制限できます。標準の Secret Key がすべての Stripe API にフルアクセスできるのに対し、Restricted API Key は指定した機能のみに限定されます。

例えば、決済処理のみを行うアプリケーションであれば、Charges の作成権限のみを与え、顧客データの削除や返金処理の権限は除外することで、攻撃者ができる操作を大幅に制限できます。

迅速なキーローテーション戦略

マイクロサービスを使用してあなたの代わりに API と相互作用する場合、それらのマイクロサービスが必要とする最小限のアクセスのみを許可する制限付きキーを定義します。この原則により、各サービスごとに独立した制限付きキーを使用することで、セキュリティインシデント発生時に影響範囲を局所化できます。

例えば、決済処理サービスの API キーが漏洩した場合でも、顧客管理サービスや分析サービスは影響を受けません。さらに、該当するキーのみを無効化すれば良いため、システム全体を停止することなく迅速な対応が可能になります。

まとめ

Stripe API キーの管理は、Parameter Store を使うことで思っているより簡単に、そして安全に実装できます。

今回解説した方法の最大のメリットは、セキュリティとコストの両立です。Secrets Manager と比較して年間約 $544 のコスト削減ができるだけでなく、CDK の grantRead() メソッドによって IAM 権限の設定ミスも防げます。手動でポリシーを書く必要がないため、セキュリティホールが生まれにくい実装になります。

Parameter Store なら API キーの変更時もアプリケーションの再デプロイは不要です。Stripe でキーを再生成したら、AWS CLI で Parameter Store を更新するだけで、Lambda 関数は次回実行時に自動的に新しいキーを取得してくれます。

さらに、Stripe の Restricted API Key と組み合わせることで、万が一の流出時も被害を最小限に抑えられます。決済処理のみの権限に制限しておけば、攻撃者ができる操作を大幅に限定できるからです。

何より重要なのは、後回しにしないことです。セキュリティ事故が起きてからでは、技術対応だけでなく顧客への説明や信頼回復など、本来の開発以外に膨大な時間を取られてしまいます。

まずはテスト環境で実際に Parameter Store と Lambda の連携を試してみてください。30分程度で基本的な実装は完了するはずです。動作確認ができたら、段階的に本番環境に適用していけば、リスクを抑えながらセキュリティを強化できます。

Stripe を活用したオンライン決済システム導入をサポートします

株式会社デジタルキューブは Stripe 公式パートナーとして、自社サービス開発で培った経験を活かし、Stripe を用いた決済システムの導入を支援します。多言語対応の決済フォーム実装、モバイル支払い対応、柔軟な従量課金システム構築など、ユースケースに合わせた活用方法の提案から、専用ダッシュボードの開発まで幅広くサポートします。

SaaS ダッシュボード開発や EC サイトへの Stripe 決済導入など、様々な導入実績があります。 API の呼び出しだけで決済機能を実装できる Stripe の利点を最大限に活かしたシステム構築をお手伝いします。


Recommend Articles

おすすめの記事