mcpField
P
pci-compliance

pci-compliance

github.com/wshobsonセキュリティ41.9k10.7k 回インストール2 週間前に更新
リポジトリ

pci-compliance について

決済システムの構築やカード会員データの取り扱いに携わるチーム向けの PCI DSS ガイドです。要件、準拠レベル、データ最小化、トークン化、暗号化のパターンを扱います。

主な機能

  1. 01PCI DSS 要件の概要
  2. 02準拠レベルの参照
  3. 03カード会員データの最小化
  4. 04決済トークン化のパターン
  5. 05保存時と転送時の暗号化

活用シーン

  1. 01安全な決済フローの設計
  2. 02PCI DSS 評価の準備
  3. 03カードデータの露出削減

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshobson/agents --skill pci-compliance

インストール後、Claude Code で/pci-complianceを入力して読み込まれたか確認します。

同梱ファイルreferences/details.md

詳細

PCI コンプライアンス

安全な決済処理とカード会員データの取り扱いに向けた PCI DSS(Payment Card Industry Data Security Standard)コンプライアンスを習得します。

このスキルを使用する場面

  • 決済処理システムを構築するとき
  • クレジットカード情報を取り扱うとき
  • 安全な決済フローを実装するとき
  • PCI コンプライアンス監査を実施するとき
  • PCI コンプライアンスの対象範囲を縮小するとき
  • トークン化と暗号化を実装するとき
  • PCI DSS 評価の準備をするとき

PCI DSS の要件(12 の中核要件)

安全なネットワークの構築と維持
  1. ファイアウォール設定を導入し、維持する
  2. ベンダー提供のデフォルトパスワードを使用しない
カード会員データの保護
  1. 保存されたカード会員データを保護する
  2. 公衆ネットワーク上でのカード会員データの送信を暗号化する
脆弱性管理の維持
  1. システムをマルウェアから保護する
  2. 安全なシステムとアプリケーションを開発し、維持する
強力なアクセス制御の実装
  1. 業務上の必要性(need-to-know)に基づいてカード会員データへのアクセスを制限する
  2. システムコンポーネントへのアクセスを識別し、認証する
  3. カード会員データへの物理的なアクセスを制限する
ネットワークの監視とテスト
  1. ネットワークリソースおよびカード会員データへのすべてのアクセスを追跡し、監視する
  2. セキュリティシステムとプロセスを定期的にテストする
情報セキュリティポリシーの維持
  1. 情報セキュリティに対応するポリシーを維持する

コンプライアンスレベル

レベル 1: 年間 600 万件超の取引(年次 ROC が必要) レベル 2: 年間 100 万~600 万件の取引(年次 SAQ) レベル 3: 年間 20,000~100 万件の e コマース取引 レベル 4: e コマース取引が 20,000 件未満、または総取引が 100 万件未満

データ最小化(決して保存しない)

# NEVER STORE THESE
PROHIBITED_DATA = {
    'full_track_data': 'Magnetic stripe data',
    'cvv': 'Card verification code/value',
    'pin': 'PIN or PIN block'
}

# CAN STORE (if encrypted)
ALLOWED_DATA = {
    'pan': 'Primary Account Number (card number)',
    'cardholder_name': 'Name on card',
    'expiration_date': 'Card expiration',
    'service_code': 'Service code'
}

class PaymentData:
    """Safe payment data handling."""

    def __init__(self):
        self.prohibited_fields = ['cvv', 'cvv2', 'cvc', 'pin']

    def sanitize_log(self, data):
        """Remove sensitive data from logs."""
        sanitized = data.copy()

        # Mask PAN
        if 'card_number' in sanitized:
            card = sanitized['card_number']
            sanitized['card_number'] = f"{card[:6]}{'*' * (len(card) - 10)}{card[-4:]}"

        # Remove prohibited data
        for field in self.prohibited_fields:
            sanitized.pop(field, None)

        return sanitized

    def validate_no_prohibited_storage(self, data):
        """Ensure no prohibited data is being stored."""
        for field in self.prohibited_fields:
            if field in data:
                raise SecurityError(f"Attempting to store prohibited field: {field}")

