# PHP/Laravel セキュリティリンター 検出ルール一覧
このドキュメントでは、セキュリティリンターが検出できる脆弱性パターンを詳細に説明します。
---
## 目次
1. [XSS (クロスサイトスクリプティング)](#1-xss-クロスサイトスクリプティング)
2. [SQLインジェクション](#2-sqlインジェクション)
3. [コマンドインジェクション](#3-コマンドインジェクション)
4. [パストラバーサル](#4-パストラバーサル)
5. [認証セキュリティ](#5-認証セキュリティ)
6. [CSRF/セッションセキュリティ](#6-csrfセッションセキュリティ)
7. [設定セキュリティ](#7-設定セキュリティ)
8. [Laravel特有のセキュリティ](#8-laravel特有のセキュリティ)
---
## 1. XSS (クロスサイトスクリプティング)
### 1.1 Blade テンプレートの生出力
| パターン | 重大度 | 説明 |
|---------|--------|------|
| `{!! $var !!}` | HIGH | エスケープされていない変数の出力 |
| `{!! $array['key'] !!}` | HIGH | 配列アクセスの生出力 |
| `{!! $obj->prop !!}` | HIGH | プロパティアクセスの生出力 |
**安全と判定されるパターン:**
```blade
{{-- エスケープ関数でラップされている場合 --}}
{!! htmlspecialchars($var) !!}
{!! e($var) !!}
{!! htmlentities($var) !!}
{!! strip_tags($html) !!}
{{-- 文字列リテラルとエスケープ値の連結 --}}
{!! '
' . htmlspecialchars($name) . '
' !!}
{{-- Laravel 組み込みの安全な出力 --}}
{!! csrf_field() !!}
{!! method_field('PUT') !!}
{!! $errors !!}
{!! $slot !!}
{{-- Markdown プロセッサ --}}
{!! Markdown::parse($content) !!}
{!! Parsedown::instance()->text($content) !!}
{{-- サニタイズ関数 --}}
{!! clean($html) !!}
{!! sanitize($html) !!}
{!! purify($html) !!}
```
### 1.2 エスケープ破壊関数の検出
エスケープ処理を無効化する関数呼び出しを検出します。
| 関数 | 説明 |
|------|------|
| `html_entity_decode()` | HTMLエンティティをデコード |
| `htmlspecialchars_decode()` | htmlspecialchars の逆変換 |
| `urldecode()` | URLエンコードをデコード |
| `rawurldecode()` | rawurlencode の逆変換 |
| `base64_decode()` | Base64 デコード |
| `json_decode()` | JSON デコード |
| `stripslashes()` | スラッシュを除去 |
| `stripcslashes()` | C スタイルのスラッシュを除去 |
**検出例:**
```blade
{{-- 危険: エスケープ後にデコードしている --}}
{!! html_entity_decode(htmlspecialchars($data)) !!}
{!! urldecode(htmlspecialchars($url)) !!}
```
### 1.3 危険なハードコード HTML の検出
文字列リテラル内の危険な HTML を検出します。
| 検出パターン | 説明 |
|-------------|------|
| `' . htmlspecialchars($input);
}
// 安全: 危険なタグを含まない
function safeFormatter($input) {
return '' . htmlspecialchars($input) . '
';
}
```
### 1.4 JavaScript コンテキスト
| パターン | 重大度 | 説明 |
|---------|--------|------|
| `` | MEDIUM | script 内の Blade 出力 |
| `` | CRITICAL | script 内の生出力 |
| `@json($var)` in `
```
### 1.5 URL コンテキスト
`javascript:` URL によるXSSを検出します。
| パターン | 重大度 |
|---------|--------|
| `href="{{ $url }}"` | MEDIUM |
| `src="{{ $url }}"` | MEDIUM |
| `action="{{ $url }}"` | MEDIUM |
| `formaction="{{ $url }}"` | MEDIUM |
**安全と判定されるパターン:**
```blade
{{-- route() や url() ヘルパーは安全 --}}
Home
About
```
**安全なコード:**
```blade
{{-- @csrf ディレクティブを使用 --}}
{{-- または 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']);
});
```
---
## 再帰的なテイント解析
リンターは、関数呼び出しを通じてユーザー入力(テイントデータ)がどのように伝播するかを追跡します。
### テイントソース(汚染源)
```php
$_GET, $_POST, $_REQUEST, $_COOKIE, $_FILES, $_SERVER
$request->input()
$request->get()
$request->post()
$request->query()
$request->all()
$request->only()
$request->except()
$request->file()
$request->cookie()
request()->input()
Request::input()
file_get_contents('php://input')
```
### サニタイザー(浄化関数)
以下の関数を通過したデータは、対応する脆弱性に対して安全と判定されます:
```php
// XSS
htmlspecialchars(), htmlentities(), strip_tags(), e()
// SQL
addslashes(), mysqli_real_escape_string(), pg_escape_string()
// パス
basename(), realpath()
// 型キャスト
(int), (float), (bool), intval(), floatval(), boolval()
// バリデーション
validate(), validated()
```
### 解析の深度
デフォルトで最大10レベルの関数呼び出しを追跡します。
```php
// 例: 3レベルの追跡
function level1($input) { return level2($input); }
function level2($input) { return level3($input); }
function level3($input) { return $input; } // テイント
echo level1($_GET['input']); // 検出される
```
---
## 使用例
```bash
# 単一ファイルの解析
php bin/security-lint path/to/file.php
# ディレクトリの解析(再帰的な関数解析が有効)
php bin/security-lint app/
# 高重大度のみ表示
php bin/security-lint app/ -s high
# JSON 形式で出力
php bin/security-lint app/ -f json -o report.json
# 英語で出力
php bin/security-lint app/ -l en
```
---
## 重大度レベル
| レベル | 説明 |
|--------|------|
| **CRITICAL** | 即時対応が必要。直接的な攻撃が可能 |
| **HIGH** | 早急な対応が必要。重大な脆弱性 |
| **MEDIUM** | 計画的な対応が必要。潜在的なリスク |
| **LOW** | 改善推奨。ベストプラクティス違反 |
---
## 制限事項
1. **静的解析の限界**: 実行時の値は判定できません
2. **動的なメソッド呼び出し**: `$obj->$method()` は追跡困難
3. **外部ライブラリ**: vendor 内のコードは解析対象外
4. **フレームワーク固有の機能**: 一部のLaravel固有パターンは検出できない場合があります
5. **偽陽性**: 安全なコードが危険と判定される場合があります
---
## バージョン
- バージョン: 0.0.1
- 対応 PHP バージョン: 8.1+
- 対応 Laravel バージョン: 9.x, 10.x, 11.x