pci-compliance について
決済システムの構築やカード会員データの取り扱いに携わるチーム向けの PCI DSS ガイドです。要件、準拠レベル、データ最小化、トークン化、暗号化のパターンを扱います。
主な機能
- 01PCI DSS 要件の概要
- 02準拠レベルの参照
- 03カード会員データの最小化
- 04決済トークン化のパターン
- 05保存時と転送時の暗号化
活用シーン
- 01安全な決済フローの設計
- 02PCI DSS 評価の準備
- 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 の中核要件)
安全なネットワークの構築と維持
- ファイアウォール設定を導入し、維持する
- ベンダー提供のデフォルトパスワードを使用しない
カード会員データの保護
- 保存されたカード会員データを保護する
- 公衆ネットワーク上でのカード会員データの送信を暗号化する
脆弱性管理の維持
- システムをマルウェアから保護する
- 安全なシステムとアプリケーションを開発し、維持する
強力なアクセス制御の実装
- 業務上の必要性(need-to-know)に基づいてカード会員データへのアクセスを制限する
- システムコンポーネントへのアクセスを識別し、認証する
- カード会員データへの物理的なアクセスを制限する
ネットワークの監視とテスト
- ネットワークリソースおよびカード会員データへのすべてのアクセスを追跡し、監視する
- セキュリティシステムとプロセスを定期的にテストする
情報セキュリティポリシーの維持
- 情報セキュリティに対応するポリシーを維持する
コンプライアンスレベル
レベル 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 にあります。完全なパターンライブラリについては、そのファイルをお読みください。
コメント
Markdown 対応