Stripeで大量のクレジットカード登録やカード番号の使い回しを検知・ブロックする方法

この記事では、Stripeを使って構築したECサイトやサブスクリプションサービスにおいて、クレジットカード情報の使い回しや多重登録を防止する方法を紹介します。Stripe APIやWebhook、ダッシュボードを使った対策とそのメリットデメリットを3種類まとめました。長期間のトライアルを提供しようとしているユーザーや、クレジットカードマスター・カードテスティング攻撃への対策を検討されている方は、ぜひ検討内容の参考資料としてお使いください。

## なぜカードの過剰な複数登録や使い回しへの対策が必要なのか

カードの過剰な複数登録や使い回しへの対策は、オンラインサービスの健全性と安全性を維持するために非常に重要です。この対策を行うことで、次のようなメリットが生まれます。

1.**不正利用のリスク軽減：**
   同一のクレジットカードが複数のアカウントで使用されると、盗難されたカード情報や不正に入手されたカード情報が使用されている可能性が高まります。これは、カードの所有者や他の顧客に金銭的損害を与える可能性があるだけでなく、サービス提供者の評判にも悪影響を及ぼす可能性があります。

2.**サービスの不正利用防止：**
   特に無料トライアルやキャンペーンを提供しているサービスでは、同一ユーザーが複数のアカウントを作成し、何度も特典を受けようとする場合があります。これはサービスの公平性を損ない、正規ユーザーの利益を害する可能性があります。

3.**クレジットカードマスター攻撃の防止：**
   攻撃者が大量のクレジットカード番号を試し、有効なものを見つけ出そうとする「クレジットカードマスター攻撃」を防ぐことができます。これにより、盗難されたカード情報の不正利用を防ぎ、カード所有者を保護することができます。

この記事では、これらの対策方法として、以下の3つの方法を紹介します。

-  Customerに登録されたPayment Methodを事前に調査する
-  Card fingerprintとStripe Webhookを利用して使い回しを検知する
-  Radar for Teamsを利用したコードを書かずに行う使い回し検知

## 1: Customerに登録されたPayment Methodを事前に調査する

