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 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 21:45:26 +09:00
parent 31a20b0509
commit dbbde1cc45
2 changed files with 254 additions and 0 deletions

View File

@@ -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 トークンがない場合を検出します。
| パターン | 重大度 |
|---------|--------|
| `<form method="POST">` without `@csrf` | HIGH |
| `<form method="post">` without `csrf_field()` | HIGH |
| `<form method="PUT">` without CSRF | HIGH |
| `<form method="DELETE">` without CSRF | HIGH |
**脆弱なコード:**
```blade
{{-- 危険: @csrf がない --}}
<form method="POST" action="/submit">
<input type="text" name="data">
<button type="submit">送信</button>
</form>
```
**安全なコード:**
```blade
{{-- @csrf ディレクティブを使用 --}}
<form method="POST" action="/submit">
@csrf
<input type="text" name="data">
<button type="submit">送信</button>
</form>
{{-- または csrf_field() ヘルパー --}}
<form method="POST" action="/submit">
{{ csrf_field() }}
<input type="text" name="data">
<button type="submit">送信</button>
</form>
```
### 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']);
});
```
---
## 再帰的なテイント解析
リンターは、関数呼び出しを通じてユーザー入力(テイントデータ)がどのように伝播するかを追跡します。