Welcome to Tech Athletes | テック・アスリート   Click to listen highlighted text! Welcome to Tech Athletes | テック・アスリート

【2026年版】Webセキュリティ完全ガイド|OWASP Top10・脆弱性診断・セキュアコーディングの実践

なぜWebセキュリティが重要なのか

2026年、サイバー攻撃の被害は年間数十兆円規模に達しています。エンジニアがセキュリティを「後から考えること」にすると、データ漏洩・サービス停止・法的責任のリスクを抱えます。本記事ではOWASP Top 10に基づき、実践的なセキュリティ対策を解説します。

OWASP Top 10(2026年版)

順位脆弱性概要
A01アクセス制御の不備認可チェック漏れによる不正アクセス
A02暗号化の失敗平文での機密データ送受信
A03インジェクションSQLi・XSS・コマンドインジェクション
A04セキュアでない設計設計段階でのセキュリティ考慮不足
A05セキュリティの設定ミスデフォルト設定・不要な機能の有効化
A06脆弱なコンポーネント古いライブラリ・フレームワークの使用
A07認証・認可の失敗弱いパスワード・不適切なセッション管理
A08ソフトウェアとデータの完全性CI/CDパイプラインへの攻撃
A09ログ・モニタリングの失敗攻撃の検知・対応遅延
A10SSRFサーバーサイドリクエストフォージェリ

SQLインジェクション対策

# 危険なコード(SQLインジェクション脆弱)
def get_user_bad(username: str):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    # username = "admin' OR '1'='1" で全ユーザーが取れてしまう!

# 安全なコード(プレースホルダ使用)
import sqlite3

def get_user_safe(username: str):
    conn = sqlite3.connect('app.db')
    cursor = conn.cursor()
    # プレースホルダ(?)を使う
    cursor.execute("SELECT * FROM users WHERE username = ?", (username,))
    return cursor.fetchone()

# SQLAlchemyを使う場合(ORM)
from sqlalchemy.orm import Session

def get_user_sqlalchemy(db: Session, username: str):
    return db.query(User).filter(User.username == username).first()

XSS(クロスサイトスクリプティング)対策

# バックエンド(FastAPI)でのサニタイズ
import html
from bleach import clean, linkify

def sanitize_content(content: str) -> str:
    # HTMLタグをエスケープ
    escaped = html.escape(content)
    
    # または許可するタグを限定
    allowed_tags = ['p', 'br', 'strong', 'em', 'a', 'ul', 'ol', 'li']
    allowed_attrs = {'a': ['href', 'title', 'rel']}
    cleaned = clean(content, tags=allowed_tags, attributes=allowed_attrs)
    return cleaned

# フロントエンド(React)では自動でエスケープ
const UserContent = ({ content }) => {
  // ReactのJSXは自動的にXSSを防ぐ
  return 
{content}
; // 安全 // dangerouslySetInnerHTMLは使わない! // return
; // 危険 };

認証・認可のベストプラクティス

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
import secrets

app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
security = HTTPBearer()

SECRET_KEY = secrets.token_urlsafe(32)  # 本番では環境変数から読む
ALGORITHM = "HS256"

# パスワードのハッシュ化
def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

# JWTトークンの生成
def create_access_token(user_id: str, expires_delta: timedelta = timedelta(hours=1)) -> str:
    payload = {
        "sub": user_id,
        "exp": datetime.utcnow() + expires_delta,
        "iat": datetime.utcnow(),
        "jti": secrets.token_urlsafe(16)  # JWT ID(リプレイ攻撃防止)
    }
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

# トークン検証
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
    try:
        payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=[ALGORITHM])
        user_id = payload.get("sub")
        if not user_id:
            raise HTTPException(status_code=401, detail="Invalid token")
        return user_id
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.JWTError:
        raise HTTPException(status_code=401, detail="Invalid token")

セキュリティヘッダーの設定

# FastAPIでセキュリティヘッダーを追加
from fastapi import FastAPI, Request, Response
from fastapi.middleware.httpsredirect import HTTPSRedirectMiddleware

app = FastAPI()

@app.middleware("http")
async def add_security_headers(request: Request, call_next):
    response = await call_next(request)
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "DENY"
    response.headers["X-XSS-Protection"] = "1; mode=block"
    response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
    response.headers["Content-Security-Policy"] = (
        "default-src 'self'; "
        "script-src 'self' 'nonce-{nonce}'; "
        "style-src 'self' 'unsafe-inline'; "
        "img-src 'self' data: https:; "
        "connect-src 'self' https://api.example.com"
    )
    response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
    response.headers["Permissions-Policy"] = "geolocation=(), microphone=(), camera=()"
    return response

脆弱性スキャンツール

  • OWASP ZAP:無料のWebアプリ脆弱性スキャナー
  • Burp Suite:プロ向けセキュリティテストツール
  • Snyk:依存パッケージの脆弱性検出(CI/CD統合可能)
  • Trivy:コンテナイメージの脆弱性スキャン
  • Semgrep:静的解析によるセキュリティチェック

セキュリティチェックリスト

  • □ 全通信をHTTPS化(Let’s Encrypt活用)
  • □ パスワードはbcryptでハッシュ化
  • □ SQLはプレースホルダ/ORMを使用
  • □ ユーザー入力はサニタイズ・バリデーション
  • □ 依存パッケージを定期的に更新(npm audit / pip-audit)
  • □ セキュリティヘッダーを全レスポンスに設定
  • □ ログに個人情報・パスワードを出力しない
  • □ レート制限でブルートフォース攻撃を防ぐ
  • □ CSRFトークンでCSRF攻撃を防ぐ
  • □ 定期的なペネトレーションテスト実施

まとめ

セキュリティは後付けではなく、設計段階から組み込むことが重要です。OWASP Top 10を理解し、日々のコーディングでセキュアな実装を習慣づけましょう。セキュリティエンジニアの需要は2026年も高く、キャリアとしても有望な分野です。

投稿者 kasata

IT企業でエンジニアとして勤務後、テクノロジー情報メディア「Tech Athletes(テック・アスリート)」を運営。プログラミング、クラウドインフラ(AWS/GCP/Azure)、AI活用、Webサービス開発を専門とする。エンジニア・ビジネスパーソン向けに、実際に使ってみた経験をもとに信頼できる技術情報を発信中。資格:AWS認定ソリューションアーキテクト、Python 3 エンジニア認定試験合格。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

Click to listen highlighted text!