mcpField
P
pci-compliance

pci-compliance

github.com/ovachieverセキュリティ5642 回インストール48 週間前に更新
リポジトリ

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/ovachiever/droid-tings --skill pci-compliance

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

詳細

PCI コンプライアンス

安全な決済処理とカード会員データの取り扱いに向けて、PCI DSS(Payment Card Industry Data Security Standard)への準拠を習得します。

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

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

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

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

コンプライアンスレベル

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

アクセス制御

from functools import wraps
from flask import session

def require_pci_access(f):
    """Decorator to restrict access to cardholder data."""
    @wraps(f)
    def decorated_function(*args, **kwargs):
        user = session.get('user')

        # Check if user has PCI access role
        if not user or 'pci_access' not in user.get('roles', []):
            return {'error': 'Unauthorized access to cardholder data'}, 403

        # Log access attempt
        audit_log(
            user=user['id'],
            action='access_cardholder_data',
            resource=f.__name__
        )

        return f(*args, **kwargs)

    return decorated_function

@app.route('/api/payment-methods')
@require_pci_access
def get_payment_methods():
    """Retrieve payment methods (restricted access)."""
    # Only accessible to users with pci_access role
    pass

監査ログ

import logging
from datetime import datetime

class PCIAuditLogger:
    """PCI-compliant audit logging."""

    def __init__(self):
        self.logger = logging.getLogger('pci_audit')
        # Configure to write to secure, append-only log

    def log_access(self, user_id, resource, action, result):
        """Log access to cardholder data."""
        entry = {
            'timestamp': datetime.utcnow().isoformat(),
            'user_id': user_id,
            'resource': resource,
            'action': action,
            'result': result,
            'ip_address': request.remote_addr
        }

        self.logger.info(json.dumps(entry))

    def log_authentication(self, user_id, success, method):
        """Log authentication attempt."""
        entry = {
            'timestamp': datetime.utcnow().isoformat(),
            'user_id': user_id,
            'event': 'authentication',
            'success': success,
            'method': method,
            'ip_address': request.remote_addr
        }

        self.logger.info(json.dumps(entry))

# Usage
audit = PCIAuditLogger()
audit.log_access(user_id=123, resource='payment_methods', action='read', result='success')

セキュリティのベストプラクティス

入力検証
import re

def validate_card_number(card_number):
    """Validate card number format (Luhn algorithm)."""
    # Remove spaces and dashes
    card_number = re.sub(r'[\s-]', '', card_number)

    # Check if all digits
    if not card_number.isdigit():
        return False

    # Luhn algorithm
    def luhn_checksum(card_num):
        def digits_of(n):
            return [int(d) for d in str(n)]

        digits = digits_of(card_num)
        odd_digits = digits[-1::-2]
        even_digits = digits[-2::-2]
        checksum = sum(odd_digits)
        for d in even_digits:
            checksum += sum(digits_of(d * 2))
        return checksum % 10

    return luhn_checksum(card_number) == 0

def sanitize_input(user_input):
    """Sanitize user input to prevent injection."""
    # Remove special characters
    # Validate against expected format
    # Escape for database queries
    pass

PCI DSS SAQ(自己評価質問票)

SAQ A(要件が最も少ない)
  • ホスト型決済ページを使用した e コマース
  • システム上にカードデータがない
  • 約 20 の質問
SAQ A-EP
  • 埋め込み型決済フォームを使用した e コマース
  • JavaScript を使用してカードデータを処理する
  • 約 180 の質問
SAQ D(要件が最も多い)
  • カードデータを保存、処理、または送信する
  • PCI DSS の全要件が適用される
  • 約 300 の質問

コンプライアンスチェックリスト

PCI_COMPLIANCE_CHECKLIST = {
    'network_security': [
        'Firewall configured and maintained',
        'No vendor default passwords',
        'Network segmentation implemented'
    ],
    'data_protection': [
        'No storage of CVV, track data, or PIN',
        'PAN encrypted when stored',
        'PAN masked when displayed',
        'Encryption keys properly managed'
    ],
    'vulnerability_management': [
        'Anti-virus installed and updated',
        'Secure development practices',
        'Regular security patches',
        'Vulnerability scanning performed'
    ],
    'access_control': [
        'Access restricted by role',
        'Unique IDs for all users',
        'Multi-factor authentication',
        'Physical security measures'
    ],
    'monitoring': [
        'Audit logs enabled',
        'Log review process',
        'File integrity monitoring',
        'Regular security testing'
    ],
    'policy': [
        'Security policy documented',
        'Risk assessment performed',
        'Security awareness training',
        'Incident response plan'
    ]
}

リソース

  • references/data-minimization.md:禁止されているデータを保存しない
  • references/tokenization.md:トークン化の戦略
  • references/encryption.md:暗号化の要件
  • references/access-control.md:ロールベースのアクセス
  • references/audit-logging.md:包括的なログ記録
  • assets/pci-compliance-checklist.md:完全なチェックリスト
  • assets/encrypted-storage.py:暗号化ユーティリティ
  • scripts/audit-payment-system.sh:コンプライアンス監査スクリプト

よくある違反

  1. CVV の保存:カード検証コードは決して保存しないでください
  2. PAN の非暗号化:カード番号は保存時に暗号化する必要があります
  3. 弱い暗号化:AES-256 または同等の方式を使用してください
  4. アクセス制御の欠如:カード会員データにアクセスできるユーザーを制限してください
  5. 監査ログの欠落:決済データへのすべてのアクセスを記録する必要があります
  6. 安全でない送信:常に TLS 1.2 以降を使用してください
  7. デフォルトパスワード:すべてのデフォルト認証情報を変更してください
  8. セキュリティテストの欠如:定期的な侵入テストが必要です

PCI の対象範囲を縮小する

  1. ホスト型決済を使用する:Stripe Checkout、PayPal など
  2. トークン化:カードデータをトークンに置き換える
  3. ネットワークのセグメント化:カード会員データ環境を分離する
  4. 外部委託する:PCI 準拠の決済処理業者を利用する
  5. 保存しない:カードの詳細情報全体を決して保存しない

カードデータに触れるシステムを最小限に抑えることで、コンプライアンスの負担を大幅に軽減できます。

コメント