1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OWASP Top 10 : 2025 — Java × Spring Bootで覚える

1
Posted at

この記事を書いたきっかけ

こんな経験、ありませんか?

面接官: 「ソフトウェア開発でセキュリティ面、何か意識されていることは?」

私(ドヤ顔): 「OWASP TOP10に書かれている内容は本番リリース前に必ず潰すようにしています」

面接官: 「いいですね。では具体的に、過去対応されたOWASP TOP10の例を教えてください」

私: 「えーっと……クロスサイトスクリプティング……を……」

面接官: 「……他には?」

私:「……。」

こんな状況を二度と味わわないために、OWASP Top10を脳にぶちこんでおきましょう。

前提: 本記事は OWASP Top 10 2025(公開ドラフト段階) の構成に合わせて書いています。正式版で名称・順位が変わる可能性があるため、最新は owasp.org/Top10 を参照してください。2021 版との主な差分は末尾にまとめています。

ここから順番に見ていきましょう!

A01: Broken Access Control(アクセス制御の不備)— 1位は不動

問題: 認証済みユーザーが他人のリソースに触れる(IDOR=Insecure Direct Object References)、権限昇格。

// ❌ IDOR: {id} に他人のIDを入れれば見える
@GetMapping("/api/accounts/{id}")
public Account get(@PathVariable Long id) {
    return repo.findById(id).orElseThrow();
}

// ✅ オーナーシップを認可で縛る
@PreAuthorize("@accountSec.isOwner(#id, authentication)")
@GetMapping("/api/accounts/{id}")
public Account get(@PathVariable Long id) { ... }

覚え方: 「認証 ≠ 認可」。ログイン確認だけでは足りない。Spring Security の @PreAuthorize / method security を常用。Deny by default(デフォルト拒否)。


A02: Security Misconfiguration(設定ミス)— 🆙 急上昇

問題: デフォルトパスワード、不要エンドポイント露出、詳細エラーページ、CORSゆるゆる。

# ❌ 本番でこれは致命的
management.endpoints.web.exposure.include=*
server.error.include-stacktrace=always
spring.h2.console.enabled=true

# ✅ 本番設定
management.endpoints.web.exposure.include=health,info
server.error.include-stacktrace=never
server.error.include-message=never
// ✅ CORSは明示的に絞る
@Bean
CorsConfigurationSource cors() {
    var c = new CorsConfiguration();
    c.setAllowedOrigins(List.of("https://app.example.com"));
    c.setAllowedMethods(List.of("GET","POST"));
    ...
}

覚え方: 「デフォルトは敵」。Actuator、H2コンソール、Swagger UI、* CORS — "便利な初期値"はだいたい本番では脆弱性。環境別プロファイル (application-prod.properties) を必ず分ける。


A03: Software Supply Chain Failures(サプライチェーンの失敗)— 🆕

問題: Log4Shell (CVE-2021-44228) のような依存ライブラリ由来。依存の依存(推移的)まで含む。ビルドパイプラインの改ざんも含む。

<!-- ❌ 古いまま放置 -->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.14.0</version>
</dependency>

対策:

  • mvn versions:display-dependency-updates / gradle dependencyUpdates
  • OWASP Dependency-Check / Snyk / Dependabot をCIに組み込む
  • Spring Boot の BOM (spring-boot-dependencies) を使い版ズレ防止
  • SBOM (CycloneDX) を生成・保管
  • ビルド成果物に署名 (Sigstore 等)

覚え方: 「自分のコードより依存コードの方が多い」。Log4Shellを思い出せ。CIで自動スキャン+SBOMが基本セット。


A04: Cryptographic Failures(暗号化の失敗)

問題: 平文パスワード、MD5/SHA-1、HTTP通信、ハードコードされた鍵。

// ❌
String hash = DigestUtils.md5Hex(password);

// ✅
@Bean
PasswordEncoder encoder() { return new BCryptPasswordEncoder(12); }
// または Argon2PasswordEncoder (OWASP推奨)
server.ssl.enabled=true
# HSTS は Spring Security がデフォルトで付与

