Files
php-security-linter/docs/DETECTION_RULES.md
Yutaka Kurosaki 208227b77e Prepare for OSS release v0.0.1
Version updates:
- Set version to 0.0.1 across all files
- Update CLI banner, SARIF output, and documentation

New files:
- LICENSE: MIT license
- CHANGELOG.md: Initial changelog with all features
- CONTRIBUTING.md: Contribution guidelines

composer.json enhancements:
- Add version, keywords, homepage, support URLs
- Add authors section
- Add require-dev for PHPUnit

README.md updates:
- Update repository URLs to security-linter/php-laravel
- Update Docker image references

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 11:57:18 +09:00

31 KiB
Raw Blame History

PHP/Laravel セキュリティリンター 検出ルール一覧

このドキュメントでは、セキュリティリンターが検出できる脆弱性パターンを詳細に説明します。


目次

  1. XSS (クロスサイトスクリプティング)
  2. SQLインジェクション
  3. コマンドインジェクション
  4. パストラバーサル
  5. 認証セキュリティ
  6. CSRF/セッションセキュリティ
  7. 設定セキュリティ
  8. Laravel特有のセキュリティ

1. XSS (クロスサイトスクリプティング)

1.1 Blade テンプレートの生出力

パターン 重大度 説明
{!! $var !!} HIGH エスケープされていない変数の出力
{!! $array['key'] !!} HIGH 配列アクセスの生出力
{!! $obj->prop !!} HIGH プロパティアクセスの生出力

安全と判定されるパターン:

{{-- エスケープ関数でラップされている場合 --}}
{!! htmlspecialchars($var) !!}
{!! e($var) !!}
{!! htmlentities($var) !!}
{!! strip_tags($html) !!}

{{-- 文字列リテラルとエスケープ値の連結 --}}
{!! '<div>' . htmlspecialchars($name) . '</div>' !!}

