OWASP Top 10とは?
OWASP(Open Web Application Security Project)は、Webアプリケーションのセキュリティに関するリスクを年次でランク付けした「OWASP Top 10」を公開しています。2021年版が現在も広く参照されており、すべてのWeb開発者が把握すべき脆弱性リストです。
1. インジェクション攻撃(SQLインジェクション)
最も古典的で危険な攻撃の一つです。ユーザー入力がそのままSQLクエリに埋め込まれると、データベースを不正操作される危険があります。
## 危険なコード(SQLインジェクション脆弱あり)
# Python + SQLiteの悪い例
def get_user_bad(username):
query = "SELECT * FROM users WHERE username = '" + username + "'"
# 悪意あるusername: "admin' OR '1'='1" → 全ユーザー取得可能!
cursor.execute(query)
return cursor.fetchone()
## 安全なコード(パラメータ化クエリ)
def get_user_safe(username):
query = "SELECT * FROM users WHERE username = ?"
cursor.execute(query, (username,)) # バインドパラメータで安全
return cursor.fetchone()
## ORMを使う(最も安全)
from sqlalchemy.orm import Session
def get_user_orm(db: Session, username: str):
return db.query(User).filter(User.username == username).first()
# ORMが自動的にパラメータ化してくれる
2. 壊れた認証(パスワード管理の不備)
## 危険:パスワードを平文やMD5で保存
import hashlib
# NG: MD5はレインボーテーブルで解析可能
hashed = hashlib.md5(password.encode()).hexdigest()
## 安全:bcryptまたはArgon2を使用
import bcrypt
from argon2 import PasswordHasher
# bcryptの使用例
def hash_password(password: str) -> str:
return bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)).decode()
def verify_password(password: str, hashed: str) -> bool:
return bcrypt.checkpw(password.encode(), hashed.encode())
# Argon2(より新しくより安全)
ph = PasswordHasher(time_cost=2, memory_cost=65536, parallelism=2)
def hash_password_argon2(password: str) -> str:
return ph.hash(password) # Argon2idアルゴリズム使用
3. XSS(クロスサイトスクリプティング)
// 危険:ユーザー入力をそのままHTMLに埋め込む
document.getElementById('output').innerHTML = userInput; // XSS脆弱!
// 安全:textContentを使う(HTMLとして解釈されない)
document.getElementById('output').textContent = userInput;
// React/Vueを使う場合は基本的に安全
// Reactは自動エスケープするため、dangerouslySetInnerHTMLは使わないこと
function SafeComponent({ userInput }) {
return {userInput}; // 安全:自動エスケープ
}
// サーバーサイドでのサニタイズ(DOMPurify)
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyHTML);
// Python(Flask)でのJinja2テンプレートは自動エスケープ
// {{ user_input }} → 安全(自動エスケープ)
// {{ user_input | safe }} → 危険!絶対に使わない
4. セキュリティヘッダーの設定
# Nginx設定でのセキュリティヘッダー
# /etc/nginx/conf.d/security.conf
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# Content Security Policy(CSP)- 最重要
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.yourdomain.com;
frame-ancestors 'none';
" always;
# HSTS(HTTPSを強制)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
5. CSRF対策
# Django(組み込みCSRF保護)
# settings.py
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware', # デフォルトで有効
# ...
]
# テンプレートでのCSRFトークン使用
# {% csrf_token %}
# FastAPIでのCSRF対策
from fastapi import Request, HTTPException
import secrets
def generate_csrf_token():
return secrets.token_urlsafe(32)
async def verify_csrf_token(request: Request):
token_header = request.headers.get("X-CSRF-Token")
token_cookie = request.cookies.get("csrf_token")
if not token_header or not token_cookie:
raise HTTPException(status_code=403, detail="CSRF token missing")
if not secrets.compare_digest(token_header, token_cookie):
raise HTTPException(status_code=403, detail="CSRF token invalid")
セキュリティチェックリスト
- ✅ すべてのSQL・コマンド実行でパラメータ化クエリを使用
- ✅ パスワードはbcryptまたはArgon2でハッシュ化
- ✅ HTTPSを強制(HSTSヘッダー設定)
- ✅ 適切なCSPヘッダーを設定
- ✅ 依存ライブラリを定期的に更新(npm audit / pip-audit)
- ✅ エラーメッセージにスタックトレースを含めない(本番環境)
- ✅ レート制限の実装(ブルートフォース対策)
- ✅ ログイン試行の監視とアラート設定
まとめ
Webアプリケーションのセキュリティは「一度設定すれば終わり」ではありません。定期的な脆弱性スキャン、依存関係の更新、ペネトレーションテストを実施することが重要です。OWASP Top 10を起点として、自分のアプリケーションのセキュリティを継続的に改善していきましょう。