トークン化

決済プロセッサのトークンを使用する
import stripe

class TokenizedPayment:
    """Handle payments using tokens (no card data on server)."""

    @staticmethod
    def create_payment_method_token(card_details):
        """Create token from card details (client-side only)."""
        # THIS SHOULD ONLY BE DONE CLIENT-SIDE WITH STRIPE.JS
        # NEVER send card details to your server

        """
        // Frontend JavaScript
        const stripe = Stripe('pk_...');

        const {token, error} = await stripe.createToken({
            card: {
                number: '4242424242424242',
                exp_month: 12,
                exp_year: 2024,
                cvc: '123'
            }
        });

        // Send token.id to server (NOT card details)
        """
        pass

    @staticmethod
    def charge_with_token(token_id, amount):
        """Charge using token (server-side)."""
        # Your server only sees the token, never the card number
        stripe.api_key = "sk_..."

        charge = stripe.Charge.create(
            amount=amount,
            currency="usd",
            source=token_id,  # Token instead of card details
            description="Payment"
        )

        return charge

    @staticmethod
    def store_payment_method(customer_id, payment_method_token):
        """Store payment method as token for future use."""
        stripe.Customer.modify(
            customer_id,
            source=payment_method_token
        )

        # Store only customer_id and payment_method_id in your database
        # NEVER store actual card details
        return {
            'customer_id': customer_id,
            'has_payment_method': True
            # DO NOT store: card number, CVV, etc.
        }
カスタムトークン化(上級)
import secrets
from cryptography.fernet import Fernet

class TokenVault:
    """Secure token vault for card data (if you must store it)."""

    def __init__(self, encryption_key):
        self.cipher = Fernet(encryption_key)
        self.vault = {}  # In production: use encrypted database

    def tokenize(self, card_data):
        """Convert card data to token."""
        # Generate secure random token
        token = secrets.token_urlsafe(32)

        # Encrypt card data
        encrypted = self.cipher.encrypt(json.dumps(card_data).encode())

        # Store token -> encrypted data mapping
        self.vault[token] = encrypted

        return token

    def detokenize(self, token):
        """Retrieve card data from token."""
        encrypted = self.vault.get(token)
        if not encrypted:
            raise ValueError("Token not found")

        # Decrypt
        decrypted = self.cipher.decrypt(encrypted)
        return json.loads(decrypted.decode())

    def delete_token(self, token):
        """Remove token from vault."""
        self.vault.pop(token, None)

暗号化

保存データ
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

class EncryptedStorage:
    """Encrypt data at rest using AES-256-GCM."""

    def __init__(self, encryption_key):
        """Initialize with 256-bit key."""
        self.key = encryption_key  # Must be 32 bytes

    def encrypt(self, plaintext):
        """Encrypt data."""
        # Generate random nonce
        nonce = os.urandom(12)

        # Encrypt
        aesgcm = AESGCM(self.key)
        ciphertext = aesgcm.encrypt(nonce, plaintext.encode(), None)

        # Return nonce + ciphertext
        return nonce + ciphertext

    def decrypt(self, encrypted_data):
        """Decrypt data."""
        # Extract nonce and ciphertext
        nonce = encrypted_data[:12]
        ciphertext = encrypted_data[12:]

        # Decrypt
        aesgcm = AESGCM(self.key)
        plaintext = aesgcm.decrypt(nonce, ciphertext, None)

        return plaintext.decode()

# Usage
storage = EncryptedStorage(os.urandom(32))
encrypted_pan = storage.encrypt("4242424242424242")
# Store encrypted_pan in database
転送中データ
# Always use TLS 1.2 or higher
# Flask/Django example
app.config['SESSION_COOKIE_SECURE'] = True  # HTTPS only
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'

# Enforce HTTPS
from flask_talisman import Talisman
Talisman(app, force_https=True)

その他のパターンとテンプレート

より詳細なテンプレートと実践例は references/details.md にあります。完全なパターンライブラリについては、そのファイルをお読みください。

コメント