Security レポート
本プロジェクト(Spring Boot バックエンド + React フロントエンド + Supabase 認証/DB)について、IPA「安全なウェブサイトの作り方」の11項目に基づきセキュリティ実装状況を整理したものです。
調査日: 2026-05-02
対象ブランチ: main
総合判定サマリー
| # | 脆弱性 | 判定 | 対応概要 |
|---|---|---|---|
| 1.1 | SQLインジェクション | ✅ 対策済 |
JdbcClient のパラメータバインディング |
| 1.2 | OSコマンド・インジェクション | ➖ 該当なし | OSコマンド実行処理なし |
| 1.3 | ディレクトリ・トラバーサル | ➖ 該当なし | ファイルアップロード/ダウンロードなし |
| 1.4 | セッション管理 | ✅ 対策済 | JWT + HttpOnly Cookie + SameSite + Stateless |
| 1.5 | XSS | ✅ 対策済 | React 自動エスケープ + JSON API |
| 1.6 | CSRF | ✅ 対策済 | CSRF トークン + SameSite + login/logout/refresh で検証 |
| 1.7 | HTTPヘッダ・インジェクション | ✅ 対策済 | リダイレクト処理なし + HSTS / X-Content-Type-Options / Referrer-Policy 設定 |
| 1.8 | メールヘッダ・インジェクション | ➖ 該当なし | メール送信機能なし |
| 1.9 | クリックジャッキング | ✅ 対策済 |
X-Frame-Options: DENY + CSP frame-ancestors 'none'
|
| 1.10 | バッファオーバーフロー | ➖ 該当なし | Java/JVM 環境 |
| 1.11 | アクセス制御・認可制御 | ✅ 対策済 | ロール別制御 + IDOR チェック + Supabase RLS |
| 補足 | サイドチャネル対策 (Spectre 等) | ✅ 対策済 | COOP same-origin + Sec-Fetch-Site 検証フィルタ |
詳細
1.1 SQLインジェクション ✅
対策内容:
- すべてのSQL操作で
JdbcClient(Spring Framework) を使用し、プレースホルダ + パラメータバインディングで実装。 - ユーザー入力のSQL文字列連結は存在しない。
- ILIKE 検索でも
likeQueryパラメータでバインド済み。
根拠ファイル:
backend/src/main/java/com/example/studentmanagement/repository/StudentRepository.javabackend/src/main/java/com/example/studentmanagement/repository/AttendanceRepository.javabackend/src/main/java/com/example/studentmanagement/repository/DashboardRepository.java
// 例: StudentRepository.java
.param("studentCode", request.studentCode())
1.2 OSコマンド・インジェクション ➖
Runtime.exec() / ProcessBuilder の使用なし。該当する攻撃面が存在しない。
1.3 ディレクトリ・トラバーサル ➖
ファイルアップロード/ダウンロード機能なし。パス操作は UUID ベースの ID のみで動的パス構築なし。
1.4 セッション管理 ✅
対策内容:
- JWT (Supabase Auth) ベースの認証。
- Spring Security は Stateless 構成。
- ログイン時にアクセストークン + リフレッシュトークン発行、
/api/auth/refreshで更新可能。 - ログアウト時に Supabase API 呼び出し + 全認証クッキー削除。
- Cookie 属性:
HttpOnly=true-
Secure=設定可(環境変数APP_AUTH_COOKIE_SECURE) SameSite=Lax- リフレッシュトークンは
Path=/api/auth/refreshに限定
根拠ファイル:
backend/src/main/java/com/example/studentmanagement/security/AuthCookieService.java:71-75-
backend/src/main/java/com/example/studentmanagement/config/SecurityConfig.java:48(SessionCreationPolicy.STATELESS) backend/src/main/java/com/example/studentmanagement/controller/AuthController.java:58-83
1.5 XSS ✅
対策内容:
- フロントは React JSX による自動 HTML エスケープ。
-
dangerouslySetInnerHTML/innerHTMLの使用なし。 - バックエンドは JSON API 専用 (
application/json;charset=UTF-8)。 - 入力値は
react-hook-form+zodでバリデーション。
根拠ファイル:
frontend/src/pages/LoginPage.tsxfrontend/src/pages/StudentDetailPage.tsxbackend/src/main/java/com/example/studentmanagement/config/SecurityConfig.java:66
1.6 CSRF ✅
対策内容:
- Spring Security の
CookieCsrfTokenRepository.withHttpOnlyFalse()でトークン発行 (XSRF-TOKENクッキー)。 -
/api/auth/csrfで初期トークン取得用エンドポイント。 - フロントは
X-XSRF-TOKENヘッダで自動送信 (http.ts)。 - CSRF 検証対象を
/api/auth/login,/logout,/refreshに明示。 -
SameSite=LaxCookie でクロスサイト送信を制限。
根拠ファイル:
backend/src/main/java/com/example/studentmanagement/config/SecurityConfig.java:79-86backend/src/main/java/com/example/studentmanagement/controller/AuthController.java:95-103frontend/src/lib/api/http.ts:44-98
1.7 HTTPヘッダ・インジェクション ✅
対策内容:
- 動的リダイレクト処理がないため
Locationヘッダ経由の攻撃面はない。 - 追加防御として以下のセキュリティヘッダを
SecurityConfig.headers(...)で配信:-
Strict-Transport-Security: max-age=31536000; includeSubDomains(HTTPS リクエストのみ) X-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-origin
-
根拠ファイル:
backend/src/main/java/com/example/studentmanagement/config/SecurityConfig.java:52-66
1.8 メールヘッダ・インジェクション ➖
メール送信機能なし。@Email バリデーションは入力検証のみ。
1.9 クリックジャッキング ✅
対策内容:
-
X-Frame-Options: DENYを全レスポンスに付与(旧ブラウザ互換)。 -
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'を全レスポンスに付与(モダンブラウザ準拠)。 - API は JSON 専用のため
default-src 'none'でも副作用なし。 - iframe 埋め込みは完全禁止。
根拠ファイル:
backend/src/main/java/com/example/studentmanagement/config/SecurityConfig.java:52-66
1.10 バッファオーバーフロー ➖
Java/JVM 環境のためメモリ安全性は言語レベルで担保。
1.11 アクセス制御・認可制御 ✅
対策内容:
- ロール定義:
ADMIN/TEACHER/STAFF(StaffRole.java) - 各 Service 層で明示的な権限チェック:
- 生徒登録: admin のみ
- 生徒更新: admin、または teacher(担当生徒のみ)
- 生徒削除: admin のみ
- IDOR 対策:
teacherCanAccessStudent()で teacher の担当外生徒アクセスを拒否。 - Supabase RLS との連携:
RlsContextServiceで JWT claims を PostgreSQL セッション変数に伝搬し、DB レベルでも権限チェック。 - フロントは
ProtectedRouteでallowedRolesをチェック。
根拠ファイル:
backend/src/main/java/com/example/studentmanagement/service/StudentService.java:52-83backend/src/main/java/com/example/studentmanagement/repository/StudentRepository.java:183-199backend/src/main/java/com/example/studentmanagement/security/RlsContextService.javafrontend/src/components/layout/ProtectedRoute.tsx:25-29
その他
パスワードハッシュ化 ✅
Supabase Auth に委譲(bcrypt + salt)。アプリ側で平文パスワードを保持しない。
CORS ⚠️
-
allowedOriginsはホワイトリスト方式 (APP_CORS_ALLOWED_ORIGINS環境変数)。 -
allowCredentials=true。 - ⚠️
allowedHeaders=List.of("*")のため、必要最低限に絞ることを推奨。
環境変数による本番/開発切り替え ✅
-
application.ymlで.env読み込み + デフォルト値。 - 本番では
APP_AUTH_COOKIE_SECURE=true,APP_CORS_ALLOWED_ORIGINS=https://...を設定する想定。
サイドチャネル対策 (Spectre 等) ✅
対策内容:
-
Cross-Origin-Opener-Policy: same-originを全レスポンスに付与し、tabnabbing と cross-window のサイドチャネルを抑制。 -
FetchMetadataFilterで/api/**への状態変更リクエスト (POST/PUT/PATCH/DELETE) についてSec-Fetch-Siteを検証し、same-origin/same-site/none以外なら 403 で拒否。CSRF トークンと多重防御を構成。 -
Sec-Fetch-Mode: navigateのトップレベル遷移は除外し、ヘッダ未送信の旧クライアントは通過(CSRF トークンで防御)。 - COEP は外部リソース連携への副作用を避けるため意図的に未対応とした。
根拠ファイル:
backend/src/main/java/com/example/studentmanagement/config/SecurityConfig.java:65-66, 80backend/src/main/java/com/example/studentmanagement/security/FetchMetadataFilter.java
補足: 配信されるセキュリティヘッダ一覧
| ヘッダ | 値 | 目的 |
|---|---|---|
X-Frame-Options |
DENY |
クリックジャッキング |
Content-Security-Policy |
default-src 'none'; frame-ancestors 'none' |
XSS 緩和 / クリックジャッキング |
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
HTTPS 強制 (HTTPS リクエスト時のみ) |
X-Content-Type-Options |
nosniff |
MIME スニッフィング抑制 |
Referrer-Policy |
strict-origin-when-cross-origin |
リファラ漏洩防止 |
Cross-Origin-Opener-Policy |
same-origin |
cross-window サイドチャネル対策 |
次回使える Security チェックリスト
新規プロジェクトや機能追加時に、本プロジェクトで実施した対策を再現するためのチェックリストです。
認証・セッション
- 認証方式を決定(JWT / セッション / OAuth など)
- パスワードは外部認証サービス or bcrypt 等でハッシュ化(平文保持禁止)
-
Spring Security を
SessionCreationPolicy.STATELESSで構成(JWT 採用時) - アクセストークン + リフレッシュトークンの 2 段構成
- ログイン後にセッション ID(またはトークン)を再発行
- ログアウト時に全認証クッキーをクリア + 外部認証サービスのログアウト API 呼び出し
-
認証 Cookie に
HttpOnly=true -
認証 Cookie に
Secure=true(本番環境)— 環境変数で開発時は false 切替 -
認証 Cookie に
SameSite=Lax以上 -
リフレッシュトークン Cookie の
Pathを/api/auth/refresh等に限定 - アクセス/リフレッシュトークンの有効期限を設定(例: refresh 30日)
-
フロント側で 401 時の自動リトライ実装(
http.ts)
CSRF
-
CookieCsrfTokenRepository.withHttpOnlyFalse()でトークン発行 -
XSRF-TOKENCookie にSameSite=Lax -
CSRF 検証対象を明示的に列挙(
/api/auth/login,/logout,/refreshなど) -
CSRF トークン取得用エンドポイント(
/api/auth/csrf)の用意 -
フロント側で
X-XSRF-TOKENヘッダを自動付与(GET/HEAD/OPTIONS/TRACE 除く) - フロント初回ロード時に CSRF トークンを取得する仕組み
SQL インジェクション
-
JdbcClient/JdbcTemplate/ JPA / MyBatis のパラメータバインディング使用 - SQL の文字列連結は禁止(コードレビューで確認)
- LIKE / ILIKE 検索もパラメータバインドで実装
- DB ユーザー権限を最小限に絞る
- エラーメッセージで SQL 文や DB 構造を露出しない
XSS
- フロントは React/Vue 等の自動エスケープ付きフレームワーク採用
-
dangerouslySetInnerHTML/v-html/innerHTMLの使用禁止 -
バックは JSON API 形式(
Content-Type: application/json;charset=UTF-8) -
入力バリデーションを
zod+react-hook-form(フロント)と@Valid+@NotBlank等(バック)で二重実装 - エラーレスポンスも JSON で返却し、HTML 埋め込みしない
認可制御(IDOR 対策含む)
-
ロール定義(例:
ADMIN/TEACHER/STAFF) - 各エンドポイントで「ログイン済み」だけでなく「権限があるか」を確認
- Service 層で明示的な権限チェック(Controller だけに任せない)
- リソース所有者チェック(teacher は担当生徒のみ等)
- DB レベルでも RLS 等で多重防御(Supabase RLS、PostgreSQL ポリシー)
-
JWT claims を DB セッション変数へ伝搬する仕組み(
RlsContextService相当) -
フロントの
ProtectedRouteでallowedRolesチェック - 動的 SQL のロール別分岐ロジックを Repository に実装
- テストで他人 ID へのアクセスが 403 になることを確認
CORS
-
allowedOriginsをホワイトリスト方式(*禁止) -
allowCredentials=true時はオリジンを必ず限定 -
allowedMethodsを必要なメソッドのみに絞る -
allowedHeadersを必要なヘッダのみに絞る(*を避ける) -
環境変数 (
APP_CORS_ALLOWED_ORIGINS等) で本番/開発を切り替え
セッション管理(Cookie 詳細)
- HttpOnly / Secure / SameSite を全認証 Cookie に設定
- Cookie の Path 属性で送信範囲を限定
- Cookie の Max-Age / Expires を適切に設定
- 開発環境向け Cookie 設定の上書き機構(環境変数)
セキュリティヘッダ(本プロジェクト実装済)
-
X-Frame-Options: DENY(またはSAMEORIGIN/ CSPframe-ancestorsで代替) -
Content-Security-Policy設定(API 専用ならdefault-src 'none'; frame-ancestors 'none') -
Strict-Transport-Security(HSTS) — HTTPS 環境で -
X-Content-Type-Options: nosniff -
Referrer-Policy: strict-origin-when-cross-origin等 -
Cross-Origin-Opener-Policy: same-origin(Spectre 系サイドチャネル対策) -
Cross-Origin-Embedder-Policy: require-corp— 外部リソース利用時は副作用注意(本プロジェクトは未採用) -
Cross-Origin-Resource-Policy— COEP とセットで検討 -
Permissions-Policy— 利用ブラウザ機能を絞りたい場合に設定
実装ポイント:
- Spring Security 6 系では
http.headers(headers -> headers.frameOptions(...).contentSecurityPolicy(...).httpStrictTransportSecurity(...))で一括設定可能。 -
frameOptions(frame -> frame.disable()) + addHeaderWriter(new XFrameOptionsHeaderWriter(DENY))のパターンでSAMEORIGINデフォルトを上書き。 - HSTS は HTTPS リクエスト時のみ送出されるため、ローカル HTTP 開発を阻害しない。
Fetch Metadata 検証
-
/api/**の状態変更リクエスト (POST/PUT/PATCH/DELETE) でSec-Fetch-Siteを検証 -
same-origin/same-site/noneのいずれかのみ通過、それ以外は 403 拒否 -
Sec-Fetch-Mode: navigateのトップレベル遷移は除外 - ヘッダ未送信の旧ブラウザ・curl 等は通過(CSRF トークンで防御)
-
レスポンスは
{"code":"FORBIDDEN","message":"..."}JSON 形式で統一 - CSRF フィルタより前段に挿入し、cross-site リクエストを早期遮断
環境変数・設定管理
-
.envを.gitignoreに登録 -
本番/開発切替を環境変数で制御(
application.ymlのデフォルト値 + override) - Supabase ANON_KEY / JWT_ISSUER などの秘匿情報を環境変数化
- 本番デプロイ時に Cookie Secure や CORS Origin が正しく上書きされることを確認
エラーハンドリング・ログ
-
統一的な
ApiExceptionHandlerで 401/403/400/500 を JSON で返却 - エラーメッセージで内部実装(テーブル名、スタックトレース)を露出しない
- 認証失敗・権限エラーは構造化ログに記録
- 機密情報(パスワード、トークン)をログに出力しない
ファイル操作(該当時)
- ファイル名やパスをユーザー入力から直接構築しない
- アップロード先ディレクトリを固定 + ファイル名を UUID 等で再生成
- パス正規化後にベースディレクトリ配下か確認
- アップロードサイズ・拡張子の制限
OS コマンド実行(該当時)
- 可能な限り使用しない(ライブラリ API で代替)
- 引数は配列形式で渡す(シェル経由を避ける)
- 入力値はホワイトリスト検証
メール送信(該当時)
- メール送信ライブラリ(Spring Boot Starter Mail 等)を使用
- Subject / From / To に改行文字を許可しない
- 送信先はアプリ側で制御(ユーザー入力で送信先を決めない)
デプロイ前最終チェック
- 本番環境で HTTPS 強制
-
APP_AUTH_COOKIE_SECURE=true -
APP_CORS_ALLOWED_ORIGINSが本番ドメインに設定 - Supabase RLS ポリシーが有効
- OS / ミドルウェア / 依存ライブラリが最新
-
gradle dependencyUpdates等で脆弱な依存がないか確認 -
npm auditでフロント依存の脆弱性確認 - バックアップ・ロールバック手順の整備
- インシデント対応手順の整備