injection について
injection は、Python プロジェクトのインジェクションリスクを調べるセキュリティテストスキルです。コードレビューや Web アプリのテストを行う開発者向けに、SQL インジェクション、XSS、SSTI、コマンドインジェクション、入力検証を扱い、Bandit と OWASP ZAP のスキャン手順を示します。
主な機能
- 01SQL、XSS、SSTI、コマンドインジェクションの検査
- 02入力検証リスクの確認
- 03危険なコードと安全なコードの例
- 04Bandit による静的スキャン手順
- 05OWASP ZAP による動的スキャン手順
活用シーン
- 01Python プロジェクトの生 SQL とコマンド実行をレビュー
- 02テンプレート描画とユーザー入力処理のリスクを確認
- 03CI/CD でインジェクションスキャンを実行
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ scholarly360/ owasp-top10-web-skills --skill injectionインストール後、Claude Code で/injectionを入力して読み込まれたか確認します。
同梱ファイルreferences/payloads.md
詳細
OWASP A05:2025 — インジェクション:Python セキュリティテストスキル
概要
インジェクションは、信頼できないユーザー入力がインタープリター(データベース、OS シェル、テンプレートエンジン、ブラウザーなど)に渡され、コマンドやクエリの一部として実行されると発生します。2025 年には第 3 位から第 5 位に下がったものの、インジェクションはOWASP の全カテゴリーで最も多くの CVE を占めており、件数は 62,445 件です。そのうち 30,000 件以上は XSS のみで発生しています。
このスキルで扱うインジェクションの種類:
- SQL インジェクション(SQLi)— CWE-89
- クロスサイトスクリプティング(XSS)— CWE-79
- サーバーサイドテンプレートインジェクション(SSTI)— CWE-94
- OS コマンドインジェクション — CWE-78 / CWE-77
- ORM インジェクション
- 入力検証の不備 — CWE-20
- LLM プロンプトインジェクション(関連する分類。OWASP 2025 で言及)
検出チェックリスト
1. SQL インジェクション(CWE-89)
フラグを立てるべき高リスクパターン:
# ❌ VULNERABLE — f-string in raw SQL
db.execute(f"SELECT * FROM users WHERE email = '{email}'")
# ❌ VULNERABLE — .format() in SQLAlchemy text()
db.execute(text("SELECT * FROM users WHERE id = {}".format(user_id)))
# ❌ VULNERABLE — string concatenation in ORM raw query
User.query.filter(text("name = '" + name + "'"))
適用すべき安全なパターン:
# ✅ SAFE — bound parameters in SQLAlchemy text()
db.execute(text("SELECT * FROM users WHERE email = :email"), {"email": email})
# ✅ SAFE — ORM method (avoids interpreter entirely)
User.query.filter_by(email=email).first()
# ✅ SAFE — FastAPI with Pydantic + SQLAlchemy ORM
async def get_user(user_id: int, db: Session = Depends(get_db)):
return db.query(User).filter(User.id == user_id).first()
テスト方法:
- コードベースで
text(、execute(、raw(、cursor.execute(と文字列補間の組み合わせを検索します - Bandit を実行します:
bandit -r . -t B608(ハードコードされた SQL 式) - ペイロードを注入します:
' OR '1'='1、'; DROP TABLE users; --、1 UNION SELECT null,null-- - 文字列パラメーターを受け付けるすべてのエンドポイントで、OWASP ZAP のアクティブスキャンを使用します
2. XSS — クロスサイトスクリプティング(CWE-79)
フラグを立てるべき高リスクパターン:
# ❌ VULNERABLE — Jinja2 auto-escaping disabled globally
app = Flask(__name__)
# Missing: app.jinja_env.autoescape = True (it IS on by default for .html — verify!)
# ❌ VULNERABLE — |safe filter on untrusted data
# In template: {{ user_comment | safe }}
# ❌ VULNERABLE — render_template_string with user input
@app.route("/greet")
def greet():
name = request.args.get("name")
return render_template_string(f"<h1>Hello {name}</h1>") # Also SSTI risk!
適用すべき安全なパターン:
# ✅ SAFE — auto-escaping on (Flask default for .html/.htm/.xml/.xhtml)
# Verify: app.jinja_env.autoescape is True for the template extension
# ✅ SAFE — explicit escaping when rendering HTML from user input
from markupsafe import escape
safe_name = escape(request.args.get("name", ""))
# ✅ SAFE — Content-Security-Policy header
@app.after_request
def set_csp(response):
response.headers["Content-Security-Policy"] = "default-src 'self'"
return response
テスト方法:
- テンプレートで
| safeを Grep で検索します。使用箇所ごとに手動レビューが必要です - すべてのテンプレートタイプで、Jinja2 環境の
autoescape=Trueを確認します - 次を注入します:
<script>alert(1)</script>、"><img src=x onerror=alert(1)> - レスポンスの Content-Security-Policy ヘッダーと X-XSS-Protection ヘッダーを確認します
3. SSTI — サーバーサイドテンプレートインジェクション(CWE-94)
Jinja2 を使用する Flask アプリで、最も重大なインジェクションリスクです。ユーザー入力をテンプレート文字列としてレンダリングすると、Python 式を完全に実行できてしまいます。
フラグを立てるべき高リスクパターン:
# ❌ VULNERABLE — user input in template constructor
from jinja2 import Template
user_template = request.args.get("template")
return Template(user_template).render()
# ❌ VULNERABLE — render_template_string with user data in template itself
return render_template_string(user_provided_content)
# ❌ VULNERABLE — format string from user input
msg_template = user_input # "Hello {user.name}"
msg_template.format(user=current_user) # exposes user object attributes
適用すべき安全なパターン:
# ✅ SAFE — only render static template files; pass user input as context variables
return render_template("greet.html", name=user_name) # name is escaped, not executed
# ✅ SAFE — if dynamic templates needed, use sandboxed environment
from jinja2.sandbox import SandboxedEnvironment
env = SandboxedEnvironment()
env.from_string(template_str).render(data=safe_data)
テスト方法:
- ユーザーデータを含むソースが使われている
render_template_string(、Template(、.format(を検索します - SSTI のプローブを注入します:
{{7*7}}(49が返ることを想定)、{{config}}、{{''.__class__.__mro__}} - エスカレーション用ペイロード:
{{''.__class__.__mro__[1].__subclasses__()}}でクラスを列挙します
4. OS コマンドインジェクション(CWE-78、CWE-77)
フラグを立てるべき高リスクパターン:
# ❌ VULNERABLE — shell=True with user input
import subprocess
filename = request.args.get("file")
subprocess.call(f"cat {filename}", shell=True)
# ❌ VULNERABLE — os.system with any user-controlled string
os.system("convert " + user_filename + " output.jpg")
# ❌ VULNERABLE — os.popen
result = os.popen(f"ls {directory}").read()
適用すべき安全なパターン:
# ✅ SAFE — shell=False (default) with argument list
subprocess.run(["cat", filename], shell=False, capture_output=True)
# ✅ SAFE — validate and sanitize before passing to any subprocess
import re
if not re.match(r'^[a-zA-Z0-9._-]+$', filename):
raise ValueError("Invalid filename")
subprocess.run(["convert", filename, "output.jpg"])
# ✅ SAFE — avoid shell entirely when possible (use Python libraries)
# Instead of: os.system("rm " + path)
import pathlib
pathlib.Path(path).unlink()
テスト方法:
- Bandit:
bandit -r . -t B602,B603,B605,B606,B607(subprocess とシェルに関する問題) - Grep:
os.system(、os.popen(、shell=True - 次を注入します:
; cat /etc/passwd、| id、&& whoami、`id`
5. 入力検証の不備(CWE-20)
FastAPI(Pydantic — 推奨):
# ✅ SAFE — Pydantic enforces types, lengths, patterns at parse time
from pydantic import BaseModel, Field, constr
class UserCreate(BaseModel):
username: constr(min_length=3, max_length=50, pattern=r'^[a-zA-Z0-9_]+$')
age: int = Field(ge=0, le=150)
email: str = Field(max_length=255)
@app.post("/users")
async def create_user(user: UserCreate):
... # Input already validated
# ❌ VULNERABLE — bypassing Pydantic by reading raw request body
body = await request.body()
data = json.loads(body) # No validation!
Flask(手動での検証が必要):
# ✅ SAFE — explicit validation with WTForms or marshmallow
from marshmallow import Schema, fields, validate
class UserSchema(Schema):
username = fields.Str(required=True, validate=validate.Length(min=3, max=50))
age = fields.Int(validate=validate.Range(min=0, max=150))
# ❌ VULNERABLE — using request.args directly without validation
name = request.args.get("name") # Could be anything
db.execute(text(f"SELECT * FROM users WHERE name = '{name}'"))
自動スキャンパイプライン
SAST — Bandit
pip install bandit
bandit -r ./app -f json -o bandit_report.json
# Injection-specific test IDs:
# B608 — Hardcoded SQL expressions
# B602 — subprocess with shell=True
# B603 — subprocess without shell_injection check
# B605 — os.system call
# B606 — os.popen call
# B607 — partial executable path
# B701 — Jinja2 autoescape=False
DAST — OWASP ZAP
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t http://localhost:8000 \
-r zap_report.html
# For active injection testing (more thorough but more aggressive):
docker run -t owasp/zap2docker-stable zap-full-scan.py \
-t http://localhost:8000
組み合わせパイプライン(CI/CD):
# .github/workflows/security.yml
- name: Run Bandit SAST
run: bandit -r ./app -t B608,B602,B603,B605,B606,B701 --exit-zero -f json -o bandit.json
- name: Upload results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: bandit.json
主な CWE リファレンス
| CWE | 名称 | 主なリスク |
|---|---|---|
| CWE-89 | SQL インジェクション | DB データの窃取/改ざん |
| CWE-79 | XSS | セッションハイジャック、フィッシング |
| CWE-78 | OS コマンドインジェクション | RCE、サーバーの乗っ取り |
| CWE-77 | コマンドインジェクション | eval/exec を介した RCE |
| CWE-94 | コードインジェクション/SSTI | Python の完全な実行 |
| CWE-20 | 不適切な入力検証 | 他のすべてのインジェクションにつながる入口 |
LLM プロンプトインジェクション(2025 年追加)
2025 年の OWASP リストでは、LLM プロンプトインジェクションが関連する分類として挙げられています。AI API を使用するアプリケーションでは、次の点に注意してください。
# ❌ VULNERABLE — user input directly in system prompt
system_prompt = f"You are a helpful assistant. Context: {user_provided_context}"
# ✅ SAFER — separate user content from system instructions
messages = [
{"role": "system", "content": STATIC_SYSTEM_PROMPT},
{"role": "user", "content": user_input} # Never injected into system role
]
# Additional mitigations:
# - Input/output filtering for prompt injection patterns
# - Least-privilege tool use (don't give LLM access to sensitive ops)
# - Structured outputs (JSON schema) to limit instruction-following surface
攻撃タイプ別の重大度クイックリファレンス
| 攻撃 | 重大度 | 悪用の容易さ | 典型的な影響 |
|---|---|---|---|
| SSTI | 致命的 | 中程度 | RCE |
| OS コマンドインジェクション | 致命的 | 中程度 | RCE |
| SQL インジェクション | 高 | 容易~中程度 | データ侵害、認証の回避 |
| XSS(格納型) | 高 | 容易 | セッションの乗っ取り |
| XSS(反射型) | 中 | 中程度 | フィッシング、認証情報の窃取 |
| ORM インジェクション | 高 | 中程度 | データの漏えい |
| プロンプトインジェクション | 中~高 | 容易 | ロジックの回避、データの流出 |
参考資料
- OWASP A05:2025 Injection — 公式ガイダンス
- Bandit docs — Python 用 SAST ツール
- Jinja2 Sandbox — 安全なテンプレートレンダリング
- インジェクションペイロードのテストライブラリ全体については、
references/payloads.mdを参照してください