覚え方: 「保存時・通信時・使用時」の3状態を暗号化で守る。パスワードはハッシュ(BCrypt/Argon2/PBKDF2)、データは暗号化(AES-GCM)、鍵はKMS/Vault。自前で暗号アルゴリズムを書かない。


A05: Injection(インジェクション)— XSS もここに統合済

問題: SQLi、コマンドインジェクション、LDAPi、XSS(2021 からここに統合)。

// ❌ SQLi
em.createQuery("SELECT u FROM User u WHERE u.name = '" + name + "'");

// ✅ パラメータバインド
em.createQuery("SELECT u FROM User u WHERE u.name = :n")
  .setParameter("n", name);

// ✅ Spring Data JPA
userRepo.findByName(name);
<!-- ❌ XSS -->
<div th:utext="${comment}"></div>

<!-- ✅ Thymeleaf のデフォルト th:text はエスケープされる -->
<div th:text="${comment}"></div>

覚え方: 「文字列連結でクエリ/コマンドを作ったら負け」。PreparedStatement / JPA パラメータ / Thymeleaf の th:text がデフォルト安全。utextRuntime.exec(String)ProcessBuilder で文字列連結は赤信号。


A06: Insecure Design(安全でない設計)

問題: 実装バグではなく設計そのものが脆弱。
例: パスワードリセットの秘密の質問、レート制限なしログイン、購入フローで金額をクライアント送信。

// ❌ 金額をクライアントから受け取る
@PostMapping("/checkout")
public void buy(Long productId, BigDecimal price) { ... }

// ✅ サーバ側でマスタから取得
@PostMapping("/checkout")
public void buy(Long productId) {
    var price = productRepo.findById(productId)
                           .orElseThrow()
                           .getPrice();
}
  • レート制限: Bucket4j + Spring Boot Starter
  • MFA、リスクベース認証の設計

覚え方: 「後付けのセキュリティは無力」。脅威モデリング(STRIDE)を設計段階で。「クライアントを信じない」「信頼境界を図に書けるか」。


A07: Authentication Failures(認証の失敗)

問題: ブルートフォース許容、セッションID漏洩、弱いパスワードポリシー、ログアウトでセッション破棄しない。

// ✅ Spring Security
http
  .formLogin(f -> f.loginPage("/login"))
  .sessionManagement(s -> s
    .sessionFixation().migrateSession() // セッション固定攻撃対策
    .maximumSessions(1))
  .logout(l -> l.invalidateHttpSession(true)
                .deleteCookies("JSESSIONID"));
  • ログイン試行制限(Bucket4j / Spring Security の LockedException
  • MFA、パスワードレス(WebAuthn/Passkey)
  • JWT を Cookie で 運用するなら短寿命+リフレッシュトークン、HttpOnly; Secure; SameSite=StrictAuthorization: Bearer ヘッダで送るなら XSS で窃取されない保管場所(メモリ等)を選ぶ。

覚え方: 「ID・セッション・パスワード」の3点セットを守る。ログイン後は必ずセッションID再発行(固定化攻撃対策)。


A08: Software or Data Integrity Failures(完全性の失敗)

問題: 署名なし更新、CI/CD汚染、安全でないデシリアライゼーション。

// ❌ 信頼できない入力をJavaデシリアライズ — RCE直行
ObjectInputStream ois = new ObjectInputStream(req.getInputStream());
Object o = ois.readObject();

// ✅ JSONを使い、型を限定
objectMapper.readValue(json, UserDto.class);
// Jackson の Default Typing は絶対に有効化しない
  • ライブラリは署名検証付きで取得(Maven Central はチェックサム検証)
  • CI/CDの秘匿情報・権限を絞る(SLSA準拠)

覚え方: 「readObject は RCE の入口」。Java 標準シリアライズを受け入れ口に使わない。更新配信・依存取得は署名検証。


A09: Security Logging and Alerting Failures(ロギングと検知の失敗)

問題: ログイン失敗が記録されない・監視されない、ログにパスワードやトークンを書く、ログインジェクション。

// ❌ 機密をログに
log.info("Login attempt: user={}, password={}", user, password);

// ❌ ログインジェクション(改行注入でログ偽装)
log.info("User input: " + userInput);

// ✅ 改行・制御文字を除去してから出力
String safe = user == null ? "" : user.replaceAll("[\\r\\n\\t]", "_");
log.info("Login attempt for user={}", safe);
// Logback なら %replace{%msg}{[\r\n]}{_} をパターンで適用する手もある
  • 監査ログ: Spring Boot Actuator + AuditEventRepository
  • 集約: ELK / Loki / CloudWatch、異常検知アラート

覚え方: 「攻撃に気づけないと、侵入は"成功"する」。ログは出す・守る・見るの3つ揃えて初めて価値。機密はマスク、改行はサニタイズ。


A10: Mishandling of Exceptional Conditions(例外処理の不備)— 🆕

問題: スタックトレースがレスポンスに出る、catch (Exception e) {} で握り潰し、エラー時にトランザクションやリソースが中途半端に残る。

// ❌ 例外握り潰し + 情報漏洩
try {
    payment.process();
} catch (Exception e) {
    return ResponseEntity.status(500).body(e.toString()); // 内部情報漏洩
}

// ✅ 集中ハンドリング + 汎用メッセージ
@RestControllerAdvice
class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorDto> handle(Exception e) {
        String traceId = UUID.randomUUID().toString();
        log.error("[{}] Unexpected error", traceId, e);  // 詳細はログへ
        return ResponseEntity.status(500)
            .body(new ErrorDto("Internal error", traceId)); // クライアントには最小限
    }
}
server.error.include-stacktrace=never
server.error.include-message=never
server.error.include-exception=false