1つ目の方法は、カード情報を保存するページ・ステップにて実施します。カード情報を保存する際に利用する[Setup Intents API](https://docs.stripe.com/payments/setup-intents?locale=ja-JP)を実行する前に、対象の顧客に登録されている決済手段の情報をAPIでチェックします。もし事前に規定した数値以上の決済手段を登録しようとしている様子であれば、Setup Intents APIを作成する処理をエラーで中断させるようにします。


ユーザーごとに登録できるカードの数を制限したい場合は、Payment Methods APIのリストメソッドを使用して、現在のカード数をチェックします。

```javascript
const MAX_CARDS_PER_CUSTOMER = 3;

app.get('/create_setup_intent', async (req, res) => {
  const stripe = new Stripe('sk_test_...');
  const customerId = 'cus_...';

  try {
    // 既存のカード数をチェック
    const paymentMethods = await stripe.paymentMethods.list({
      type: 'card',
      customer: customerId,
      limit: MAX_CARDS_PER_CUSTOMER - 1,
    });

    if (paymentMethods.has_more) {
      return res.status(400).json({
        message: 'カード登録数の上限に到達しました。新しいカードを保存したい場合は、使用しないカードを削除してください。'
      });
    }

    // 上限に達していない場合、新しいSetupIntentを作成
    const setupIntent = await stripe.setupIntents.create({
      customer: customerId,
    });
    res.json({ client_secret: setupIntent.client_secret });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
```

:::note warn
この方法では、同時に複数の端末やブラウザから操作された場合に、上限を超えたカード登録が可能になる場合があります。より厳密な制御が必要な場合は、データベースのトランザクション機能などを使用して、カード登録操作の状態を管理することをお勧めします。
:::

## 2: Card fingerprintとStripe Webhookを利用して使い回しを検知する


Stripeは各カードに対して一意のフィンガープリントを生成します。このフィンガープリントを使用して、同じカードが複数のアカウントで使用されているかを検知できます。

https://support.stripe.com/questions/how-can-i-detect-duplicate-cards-or-bank-accounts?locale=ja-JP

例えばカード情報の登録が行われた際にトリガーされる`payment_method.attached`のWebhookイベントを利用して、次のような処理が作れます。この処理では、すでに登録済みのカードで別の顧客が過去に決済を行なっていた場合、顧客や社内への通知やフラグ立て・[デフォルトの支払い方法に登録する処理の中断](https://docs.stripe.com/payments/checkout/subscriptions/update-payment-details#set-default-payment-method)などの措置を入れることができます。

```javascript
app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, 'whsec_...');
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'payment_method.attached') {
    const paymentMethod = event.data.object;
    if (paymentMethod.type === 'card') {
      const fingerprint = paymentMethod.card.fingerprint;
      // フィンガープリントを使用して重複チェックを行う
      await checkDuplicateCard(fingerprint, paymentMethod.customer);
    }
  }

  res.json({received: true});
});

async function checkDuplicateCard(fingerprint, customerId) {
  const charges = await stripe.charges.search({
    query: `payment_method_details.card.fingerprint:"${fingerprint}" -customer:"${customerId}"`,
  });

  if (charges.data.length > 0) {
    console.log('このカードは他の顧客によって既に使用されています');
    // ここで適切な処理を行う（例: 警告の送信、カードの削除など）
  }
}
```

:::note warn
この方法では、カード登録自体は防止できず、また決済が行われていないカードは検出できません。トライアル対策として使用する場合は、customer_id と fingerprint の対応を保存するデータストアが必要になります。
:::

## 3: Radar for Teamsを利用したコードを書かずに行う使い回し検知



Stripe Radar for Teamsを使用すると、より高度なルールを設定してカードの使い回しを防止できます。

### 同じカードの使い回し検知

`email_count_for_card_all_time` 属性を使用して、1枚のカードに紐づく異なるメールアドレスの数を制限するルールを設定できます。

例: 同じカードに3個以上のメールアドレスが紐づいた場合にレビューまたはブロックする

```
Block if :email_count_for_card_all_time: >= 3
```

### 複数カードの同一人物判断

`card_count_for_ip_address_all_time` 属性を使用して、同一IPアドレスから登録されたカードの数を制限するルールを設定できます。

例: 1つのIPアドレスから5枚以上のカードが登録された場合にレビューする

```
Review if :card_count_for_ip_address_all_time: >= 5
```

### 過去に不正利用されたカードのブロック

過去に不正利用の履歴があるカードを自動的にブロックするルールも設定可能です。

```
Block if :total_customers_with_prior_fraud_activity_for_card_yearly: > 0
```

:::note warn
Radar for Teamsは有料機能ですが、テスト環境では無料で試すことができます。また、Radarアシスタントを使用してルールのテストが可能です。

![スクリーンショット 2024-10-23 13.21.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/53a8cbca-4c8d-100b-157f-d69134d39766.png)

:::


## まとめ

ECサイトやサブスクリプションサービスでは、ユーザーによる運営者が意図しないサービスの使い方を行うことや、クレジットカードの不正利用を企む攻撃者のターゲットに選ばれるリスクが存在します。そして運営者に損害を与えるユースケースの中には、同じクレジットカード情報を複数のアカウントで利用するケースやクレジットカードの登録機能を利用して、アクティブなクレジットカード番号を調べるような攻撃（クレジットカードマスター攻撃）などが存在します。

これらの意図しないユースケースや攻撃を防ぐには、ストア・サービスのアカウント１つあたりに登録できるクレジットカードの数を制限したり、同じクレジットカード番号がすでに使われているかどうかを検証する仕組みを実装することが有効です。そしてStripeでは、Customerデータを利用した組み込みや、Stripeがクレジットカードに対して発行するフィンガープリントを活用することでこれらの対策が行えます。

今回紹介した対策方法には、それぞれメリットとデメリットが存在します。ぜひこの記事を読み返して、あなたのビジネスケースや課題にマッチする対策がどれかを考えてみてください。