{{-- 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 スタイルのスラッシュを除去

検出例:

{{-- 危険: エスケープ後にデコードしている --}}
{!! html_entity_decode(htmlspecialchars($data)) !!}
{!! urldecode(htmlspecialchars($url)) !!}

1.3 危険なハードコード HTML の検出

文字列リテラル内の危険な HTML を検出します。

検出パターン 説明
<script> スクリプトタグ
<iframe> iframe タグ
<object>, <embed> プラグインタグ
<meta>, <link>, <base> メタ情報タグ
<form> フォームタグ(フィッシング)
onclick=, onerror= イベントハンドラ
javascript: JavaScript プロトコル
vbscript: VBScript プロトコル
data:...;base64 Data URL

検出例:

// 危険: ハードコードされた script タグ
function badFormatter($input) {
    return '<script>alert("XSS")</script>' . htmlspecialchars($input);
}

// 安全: 危険なタグを含まない
function safeFormatter($input) {
    return '<div class="wrapper">' . htmlspecialchars($input) . '</div>';
}

1.4 JavaScript コンテキスト

パターン 重大度 説明
<script>{{ $var }}</script> MEDIUM script 内の Blade 出力
<script>{!! $var !!}</script> CRITICAL script 内の生出力
@json($var) in <script> LOW JSON ディレクティブの使用

推奨される対策:

{{-- 推奨: Js::from() を使用 --}}
<script>
    var data = {{ Js::from($data) }};
</script>

1.5 URL コンテキスト

javascript: URL によるXSSを検出します。

パターン 重大度
href="{{ $url }}" MEDIUM
src="{{ $url }}" MEDIUM
action="{{ $url }}" MEDIUM
formaction="{{ $url }}" MEDIUM

安全と判定されるパターン:

{{-- route() や url() ヘルパーは安全 --}}
<a href="{{ route('home') }}">Home</a>
<a href="{{ url('/about') }}">About</a>
<form action="{{ action('Controller@method') }}">

1.6 イベントハンドラ

パターン 重大度
onclick="{{ $var }}" HIGH
onerror="{{ $var }}" HIGH
onload="{{ $var }}" HIGH
onclick="{!! $var !!}" CRITICAL

推奨される対策:

{{-- 危険 --}}
<button onclick="doAction('{{ $action }}')">Click</button>

{{-- 推奨: data 属性を使用 --}}
<button data-action="{{ $action }}" class="js-action">Click</button>
<script>
document.querySelector('.js-action').addEventListener('click', function() {
    doAction(this.dataset.action);
});
</script>

1.7 Style インジェクション

パターン 重大度
style="{{ $css }}" MEDIUM
style="color: {{ $color }}" MEDIUM

1.8 引用符なし属性

パターン 重大度
data-value={{ $var }} LOW

1.9 テンプレートインジェクション

パターン 重大度
@include($var) HIGH
@extends($var) HIGH
@component($var) HIGH
@each($var, ...) HIGH

1.10 SVG コンテキスト

パターン 重大度
<svg>{{ $var }}</svg> MEDIUM

1.11 @php ブロック

パターン 重大度
@php echo $var; @endphp MEDIUM

1.12 PHP の echo/print

パターン 重大度
echo $taintedVar; HIGH
print $taintedVar; HIGH
printf("%s", $taintedVar) HIGH

1.13 ユーザー定義関数の再帰解析

リンターは、ユーザー定義関数を再帰的に解析して、戻り値がエスケープされているかを判定します。

// 安全と判定: 戻り値が常にエスケープされている
function safeFormatter($input) {
    return htmlspecialchars(trim($input));
}

// 危険と判定: エスケープされていない戻り値がある
function unsafeFormatter($input) {
    return trim($input);  // エスケープなし
}

// 危険と判定: エスケープを壊している
function breakEscaping($escaped) {
    return html_entity_decode($escaped);
}

2. SQLインジェクション

2.1 Laravel クエリビルダー

パターン 重大度
DB::raw($userInput) HIGH
DB::select($query . $userInput) HIGH
->whereRaw($userInput) HIGH
->selectRaw($userInput) HIGH
->orderByRaw($userInput) HIGH
->havingRaw($userInput) HIGH
->groupByRaw($userInput) HIGH

安全なパターン:

// パラメータバインディングを使用
DB::select('SELECT * FROM users WHERE id = ?', [$id]);
$query->whereRaw('column = ?', [$value]);

2.2 PDO

パターン 重大度
$pdo->query($sql . $input) HIGH
$pdo->exec($sql . $input) HIGH
$pdo->prepare($sql . $input) HIGH

2.3 MySQLi

パターン 重大度
$mysqli->query($sql . $input) HIGH
mysqli_query($conn, $sql . $input) HIGH

2.4 文字列連結によるクエリ構築

パターン 重大度
"SELECT * FROM users WHERE id = " . $id HIGH
"SELECT * FROM users WHERE id = {$id}" HIGH

2.5 SQLサニタイザー関数の検出

リンターは以下のサニタイザー関数を認識し、適切に使用されている場合は安全と判定します。

関数/メソッド 説明
intval(), floatval() 型キャスト(最も安全)
(int), (float) 型キャスト演算子
mysqli_real_escape_string() MySQLi エスケープ
PDO::quote() PDO エスケープ
pg_escape_string(), pg_escape_literal() PostgreSQL エスケープ
addslashes() 基本的なエスケープ(非推奨)
filter_var() + FILTER_VALIDATE_INT 検証フィルター

安全と判定されるパターン:

// 型キャストによるサニタイズ
$query = "SELECT * FROM users WHERE id = " . intval($id);
$query = "SELECT * FROM users WHERE id = " . (int)$id;

// エスケープ関数
$query = "SELECT * FROM users WHERE name = '" . mysqli_real_escape_string($conn, $name) . "'";
$query = "SELECT * FROM users WHERE name = " . $pdo->quote($name);

// ユーザー定義サニタイザー関数
function sanitizeId($input) {
    return intval($input);
}
$query = "SELECT * FROM users WHERE id = " . sanitizeId($userId);

2.6 SQLサニタイザー破壊パターン

サニタイズ後に以下の関数を使用すると、サニタイズが無効化されます。

関数 説明
stripslashes() エスケープを解除
urldecode() URLデコードで特殊文字を復活
html_entity_decode() HTMLエンティティをデコード
base64_decode() Base64 デコード
sprintf() フォーマット文字列で迂回可能

危険なパターン:

// 危険: サニタイズ後にデコード
$safe = mysqli_real_escape_string($conn, $input);
$unsafe = stripslashes($safe);  // エスケープを解除

// 危険: urldecode でエスケープを迂回
$safe = addslashes($input);
$unsafe = urldecode($safe);  // %27 -> ' に変換

2.7 ユーザー定義関数の再帰解析

リンターはユーザー定義関数を再帰的に解析し、安全かどうかを判定します。

// 安全と判定: 戻り値が常にサニタイズされている
function getIntParam($key) {
    return intval($_GET[$key] ?? 0);
}

// 危険と判定: サニタイズを破壊している
function processInput($input) {
    $escaped = mysqli_real_escape_string($this->conn, $input);
    return stripslashes($escaped);  // エスケープを解除!
}

3. コマンドインジェクション

3.1 シェル実行関数

関数 重大度
exec($cmd . $input) CRITICAL
shell_exec($cmd . $input) CRITICAL
system($cmd . $input) CRITICAL
passthru($cmd . $input) CRITICAL
proc_open($cmd . $input, ...) CRITICAL
popen($cmd . $input, ...) CRITICAL
`$cmd $input` (バッククォート) CRITICAL

3.2 コード実行関数

関数 重大度
eval($code) CRITICAL
create_function($args, $code) CRITICAL
assert($expr) (文字列引数) CRITICAL
preg_replace('/.../e', ...) CRITICAL

3.3 コールバック関数

関数 重大度
call_user_func($userCallback) HIGH
call_user_func_array($userCallback, ...) HIGH
array_map($userCallback, ...) HIGH
array_filter($arr, $userCallback) HIGH

3.4 ファイルインクルード

関数 重大度
include($userPath) CRITICAL
include_once($userPath) CRITICAL
require($userPath) CRITICAL
require_once($userPath) CRITICAL

3.5 Symfony Process

パターン 重大度
Process::fromShellCommandline($cmd . $input) HIGH
new Process($stringCmd . $input) HIGH

安全なパターン:

// 配列引数を使用
$process = new Process(['command', $arg1, $arg2]);

3.6 Laravel Artisan

パターン 重大度
Artisan::call($userCommand) HIGH

3.7 コマンドサニタイザー関数の検出

リンターは以下のサニタイザー関数を認識し、適切に使用されている場合は安全と判定します。

関数 説明
escapeshellarg() 単一の引数をエスケープ
escapeshellcmd() コマンド全体をエスケープ
basename() ファイル名部分のみ抽出
intval(), floatval() 型キャスト

安全と判定されるパターン:

// escapeshellarg で引数をエスケープ
exec('ls -la ' . escapeshellarg($path));

// escapeshellcmd でコマンド全体をエスケープ
exec(escapeshellcmd($command) . ' ' . escapeshellarg($arg));

// 型キャストで数値に限定
exec('kill ' . intval($pid));

// 配列引数による安全な Process 使用
$process = new Process(['convert', $inputFile, $outputFile]);

3.8 コマンドサニタイザー破壊パターン

サニタイズ後に以下の関数を使用すると、サニタイズが無効化されます。

関数 説明
stripslashes() エスケープを解除
urldecode() URLデコードで特殊文字を復活
str_replace() エスケープ文字を削除可能
preg_replace() エスケープ文字を削除可能
sprintf() フォーマット文字列で迂回可能

危険なパターン:

// 危険: サニタイズ後にデコード
$safe = escapeshellarg($input);
$unsafe = urldecode($safe);  // %20 -> スペースに変換

// 危険: サニタイズを壊す置換
$safe = escapeshellarg($input);
$unsafe = str_replace("'", "", $safe);  // エスケープを除去

// 危険: sprintf でフォーマット文字列攻撃
$cmd = sprintf($userFormat, escapeshellarg($arg));  // $userFormat が制御可能

3.9 ユーザー定義関数の再帰解析

リンターはユーザー定義関数を再帰的に解析し、安全かどうかを判定します。

// 安全と判定: 戻り値が常にサニタイズされている
function safeCommand($input) {
    return escapeshellarg(trim($input));
}

// 危険と判定: サニタイズを破壊している
function processCommand($input) {
    $escaped = escapeshellarg($input);
    return urldecode($escaped);  // エスケープを解除!
}

4. パストラバーサル

4.1 ファイル操作関数

関数 重大度
file_get_contents($userPath) HIGH
file_put_contents($userPath, ...) HIGH
fopen($userPath, ...) HIGH
readfile($userPath) HIGH
unlink($userPath) HIGH
copy($userPath, ...) HIGH
rename($userPath, ...) HIGH

4.2 ファイルアップロード

パターン 重大度
move_uploaded_file($tmp, $userDest) HIGH

4.3 Laravel Storage

パターン 重大度
Storage::get($userPath) MEDIUM
Storage::put($userPath, ...) MEDIUM
Storage::delete($userPath) MEDIUM
Storage::download($userPath) MEDIUM

4.4 レスポンスダウンロード

パターン 重大度
response()->download($userPath) HIGH
response()->file($userPath) HIGH

4.5 パスサニタイザー関数の検出

リンターは以下のサニタイザー関数を認識し、適切に使用されている場合は安全と判定します。

関数 説明
basename() ディレクトリ成分を除去(最も効果的)
realpath() パスを正規化し、存在を確認
pathinfo(..., PATHINFO_BASENAME) ファイル名部分のみ抽出
intval() 数値IDに限定
Str::random(), Str::uuid() 安全なファイル名生成
$file->hashName() ハッシュベースの安全なファイル名

安全と判定されるパターン:

// basename でディレクトリを除去
$filename = basename($userInput);
file_get_contents($basePath . '/' . $filename);

// realpath で検証
$path = realpath($basePath . '/' . $userInput);
if ($path && str_starts_with($path, $basePath)) {
    file_get_contents($path);
}

// 数値IDに限定
$content = file_get_contents('/docs/' . intval($id) . '.txt');

// 安全なファイル名生成
$filename = Str::random(40) . '.pdf';
Storage::put($filename, $content);

// ハッシュベースのファイル名
$path = $request->file('upload')->hashName();

4.6 危険なパストラバーサルパターン

以下のパターンが文字列リテラルに含まれている場合、警告します。

パターン 説明
.. ディレクトリトラバーサル
../, ..\\ Unix/Windows トラバーサル
%2e%2e URLエンコードされた ..
%252e%252e ダブルURLエンコード
..%c0%af, ..%c1%9c オーバーロング UTF-8 エンコード

4.7 パスサニタイザー破壊パターン

サニタイズ後に以下の関数を使用すると、サニタイズが無効化されます。

関数 説明
urldecode() %2e%2e..
rawurldecode() URLデコード
base64_decode() Base64 でトラバーサルを隠蔽可能
hex2bin() 16進数でトラバーサルを隠蔽可能
html_entity_decode() HTMLエンティティをデコード
chr() 文字を構築可能

危険なパターン:

// 危険: サニタイズ後にデコード
$safe = basename($userInput);
$unsafe = urldecode($safe);  // %2e%2e -> .. に変換

// 危険: base64 でトラバーサルを隠蔽
$path = base64_decode($userInput);  // Li4vLi4vZXRjL3Bhc3N3ZA== -> ../../etc/passwd

// 危険: サニタイズ前に getClientOriginalName を使用
$filename = $file->getClientOriginalName();  // ユーザー制御!
Storage::put($filename, $content);  // トラバーサル可能

4.8 ユーザー定義関数の再帰解析

リンターはユーザー定義関数を再帰的に解析し、安全かどうかを判定します。

// 安全と判定: 戻り値が常にサニタイズされている
function safePath($input) {
    return basename(trim($input));
}

// 危険と判定: サニタイズを破壊している
function processPath($input) {
    $safe = basename($input);
    return urldecode($safe);  // サニタイズを解除!
}

// 安全と判定: 検証ロジックを含む
function validatePath($input, $baseDir) {
    $path = realpath($baseDir . '/' . $input);
    if ($path && str_starts_with($path, $baseDir)) {
        return $path;
    }
    return null;
}

5. 認証セキュリティ

5.1 弱いハッシュアルゴリズム

関数 重大度
md5($password) HIGH
sha1($password) HIGH
sha256($password) MEDIUM
hash('md5', $password) HIGH

5.2 不適切なパスワードハッシュ

パターン 重大度
password_hash($pw, PASSWORD_MD5) HIGH
password_hash($pw, ..., ['cost' => 4]) MEDIUM

5.3 ハードコードされた認証情報

パターン 重大度
$password = 'secret123' HIGH
$apiKey = 'sk-xxx...' HIGH
['password' => 'hardcoded'] HIGH

5.4 タイミング攻撃

パターン 重大度
$token == $userToken MEDIUM
strcmp($secret, $input) MEDIUM

推奨:

// 定時間比較を使用
hash_equals($expected, $actual);
password_verify($password, $hash);

5.5 Base64 エンコード

パターン 重大度
base64_encode($password) HIGH

6. CSRF/セッションセキュリティ

6.1 CSRF トークン

パターン 重大度
<form method="POST"> without @csrf HIGH
Form missing csrf_field() HIGH

6.2 メソッドスプーフィング

パターン 重大度
PUT/PATCH/DELETE form without @method LOW

6.3 セッション設定

パターン 重大度
session_start() without options MEDIUM
Missing cookie_httponly MEDIUM
Missing cookie_secure MEDIUM
Missing cookie_samesite MEDIUM

6.4 セッション固定化

パターン 重大度
session_regenerate_id() without true MEDIUM
session_regenerate_id(false) MEDIUM
パターン 重大度
Cookie without httponly MEDIUM
Cookie without secure MEDIUM
Cookie without samesite MEDIUM

7. 設定セキュリティ

7.1 デバッグ出力

関数 重大度
phpinfo() HIGH
var_dump($var) MEDIUM
print_r($var) MEDIUM
dd($var) MEDIUM
dump($var) MEDIUM

7.2 エラー表示

パターン 重大度
error_reporting(-1) MEDIUM
ini_set('display_errors', '1') MEDIUM
ini_set('display_startup_errors', '1') MEDIUM

7.3 安全でないデシリアライズ

パターン 重大度
unserialize($data) HIGH
unserialize($data, ['allowed_classes' => true]) HIGH

安全なパターン:

unserialize($data, ['allowed_classes' => false]);
unserialize($data, ['allowed_classes' => [AllowedClass::class]]);

7.4 機密情報のログ出力

パターン 重大度
Log::info($password) MEDIUM
logger($apiKey) MEDIUM

7.5 ハードコードされた設定

パターン 重大度
'debug' => true MEDIUM
'key' => 'hardcoded-secret' HIGH

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

脆弱なコード:

// 危険: $fillable/$guarded がない
class User extends Model
{
    // Mass Assignment 脆弱性
}

// 危険: $request->all() を直接使用
User::create($request->all());
$user->update($request->all());

安全なコード:

// $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

脆弱なコード:

// 危険: 変数を含む 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();

安全なコード:

// パラメータバインディングを使用
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

脆弱なコード:

{{-- 危険: @csrf がない --}}
<form method="POST" action="/submit">
    <input type="text" name="data">
    <button type="submit">送信</button>
</form>

安全なコード:

{{-- @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

脆弱なコード:

// 危険: 拡張子のみで検証
public function rules()
{
    return [
        'avatar' => ['required', 'file', 'extensions:jpg,png'],
        'document' => 'file|extensions:pdf,doc',
    ];
}

安全なコード:

// 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/

脆弱なコード:

// 危険: 認証なしで管理画面にアクセス可能
Route::get('/admin/dashboard', [AdminController::class, 'index']);
Route::get('/users/manage', [UserController::class, 'manage']);

安全なコード:

// ルートにミドルウェアを追加
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

脆弱なコード:

// 危険: レート制限なしでブルートフォース可能
Route::post('/login', [AuthController::class, 'login']);
Route::post('/password/reset', [PasswordController::class, 'reset']);

安全なコード:

// 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']);
});

再帰的なテイント解析

リンターは、関数呼び出しを通じてユーザー入力(テイントデータ)がどのように伝播するかを追跡します。

テイントソース(汚染源)

$_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')

サニタイザー(浄化関数)

以下の関数を通過したデータは、対応する脆弱性に対して安全と判定されます:

// 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レベルの関数呼び出しを追跡します。

// 例: 3レベルの追跡
function level1($input) { return level2($input); }
function level2($input) { return level3($input); }
function level3($input) { return $input; }  // テイント

echo level1($_GET['input']);  // 検出される

使用例

# 単一ファイルの解析
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