覚え方: 「例外はログへ、ユーザーには一般化したメッセージ+追跡ID」。catch (Exception e) {} は禁止、@RestControllerAdvice で集中管理。エラー時も不変条件(トランザクション/ロック/ファイルハンドル)を壊さない。


🧠 まとめて覚えるための語呂・構造

番号順キーワード(日本語)

# 英語短縮 日本語一言
A01 Access 権限はがし
A02 Misconfig 初期設定は敵
A03 Supply Chain 依存は爆弾(Log4Shell)
A04 Crypto 暗号・ハッシュ・TLS
A05 Injection 連結するな、バインドしろ
A06 Insecure Design 設計でしくじるな
A07 AuthN ログイン・セッション・パスワード
A08 Integrity 署名とデシリアライズ
A09 Logging 気づけない攻撃は成功する
A10 Exception 例外から漏らすな

3グループに圧縮

  • 入口を守る(1, 5, 7): 誰が・何を・どう入れるか → Access / Injection / Auth
  • 土台を守る(2, 3, 4, 6, 8): 設定・依存・暗号・設計・完全性
  • 出口を守る(9, 10): ログとエラー、漏らさず・見逃さず

Spring Boot 定番対策セット

対策 カバーする項目
Spring Security A01, A07
@PreAuthorize A01
BCrypt / Argon2 A04
Bean Validation A05
Parameterized JPA A05
Dependency-Check / SBOM A03
@RestControllerAdvice A09, A10
Actuator + マスキング A02, A09
プロファイル分離 A02

まずは番号順に"一言"を覚え → 次に Spring Boot の対策クラス/アノテーションを紐付け → 最後に攻撃名の具体例(IDOR, Log4Shell, SQLi, CSRF, 固定化攻撃, デシリアライズ RCE)を肉付けする、の3段階がおすすめです。

ちなみに 2021 → 2025 の主な変化はこちら (ドラフト段階):

  • 🆙 Security Misconfiguration が A05 → A02 に急上昇
  • 🆕 Software Supply Chain Failures (A03) — 旧「脆弱性のある・古いコンポーネント」を拡張
  • 🆕 Mishandling of Exceptional Conditions (A10) が新登場
  • ❌ SSRF (Server-Side Request Forgery) は単独枠から外れた(他項目に統合)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?