Initial commit: PHP/Laravel Security Linter v1.0.0
A static security analysis tool for PHP and Laravel applications with recursive taint analysis capabilities. Features: - Comprehensive vulnerability detection (XSS, SQL Injection, Command Injection, Path Traversal, CSRF, Authentication issues) - Recursive taint analysis across function calls - Blade template analysis with context-aware XSS detection - Smart escape detection and escape bypass detection - Syntax highlighting in terminal output - Multi-language support (Japanese/English) - Docker support for easy deployment - Multiple output formats (text, JSON, HTML, SARIF, Markdown) - CI/CD integration ready (GitHub Actions, GitLab CI) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
290
docs/QUICK_REFERENCE.md
Normal file
290
docs/QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# セキュリティリンター クイックリファレンス
|
||||
|
||||
## XSS 検出パターン
|
||||
|
||||
### Blade テンプレート
|
||||
|
||||
```
|
||||
重大度: CRITICAL
|
||||
├── <script>{!! $var !!}</script> # JS内の生出力
|
||||
└── onclick="{!! $var !!}" # イベントハンドラ内の生出力
|
||||
|
||||
重大度: HIGH
|
||||
├── {!! $var !!} # 生出力
|
||||
├── {!! func($var) !!} # 関数がエスケープしない場合
|
||||
├── {!! html_entity_decode(...) !!} # エスケープ破壊
|
||||
├── {!! '<script>...' . $var !!} # 危険なハードコードHTML
|
||||
├── onclick="{{ $var }}" # イベントハンドラ
|
||||
└── @include($var) # テンプレートインジェクション
|
||||
|
||||
重大度: MEDIUM
|
||||
├── <script>{{ $var }}</script> # JS コンテキスト
|
||||
├── href="{{ $var }}" # URL コンテキスト
|
||||
├── style="{{ $var }}" # CSS インジェクション
|
||||
└── <svg>{{ $var }}</svg> # SVG コンテキスト
|
||||
|
||||
重大度: LOW
|
||||
├── data-x={{ $var }} # 引用符なし属性
|
||||
└── @json($var) in <script> # JSON ディレクティブ
|
||||
```
|
||||
|
||||
### 安全なパターン(検出されない)
|
||||
|
||||
```blade
|
||||
{!! htmlspecialchars($var) !!} ✓ エスケープ関数
|
||||
{!! e($var) !!} ✓ Laravel ヘルパー
|
||||
{!! '<div>' . e($var) . '</div>' !!} ✓ 安全な連結
|
||||
{!! csrf_field() !!} ✓ 組み込み関数
|
||||
{!! $errors !!} ✓ Laravel 組み込み
|
||||
{!! Markdown::parse($var) !!} ✓ Markdown プロセッサ
|
||||
{!! safeFunc($var) !!} ✓ エスケープを返す関数
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SQLインジェクション 検出パターン
|
||||
|
||||
```
|
||||
重大度: HIGH
|
||||
├── DB::raw("... $var ...")
|
||||
├── DB::select("... $var ...")
|
||||
├── ->whereRaw("... $var ...")
|
||||
├── ->orderByRaw($var)
|
||||
├── $pdo->query("... $var ...")
|
||||
├── $mysqli->query("... $var ...")
|
||||
└── "SELECT * FROM t WHERE id = $var"
|
||||
```
|
||||
|
||||
### SQLサニタイザー関数(これらは安全と判定)
|
||||
|
||||
```php
|
||||
intval($var), (int)$var ✓ 型キャスト
|
||||
floatval($var), (float)$var ✓ 型キャスト
|
||||
mysqli_real_escape_string($conn, $var) ✓ エスケープ
|
||||
$pdo->quote($var) ✓ PDO エスケープ
|
||||
filter_var($var, FILTER_VALIDATE_INT) ✓ 検証
|
||||
```
|
||||
|
||||
### SQLサニタイザー破壊パターン(危険)
|
||||
|
||||
```php
|
||||
stripslashes(mysqli_real_escape_string(...)) ✗
|
||||
urldecode(addslashes($var)) ✗
|
||||
html_entity_decode($pdo->quote($var)) ✗
|
||||
```
|
||||
|
||||
### 安全なパターン
|
||||
|
||||
```php
|
||||
DB::select('SELECT * FROM t WHERE id = ?', [$id]); ✓
|
||||
$query->where('column', '=', $value); ✓
|
||||
$query->whereRaw('col = ?', [$value]); ✓
|
||||
"SELECT * FROM t WHERE id = " . intval($id) ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## コマンドインジェクション 検出パターン
|
||||
|
||||
```
|
||||
重大度: CRITICAL
|
||||
├── exec($cmd . $input)
|
||||
├── shell_exec($cmd . $input)
|
||||
├── system($cmd . $input)
|
||||
├── `$cmd $input`
|
||||
├── eval($code)
|
||||
├── include($userPath)
|
||||
└── preg_replace('/.../e', ...)
|
||||
|
||||
重大度: HIGH
|
||||
├── call_user_func($userCallback)
|
||||
├── Process::fromShellCommandline($cmd)
|
||||
└── Artisan::call($userCmd)
|
||||
```
|
||||
|
||||
### コマンドサニタイザー関数(これらは安全と判定)
|
||||
|
||||
```php
|
||||
escapeshellarg($arg) ✓ 引数エスケープ
|
||||
escapeshellcmd($cmd) ✓ コマンドエスケープ
|
||||
basename($path) ✓ ファイル名のみ
|
||||
intval($var) ✓ 型キャスト
|
||||
```
|
||||
|
||||
### コマンドサニタイザー破壊パターン(危険)
|
||||
|
||||
```php
|
||||
urldecode(escapeshellarg($var)) ✗
|
||||
str_replace("'", "", escapeshellarg($var)) ✗
|
||||
stripslashes(escapeshellcmd($var)) ✗
|
||||
```
|
||||
|
||||
### 安全なパターン
|
||||
|
||||
```php
|
||||
$process = new Process(['cmd', $arg1, $arg2]); ✓
|
||||
exec(escapeshellcmd($cmd) . ' ' . escapeshellarg($a)); ✓
|
||||
exec('ls -la ' . escapeshellarg($path)); ✓
|
||||
exec('kill ' . intval($pid)); ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## パストラバーサル 検出パターン
|
||||
|
||||
```
|
||||
重大度: HIGH
|
||||
├── file_get_contents($userPath)
|
||||
├── fopen($userPath, 'r')
|
||||
├── unlink($userPath)
|
||||
├── move_uploaded_file($tmp, $userDest)
|
||||
└── response()->download($userPath)
|
||||
|
||||
重大度: MEDIUM
|
||||
├── Storage::get($userPath)
|
||||
└── Storage::put($userPath, $content)
|
||||
```
|
||||
|
||||
### パスサニタイザー関数(これらは安全と判定)
|
||||
|
||||
```php
|
||||
basename($path) ✓ ディレクトリ除去
|
||||
realpath($path) ✓ パス正規化
|
||||
pathinfo($path, PATHINFO_BASENAME) ✓ ファイル名抽出
|
||||
intval($id) ✓ 数値ID
|
||||
Str::random(40), Str::uuid() ✓ 安全なファイル名
|
||||
$file->hashName() ✓ ハッシュファイル名
|
||||
```
|
||||
|
||||
### 危険なパストラバーサルパターン
|
||||
|
||||
```
|
||||
.. ✗ ディレクトリトラバーサル
|
||||
../ ..\\ ✗ Unix/Windows
|
||||
%2e%2e ✗ URLエンコード
|
||||
%252e%252e ✗ ダブルエンコード
|
||||
```
|
||||
|
||||
### パスサニタイザー破壊パターン(危険)
|
||||
|
||||
```php
|
||||
urldecode(basename($var)) ✗ %2e%2e -> ..
|
||||
base64_decode($var) ✗ トラバーサル隠蔽
|
||||
$file->getClientOriginalName() ✗ ユーザー制御
|
||||
```
|
||||
|
||||
### 安全なパターン
|
||||
|
||||
```php
|
||||
$safePath = basename($userInput); ✓
|
||||
$realPath = realpath($path); ✓
|
||||
if (str_starts_with($realPath, $allowedDir)) { ... } ✓
|
||||
'/docs/' . intval($id) . '.txt' ✓
|
||||
$request->file('doc')->hashName() ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 認証 検出パターン
|
||||
|
||||
```
|
||||
重大度: HIGH
|
||||
├── md5($password)
|
||||
├── sha1($password)
|
||||
├── $password = 'hardcoded'
|
||||
└── base64_encode($password)
|
||||
|
||||
重大度: MEDIUM
|
||||
├── $token == $userToken # タイミング攻撃
|
||||
└── password_hash($pw, ..., ['cost' => 4])
|
||||
```
|
||||
|
||||
### 安全なパターン
|
||||
|
||||
```php
|
||||
password_hash($pw, PASSWORD_ARGON2ID); ✓
|
||||
password_verify($input, $hash); ✓
|
||||
hash_equals($expected, $actual); ✓
|
||||
Hash::make($password); ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSRF/セッション 検出パターン
|
||||
|
||||
```
|
||||
重大度: HIGH
|
||||
└── <form method="POST"> without @csrf
|
||||
|
||||
重大度: MEDIUM
|
||||
├── session_start() without secure options
|
||||
├── Cookie without httponly
|
||||
└── session_regenerate_id() without true
|
||||
```
|
||||
|
||||
### 安全なパターン
|
||||
|
||||
```blade
|
||||
<form method="POST">
|
||||
@csrf ✓
|
||||
@method('PUT') ✓
|
||||
</form>
|
||||
```
|
||||
|
||||
```php
|
||||
session_start([
|
||||
'cookie_httponly' => true, ✓
|
||||
'cookie_secure' => true, ✓
|
||||
'cookie_samesite' => 'Strict', ✓
|
||||
]);
|
||||
session_regenerate_id(true); ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 設定 検出パターン
|
||||
|
||||
```
|
||||
重大度: HIGH
|
||||
├── phpinfo()
|
||||
└── unserialize($data) without allowed_classes
|
||||
|
||||
重大度: MEDIUM
|
||||
├── dd($var), dump($var)
|
||||
├── var_dump($var), print_r($var)
|
||||
├── error_reporting(-1)
|
||||
├── ini_set('display_errors', '1')
|
||||
└── Log::info($password)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CLI 使用方法
|
||||
|
||||
```bash
|
||||
# 基本
|
||||
php bin/security-lint <path>
|
||||
|
||||
# オプション
|
||||
-f, --format 出力形式 (text|json|html|sarif|markdown)
|
||||
-s, --severity 最小重大度 (low|medium|high|critical)
|
||||
-o, --output ファイルに出力
|
||||
-l, --lang 言語 (ja|en)
|
||||
-e, --exclude 除外パターン
|
||||
-d, --recursive-depth 再帰深度 (default: 10)
|
||||
--verbose 詳細表示
|
||||
|
||||
# 例
|
||||
php bin/security-lint app/ -s high -f json -o report.json
|
||||
php bin/security-lint resources/views/ -l en
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 終了コード
|
||||
|
||||
| コード | 意味 |
|
||||
|--------|------|
|
||||
| 0 | 問題なし |
|
||||
| 1 | 中/低重大度の問題あり |
|
||||
| 2 | クリティカル/高重大度の問題あり |
|
||||
Reference in New Issue
Block a user