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:
@@ -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']);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 再帰的なテイント解析
|
||||
|
||||
リンターは、関数呼び出しを通じてユーザー入力(テイントデータ)がどのように伝播するかを追跡します。
|
||||
|
||||
Reference in New Issue
Block a user