From dbbde1cc455725231136813afd94eef8c414c438 Mon Sep 17 00:00:00 2001 From: Yutaka Kurosaki Date: Sat, 31 Jan 2026 21:45:26 +0900 Subject: [PATCH] Add documentation for Laravel-specific security detection - Add section 8 to DETECTION_RULES.md covering: - Mass Assignment detection patterns - Raw SQL injection detection - CSRF protection checks - File upload validation rules - Route authentication middleware - Rate limiting detection - Update README.md with Laravel-specific security in detectable vulnerabilities section (ja/en) Co-Authored-By: Claude Opus 4.5 --- README.md | 18 +++ docs/DETECTION_RULES.md | 236 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) diff --git a/README.md b/README.md index 23b93ce..51a2035 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,15 @@ Place `.security-lint.json` in your project root to persist settings: - Insecure unserialize - Sensitive information logging +#### Laravel-Specific Security + +- Mass Assignment (missing $fillable/$guarded, using $request->all()) +- Raw SQL injection (DB::raw, whereRaw without bindings) +- CSRF protection (forms without @csrf) +- File upload validation (extensions-only without mimes) +- Route authentication (sensitive routes without auth middleware) +- Rate limiting (auth routes without throttle middleware) + ### Output Example ``` @@ -493,6 +502,15 @@ php bin/security-lint app/ -l en - 安全でない unserialize - 機密情報のログ出力 +#### Laravel特有のセキュリティ + +- Mass Assignment ($fillable/$guarded の欠落、$request->all() の使用) +- Raw SQL インジェクション (DB::raw、バインディングなしの whereRaw) +- CSRF 保護 (@csrf のないフォーム) +- ファイルアップロード検証 (mimes なしの extensions のみ) +- ルート認証 (auth ミドルウェアのないセンシティブなルート) +- レート制限 (throttle ミドルウェアのない認証ルート) + ### 出力例 ``` diff --git a/docs/DETECTION_RULES.md b/docs/DETECTION_RULES.md index 5ceee80..14b5b91 100644 --- a/docs/DETECTION_RULES.md +++ b/docs/DETECTION_RULES.md @@ -13,6 +13,7 @@ 5. [認証セキュリティ](#5-認証セキュリティ) 6. [CSRF/セッションセキュリティ](#6-csrfセッションセキュリティ) 7. [設定セキュリティ](#7-設定セキュリティ) +8. [Laravel特有のセキュリティ](#8-laravel特有のセキュリティ) --- @@ -751,6 +752,241 @@ unserialize($data, ['allowed_classes' => [AllowedClass::class]]); --- +## 8. Laravel特有のセキュリティ + +Laravel フレームワーク固有の脆弱性パターンを検出します。 + +### 8.1 Mass Assignment(一括代入) + +Eloquent モデルで `$fillable` または `$guarded` プロパティが定義されていない場合を検出します。 + +| パターン | 重大度 | +|---------|--------| +| Model without `$fillable` or `$guarded` | HIGH | +| `Model::create($request->all())` | HIGH | +| `$model->fill($request->all())` | HIGH | +| `$model->update($request->all())` | HIGH | + +**脆弱なコード:** +```php +// 危険: $fillable/$guarded がない +class User extends Model +{ + // Mass Assignment 脆弱性 +} + +// 危険: $request->all() を直接使用 +User::create($request->all()); +$user->update($request->all()); +``` + +**安全なコード:** +```php +// $fillable を定義 +class User extends Model +{ + protected $fillable = ['name', 'email']; +} + +// または $guarded を定義 +class User extends Model +{ + protected $guarded = ['id', 'is_admin']; +} + +// 特定のフィールドのみ使用 +User::create($request->only(['name', 'email'])); +$user->update($request->validated()); +``` + +### 8.2 Raw SQL インジェクション + +Laravel のクエリビルダーで生SQLを使用し、パラメータバインディングがない場合を検出します。 + +| パターン | 重大度 | +|---------|--------| +| `DB::raw($variable)` | HIGH | +| `whereRaw($sql)` (バインディングなし) | HIGH | +| `selectRaw($sql)` (バインディングなし) | HIGH | +| `orderByRaw($sql)` (バインディングなし) | HIGH | +| `havingRaw($sql)` (バインディングなし) | HIGH | +| `groupByRaw($sql)` (バインディングなし) | HIGH | + +**脆弱なコード:** +```php +// 危険: 変数を含む DB::raw +$column = $request->input('column'); +DB::table('users')->select(DB::raw($column))->get(); + +// 危険: バインディングなしの whereRaw +$name = $request->input('name'); +User::whereRaw("name = '$name'")->get(); + +// 危険: バインディングなしの orderByRaw +$order = $request->input('order'); +User::orderByRaw($order)->get(); +``` + +**安全なコード:** +```php +// パラメータバインディングを使用 +User::whereRaw('name = ?', [$name])->get(); +User::orderByRaw('FIELD(status, ?, ?, ?)', ['active', 'pending', 'inactive'])->get(); + +// クエリビルダーを使用(推奨) +User::where('name', $name)->get(); +User::orderBy($allowedColumns[$request->input('column')])->get(); +``` + +### 8.3 CSRF 保護 + +POST/PUT/PATCH/DELETE メソッドのフォームに CSRF トークンがない場合を検出します。 + +| パターン | 重大度 | +|---------|--------| +| `
` without `@csrf` | HIGH | +| `` without `csrf_field()` | HIGH | +| `` without CSRF | HIGH | +| `` without CSRF | HIGH | + +**脆弱なコード:** +```blade +{{-- 危険: @csrf がない --}} + + + +
+``` + +**安全なコード:** +```blade +{{-- @csrf ディレクティブを使用 --}} +
+ @csrf + + +
+ +{{-- または csrf_field() ヘルパー --}} +
+ {{ csrf_field() }} + + +
+``` + +### 8.4 ファイルアップロード検証 + +ファイルアップロードで `extensions` ルールのみを使用し、`mimes` または `mimetypes` ルールがない場合を検出します。拡張子のみのチェックは簡単にバイパスできます。 + +| パターン | 重大度 | +|---------|--------| +| `'file' => 'extensions:jpg,png'` (mimes なし) | MEDIUM | +| `'file' => ['extensions:jpg', 'file']` (mimes なし) | MEDIUM | + +**脆弱なコード:** +```php +// 危険: 拡張子のみで検証 +public function rules() +{ + return [ + 'avatar' => ['required', 'file', 'extensions:jpg,png'], + 'document' => 'file|extensions:pdf,doc', + ]; +} +``` + +**安全なコード:** +```php +// mimes または mimetypes を併用 +public function rules() +{ + return [ + // mimes を使用(推奨) + 'avatar' => ['required', 'file', 'mimes:jpeg,png'], + + // extensions と mimes を両方使用 + 'document' => ['file', 'extensions:pdf,doc', 'mimes:pdf,msword'], + + // mimetypes を使用 + 'video' => ['file', 'mimetypes:video/mp4,video/mpeg'], + ]; +} +``` + +### 8.5 ルート認証ミドルウェア + +センシティブなルートに `auth` ミドルウェアがない場合を検出します。 + +| パターン | 重大度 | +|---------|--------| +| `Route::*('/admin/*')` without auth | LOW | +| `Route::*('/dashboard/*')` without auth | LOW | +| `Route::*('/user/*')` without auth | LOW | +| `Route::*('/account/*')` without auth | LOW | +| `Route::*('/settings/*')` without auth | LOW | +| `Route::*('/profile/*')` without auth | LOW | + +**検出対象のセンシティブなルートパターン:** +- `/admin/`, `/dashboard/`, `/user/`, `/users/` +- `/account/`, `/settings/`, `/profile/` +- `/manage/`, `/management/`, `/billing/` + +**脆弱なコード:** +```php +// 危険: 認証なしで管理画面にアクセス可能 +Route::get('/admin/dashboard', [AdminController::class, 'index']); +Route::get('/users/manage', [UserController::class, 'manage']); +``` + +**安全なコード:** +```php +// ルートにミドルウェアを追加 +Route::get('/admin/dashboard', [AdminController::class, 'index']) + ->middleware('auth'); + +// グループでミドルウェアを適用(推奨) +Route::middleware(['auth'])->group(function () { + Route::get('/admin/dashboard', [AdminController::class, 'index']); + Route::get('/users/manage', [UserController::class, 'manage']); +}); +``` + +### 8.6 レート制限 + +認証関連のルートに `throttle` ミドルウェアがない場合を検出します。ブルートフォース攻撃を防ぐために重要です。 + +| パターン | 重大度 | +|---------|--------| +| `Route::post('/login')` without throttle | LOW | +| `Route::post('/register')` without throttle | LOW | +| `Route::post('/password/reset')` without throttle | LOW | +| `Route::post('/password/email')` without throttle | LOW | +| `Route::post('/forgot-password')` without throttle | LOW | +| `Route::post('/2fa/*')` without throttle | LOW | + +**脆弱なコード:** +```php +// 危険: レート制限なしでブルートフォース可能 +Route::post('/login', [AuthController::class, 'login']); +Route::post('/password/reset', [PasswordController::class, 'reset']); +``` + +**安全なコード:** +```php +// throttle ミドルウェアを追加 +Route::post('/login', [AuthController::class, 'login']) + ->middleware('throttle:5,1'); // 1分間に5回まで + +// グループでレート制限を適用 +Route::middleware(['throttle:60,1'])->group(function () { + Route::post('/login', [AuthController::class, 'login']); + Route::post('/password/reset', [PasswordController::class, 'reset']); +}); +``` + +--- + ## 再帰的なテイント解析 リンターは、関数呼び出しを通じてユーザー入力(テイントデータ)がどのように伝播するかを追跡します。