PHP安全
Mattakushi4322026-10-26查看 1467复制 902
何时激活:PHP 安全、SQL 注入、PDO 准备语句、批量分配、CSRF、Blade XSS、password_hash、会话固定、反序列化对象注入、文件上传验证、Laravel 安全
📘如何使用这个技能
- 1复制技能内容·点击上方「Copy SKILL.md」复制全部内容,或 Download 下载到本地
- 2打开你的 AI 客户端·支持 Claude Code、Trae、Cursor、ChatGPT、DeepSeek、Kimi 等任意 AI 助手
- 3粘贴为系统提示/知识文件·Claude Code:放入 .claude/skills/ 目录;普通对话:作为首条消息发送并说「请严格按这份技能文档执行」
- 4直接提出你的需求·AI 会按技能定义的工作流、格式和约束来处理,无需再解释背景
💡 提示:技能生效后,每次对话只需一句需求,AI 会按 SKILL.md 的流程执行。
SKILL.md5049 chars
---
name: php-security
description: "When to activate: PHP security, SQL injection, PDO prepared statements, mass assignment, CSRF, Blade XSS, password_hash, session fixation, unserialize object injection, file upload validation, Laravel security"
origin: zakr
metadata:
namespace: zakr/devtools-pack
version: 1.0.0
category: engineering
language: php
platforms: [claude-code, codex, gemini-cli, cursor, aider, windsurf]
---
# PHP Security Patterns
## When to Use
Reviewing or writing PHP/Laravel code that touches raw SQL, user input, authentication, file uploads, or serialized data.
## Core Patterns
### Prepared Statements, Never Interpolation
```php
// BAD: string interpolation — classic SQL injection
$pdo->query("SELECT * FROM users WHERE email = '{$email}'");
// GOOD: bound parameters
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);
```
Eloquent/query builder methods bind parameters automatically — the risk appears when developers drop to `DB::select()`/`DB::statement()` with raw strings.
```php
// BAD
DB::select("SELECT * FROM orders WHERE status = '$status'");
// GOOD — still parameterized even in raw SQL
DB::select('SELECT * FROM orders WHERE status = ?', [$status]);
```
### Mass-Assignment Protection
```php
// BAD: everything in the request can set model attributes, including is_admin
class User extends Model {
protected $guarded = [];
}
// GOOD: explicit allow-list
class User extends Model {
protected $fillable = ['name', 'email', 'password'];
}
```
Never call `->fill($request->all())` or `User::create($request->all())` — always pass `$request->validated()` from a `FormRequest`.
### Password Hashing
```php
// Storing
$hashed = password_hash($plainPassword, PASSWORD_BCRYPT);
// Laravel:
$hashed = Hash::make($plainPassword);
// Verifying — constant-time comparison built in, never use ===
if (password_verify($plainPassword, $hashed)) { /* ... */ }
if (Hash::check($plainPassword, $hashed)) { /* ... */ }
```
Never roll a custom hash (`md5`, `sha1`) or store plaintext. Rehash on login if `password_needs_rehash()` returns true after a cost-factor bump.
### Blade Output Escaping
```blade
{{-- GOOD: auto-escaped, safe by default --}}
<p>{{ $comment->body }}</p>
{{-- DANGEROUS: raw output, only for trusted/pre-sanitized HTML --}}
<div>{!! $trustedCmsContent !!}</div>
```
If user input must render as HTML, sanitize it first with a vetted library (e.g. `HTMLPurifier`) — never `{!! !!}` a raw user-supplied field.
### CSRF Protection
```blade
<form method="POST" action="/orders">
@csrf
...
</form>
```
```php
// API routes using tokens/sessions still need CSRF unless purely stateless
// with a Bearer token — verify VerifyCsrfToken middleware covers all
// state-changing routes that use cookie-based auth.
```
### Safe Deserialization
```php
// BAD: unserialize() on user input can trigger PHP object injection
$data = unserialize($_COOKIE['data']);
// GOOD: use a data-only format
$data = json_decode($_COOKIE['data'], true, flags: JSON_THROW_ON_ERROR);
```
### File Upload Validation
```php
$request->validate([
'avatar' => ['required', 'file', 'image', 'max:2048', 'mimes:jpg,png,webp'],
]);
// Store outside the public webroot when possible, or with a randomized name
$path = $request->file('avatar')->store('avatars', 'private');
```
Never trust the client-supplied MIME type or extension alone — validate the actual file content (`image` rule inspects real image data).
## Checklist
- [ ] No raw SQL string interpolation anywhere in the codebase
- [ ] Models use `$fillable`, never `$guarded = []`
- [ ] Passwords hashed with `password_hash`/`Hash::make`, never a fast general-purpose hash
- [ ] `{!! !!}` / raw HTML output only used on sanitized or trusted content
- [ ] CSRF middleware active on all cookie-authenticated state-changing routes
- [ ] `unserialize()` never called on user-controlled input
- [ ] File uploads validated by content type and size, stored outside public path when sensitive
- [ ] Secrets loaded from `.env`/`config()`, never hardcoded
## Anti-Patterns
```php
// BAD: trusting $_GET/$_POST directly in a redirect (open redirect)
header('Location: ' . $_GET['next']);
// GOOD: validate against an allow-list of known internal paths
$next = in_array($request->get('next'), $allowedRedirects, true)
? $request->get('next')
: '/dashboard';
header("Location: {$next}");
```
## Quick Reference
| Risk | Mitigation |
|---|---|
| SQL injection | Prepared statements / query builder binding |
| Mass assignment | `$fillable` allow-list + `FormRequest` validation |
| XSS | Blade `{{ }}` auto-escaping; sanitize before `{!! !!}` |
| CSRF | `@csrf` + `VerifyCsrfToken` middleware |
| Object injection | `json_decode` instead of `unserialize` |
| Weak password storage | `password_hash` / `Hash::make` (bcrypt/argon2) |
## See Also
- `skills/php-ecosystem/laravel-patterns.md`
- `skills/php-ecosystem/php-database.md`
- `skills/security/owasp-checklist.md`