python について
Python バックエンド開発向けのスキルガイドで、型安全性、非同期処理、テスト、セキュリティを扱います。保守しやすいサービスの構築やレビューに向け、入力検証からデプロイ前確認までの実践パターンを示します。
主な機能
- 01型注釈と実行時検証
- 02asyncio とパフォーマンスパターン
- 03TDD、pytest、型チェック
- 04安全な DB・ファイル・サブプロセス操作
- 05脆弱性対策とデプロイチェックリスト
活用シーン
- 01安全な API 入力検証の設計
- 02非同期データベースアクセスの実装
- 03インジェクションやパストラバーサルの確認
- 04デプロイ前のテストと依存関係監査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ martinholovsky/ claude-skills-generator --skill pythonインストール後、Claude Code で/pythonを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/advanced-patterns.mdsecurity-examples.mdthreat-model.md詳細
Python バックエンド開発スキル
ファイル構成
このスキルでは、リスクの高い要件に対応するため、ファイルを分割しています。
- SKILL.md:基本原則、パターン、セキュリティの必須事項(このファイル)
- references/security-examples.md:CVE の詳細と OWASP の実装例一式
- references/advanced-patterns.md:高度な Python パターンと最適化
- references/threat-model.md:攻撃シナリオと STRIDE 分析
検証ゲート
| ゲート | 状態 | 備考 |
|---|---|---|
| 0.1 ドメインの専門知識 | 合格 | 型安全性、非同期処理、セキュリティ、テスト |
| 0.2 脆弱性の調査 | 合格 | 5 件以上の CVE を記録(2025-11-20) |
| 0.5 ハルシネーションの確認 | 合格 | Python 3.11 以降で例をテスト済み |
| 0.11 ファイル構成 | 分割 | リスクが高い要件、約 450 行+参照資料 |
1. 概要
リスクレベル:高
理由:Python バックエンドサービスは、認証、データベースアクセス、ファイル操作、外部 API との通信を処理します。入力検証、デシリアライズ、コマンド実行、暗号化の脆弱性は、データ漏えいやシステム侵害につながる可能性があります。
あなたは、安全で保守しやすく、高性能なサービスを専門とする、Python バックエンド開発のエキスパートです。
主な専門分野
- 型注釈と実行時検証
- asyncio による非同期プログラミング
- セキュリティ:入力検証、暗号化、シークレット管理
- テスト:pytest、プロパティベーステスト、セキュリティテスト
- SQLAlchemy/asyncpg によるデータベースアクセス
- FastAPI/Starlette による API 開発
2. 主な責務
基本原則
- まず TDD:実装の前にテストを書き、テストケースを通じて API を設計します
- パフォーマンスを意識:デフォルトで、非同期処理、ジェネレーター、効率的なデータ構造を使用します
- 型安全性:あらゆる箇所で型ヒントを使用し、実行時の境界で検証します
- 多層防御:複数の検証層を設け、安全に失敗するようにします
- 安全なデフォルト設定:安全なライブラリを使用し、安全でない操作を拒否します
- 暗黙的な処理より明示的な処理:明確なエラー処理と、明示的な依存関係を使用します
- テスト容易性:テストしやすい設計にし、セキュリティテストを作成します
判断基準
| 状況 | アプローチ |
|---|---|
| ユーザー入力 | Pydantic で検証し、出力をサニタイズします |
| データベースクエリ | ORM またはパラメーター化クエリを使用し、文字列のフォーマットは決して使いません |
| ファイル操作 | パスを検証し、pathlib を使用して、パスが指定範囲内にあることを確認します |
| サブプロセス | 引数リストを使用し、ユーザー入力に対して shell=True を決して使用しません |
| シークレット | 環境変数またはシークレットマネージャーから読み込みます |
| 暗号化 | cryptography ライブラリを使用し、独自実装は決して行いません |
2.1 実装ワークフロー(TDD)
ステップ 1:まず失敗するテストを書く
import pytest
from my_service import UserService, UserNotFoundError
class TestUserService:
@pytest.mark.asyncio
async def test_get_user_returns_user_when_exists(self, db_session):
service = UserService(db_session)
user_id = await service.create_user("alice", "[email protected]")
user = await service.get_user(user_id)
assert user.username == "alice"
@pytest.mark.asyncio
async def test_get_user_raises_when_not_found(self, db_session):
service = UserService(db_session)
with pytest.raises(UserNotFoundError):
await service.get_user(99999)
@pytest.mark.asyncio
async def test_create_user_validates_email(self, db_session):
service = UserService(db_session)
with pytest.raises(ValueError, match="Invalid email"):
await service.create_user("bob", "not-an-email")
ステップ 2:テストに合格するための最小限の実装を行う
class UserNotFoundError(Exception): pass
class UserService:
def __init__(self, db: AsyncSession):
self.db = db
async def get_user(self, user_id: int) -> User:
user = await self.db.get(User, user_id)
if not user:
raise UserNotFoundError(f"User {user_id} not found")
return user
async def create_user(self, username: str, email: str) -> int:
if "@" not in email:
raise ValueError("Invalid email format")
# ... minimal implementation to pass tests
ステップ 3:必要に応じてリファクタリングする
- 共通パターンを抽出し、型ヒントを追加して、エラーから内部情報が漏れないようにします
ステップ 4:完全な検証を実行する
pytest --cov=src # All tests pass
mypy src/ --strict # Type check passes
bandit -r src/ -ll # Security scan passes
pip-audit && safety check # Dependencies clean
2.2 パフォーマンスパターン
パターン 1:asyncio.gather による非同期 I/O
# BAD: Sequential requests (slow)
for url in urls:
response = await client.get(url) # Waits for each one
# GOOD: Concurrent requests with gather
tasks = [client.get(url) for url in urls]
responses = await asyncio.gather(*tasks) # All at once
パターン 2:大量データ処理にジェネレーターを使用する
# BAD: Load all into memory
return [process(line) for line in f.readlines()] # OOM risk
# GOOD: Generator yields one at a time
def process_large_file(filepath: str) -> Iterator[dict]:
with open(filepath) as f:
for line in f:
yield process(line) # Memory efficient
パターン 3:効率的なデータ構造
# BAD: List for membership testing - O(n)
required in user_perms_list # Slow for large lists
# GOOD: Set for membership testing - O(1)
required in user_perms_set # Fast lookup
# BAD: Repeated string concatenation
result = ""; for f in fields: result += f + ", " # Creates new string each time
# GOOD: Join for string building
", ".join(fields) # Single allocation
パターン 4:コネクションプーリング
# BAD: New connection per request
engine = create_async_engine(DATABASE_URL) # Connection overhead each time
# GOOD: Reuse pooled connections
engine = create_async_engine(DATABASE_URL, pool_size=20, max_overflow=10)
async_session = sessionmaker(engine, class_=AsyncSession)
async def get_user(user_id: int):
async with async_session() as session: # Reuses pooled connection
return await session.get(User, user_id)
パターン 5:データベース操作のバッチ処理
# BAD: Individual inserts (N round trips)
for user in users:
db.add(User(**user)); await db.commit() # N commits = slow
# GOOD: Batch insert (1 round trip)
stmt = insert(User).values(users)
await db.execute(stmt); await db.commit() # Single commit
# GOOD: Chunked for very large datasets
for i in range(0, len(users), 1000):
await db.execute(insert(User).values(users[i:i+1000]))
await db.commit()
3. 技術基盤
推奨バージョン
| カテゴリー | バージョン | 備考 |
|---|---|---|
| LTS/推奨 | Python 3.11 以降 | パフォーマンスの改善、より分かりやすいエラー |
| 最小要件 | Python 3.9 | セキュリティサポートは 2025 年 10 月まで |
| 非推奨 | Python 3.8 以前 | EOL、セキュリティパッチなし |
セキュリティ依存関係
# pyproject.toml
[project]
dependencies = [
"pydantic>=2.0", "email-validator>=2.0", # Validation
"cryptography>=41.0", "argon2-cffi>=21.0", # Cryptography
"PyJWT>=2.8", "sqlalchemy>=2.0", "asyncpg>=0.28",
"httpx>=0.25", "bandit>=1.7",
]
[project.optional-dependencies]
dev = ["pytest>=7.0", "pytest-asyncio>=0.21", "hypothesis>=6.0", "safety>=2.0", "pip-audit>=2.0"]
4. 実装パターン
パターン 1:型安全な入力検証
from pydantic import BaseModel, Field, field_validator, EmailStr
from typing import Annotated
import re
class UserCreate(BaseModel):
"""Validated user creation request."""
username: Annotated[str, Field(min_length=3, max_length=50)]
email: EmailStr
password: Annotated[str, Field(min_length=12)]
@field_validator('username')
@classmethod
def validate_username(cls, v: str) -> str:
if not re.match(r'^[a-zA-Z0-9_-]+$', v):
raise ValueError('Username must be alphanumeric')
return v
@field_validator('password')
@classmethod
def validate_password_strength(cls, v: str) -> str:
if not all([re.search(r'[A-Z]', v), re.search(r'[a-z]', v), re.search(r'\d', v)]):
raise ValueError('Password needs uppercase, lowercase, and digit')
return v
パターン 2:安全なパスワードハッシュ化
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=4)
def hash_password(password: str) -> str:
return ph.hash(password)
def verify_password(password: str, hash: str) -> bool:
try:
ph.verify(hash, password)
return True
except VerifyMismatchError:
return False
パターン 3:安全なデータベースクエリ
from sqlalchemy import select, text
from sqlalchemy.ext.asyncio import AsyncSession
# NEVER: f"SELECT * FROM users WHERE username = '{username}'"
async def get_user_safe(db: AsyncSession, username: str) -> User | None:
stmt = select(User).where(User.username == username)
result = await db.execute(stmt)
return result.scalar_one_or_none()
async def search_users(db: AsyncSession, pattern: str) -> list:
stmt = text("SELECT * FROM users WHERE username LIKE :pattern")
result = await db.execute(stmt, {"pattern": f"%{pattern}%"})
return result.fetchall()
パターン 4:安全なファイル操作
from pathlib import Path
def safe_read_file(base_dir: Path, user_filename: str) -> str:
if '..' in user_filename or user_filename.startswith('/'):
raise ValueError("Invalid filename")
file_path = (base_dir / user_filename).resolve()
if not file_path.is_relative_to(base_dir.resolve()):
raise ValueError("Path traversal detected")
return file_path.read_text()
パターン 5:安全なサブプロセス実行
import subprocess
ALLOWED_PROGRAMS = {'git', 'python', 'pip'}
def run_command_safe(program: str, args: list[str]) -> str:
if program not in ALLOWED_PROGRAMS:
raise ValueError(f"Program not allowed: {program}")
result = subprocess.run(
[program, *args],
capture_output=True, text=True, timeout=30, check=True,
)
return result.stdout
5. セキュリティ標準
5.1 ドメインの脆弱性の状況
| CVE ID | 重大度 | 説明 | 緩和策 |
|---|---|---|---|
| CVE-2024-12718 | CRITICAL | tarfile フィルターのバイパス | Python 3.12.3+、filter='data' |
| CVE-2024-12254 | HIGH | asyncio のメモリ枯渇 | アップグレード、メモリを監視 |
| CVE-2024-5535 | MEDIUM | SSLContext のバッファー範囲外読み取り | OpenSSL をアップグレード |
| CVE-2023-50782 | HIGH | RSA の情報漏えい | cryptography をアップグレード |
| CVE-2023-27043 | MEDIUM | メール解析の脆弱性 | 厳格なメール検証 |
CVE の詳細と緩和策のコードについては、
references/security-examples.mdを参照してください
5.2 OWASP Top 10 との対応
| カテゴリー | リスク | 主な緩和策 |
|---|---|---|
| A01 アクセス制御の不備 | HIGH | 権限の検証、デコレーター |
| A02 暗号化の失敗 | HIGH | cryptography ライブラリー、Argon2 |
| A03 インジェクション | CRITICAL | パラメーター化クエリー、shell=True を使用しない |
| A04 安全でない設計 | MEDIUM | 型安全性、検証レイヤー |
| A05 セキュリティ設定のミス | HIGH | 安全なデフォルト設定、依存関係の監査 |
| A06 脆弱なコンポーネント | HIGH | CI で pip-audit、safety を使用 |
5.3 必須のセキュリティパターン
from pydantic import BaseModel, field_validator
import os, logging
# Secure base model - reject unknown fields, strip whitespace
class SecureInput(BaseModel):
model_config = {'extra': 'forbid', 'str_strip_whitespace': True}
@field_validator('*', mode='before')
@classmethod
def reject_null_bytes(cls, v):
if isinstance(v, str) and '\x00' in v:
raise ValueError('Null bytes not allowed')
return v
# Secrets from environment (NEVER hardcode)
API_KEY = os.environ["API_KEY"]
DB_URL = os.environ["DATABASE_URL"]
# Safe error handling - log details, return safe message
class AppError(Exception):
def __init__(self, message: str, internal: str = None):
self.message = message
if internal:
logging.error(f"{message}: {internal}")
def to_response(self) -> dict:
return {"error": self.message}
シークレットマネージャーとの統合については、
references/advanced-patterns.mdを参照してください
6. テストと検証
セキュリティテストコマンド
bandit -r src/ -ll # Static analysis
pip-audit && safety check # Dependency vulnerabilities
mypy src/ --strict # Type checking
セキュリティテストの例
import pytest
from pathlib import Path
def test_sql_injection_prevented(db):
for payload in ["'; DROP TABLE users; --", "' OR '1'='1", "admin'--"]:
assert get_user_safe(db, payload) is None
def test_path_traversal_blocked():
base = Path("/app/data")
for attack in ["../etc/passwd", "..\\windows\\system32", "foo/../../etc/passwd"]:
with pytest.raises(ValueError, match="traversal|Invalid"):
safe_read_file(base, attack)
def test_command_injection_blocked():
with pytest.raises(ValueError, match="not allowed"):
run_command_safe("rm", ["-rf", "/"])
包括的なテストパターンについては、
references/security-examples.mdを参照してください
7. よくある間違いとアンチパターン
| アンチパターン | 悪い例 | 良い例 |
|---|---|---|
| SQL のフォーマット | f"SELECT * WHERE id={id}" | select(User).where(User.id == id) |
| 信頼できない pickle | pickle.loads(data) | json.loads(data) |
| シェルインジェクション | subprocess.run(f"echo {x}", shell=True) | subprocess.run(["echo", x]) |
| 弱いハッシュ | hashlib.md5(pw).hexdigest() | PasswordHasher().hash(pw) |
| シークレットのハードコード | API_KEY = "sk-123..." | API_KEY = os.environ["API_KEY"] |
8. デプロイ前チェックリスト
フェーズ 1:コードを書く前
- 要件を理解し、文書化している
- API 設計をレビューしている(入力、出力、エラー)
- セキュリティ脅威モデルを検討している
- テストケースを先に作成している(TDD)
- エッジケースとエラーシナリオを特定している
フェーズ 2:実装中
- TDD ワークフローに従っている(テスト -> 実装 -> リファクタリング)
- パフォーマンスパターンを使用している(非同期、ジェネレーター、プーリング)
- すべての入力を Pydantic で検証している
- DB クエリーをパラメーター化している/ORM を使用している
- ファイル操作でパスの包含関係を確認している
- サブプロセスでリスト形式の引数を使用している
- パスワードを Argon2id でハッシュ化している
- シークレットを環境変数からのみ取得している
フェーズ 3:コミット前
- すべてのテストが成功する:
pytest --cov=src - 型チェックが成功する:
mypy src/ --strict - セキュリティスキャンが成功する:
bandit -r src/ -ll - 依存関係の監査が成功する:
pip-audit && safety check - コード内にハードコードされたシークレットがない
- エラーから内部の詳細情報が漏えいしない
- デバッグモードが無効になっている
- ロギングが設定されている(PII/シークレットを含めない)
9. まとめ
型安全で、セキュアで、テスト可能かつ保守しやすいPython コードを作成してください。
セキュリティの基本:
- すべてのユーザー入力を検証し、サニタイズする
- データベース操作にはパラメーター化クエリーを使用する
- ユーザー入力に対して shell=True を決して使用しない
- パスワードを Argon2id でハッシュ化する
- シークレットを環境変数から読み込む
- 依存関係を最新の状態に保ち、監査する
攻撃シナリオと脅威モデリングについては、
references/threat-model.mdを参照してください
コメント
Markdown 対応