- 认证与授权
- 使用 JWT 认证
- Token 安全存储
- 密码哈希
- 数据验证
- 输入验证
- 输出转义
- SQL 注入防护
- 使用查询构建器
- 使用参数绑定
- 使用 ORM
- XSS 防护
- 模板自动转义
- 手动转义
- 白名单过滤
- CSRF 防护
- 生成 CSRF Token
- 验证 CSRF Token
- 使用框架中间件
- 密码安全
- 密码哈希
- 密码强度要求
- 密码重置
- 文件上传安全
- 文件类型验证
- 文件大小限制
- 安全存储
- 文件内容验证
- 环境配置
- 环境变量
- 配置文件安全
- .env 文件保护
- ✅ 推荐:.env 文件不提交到版本控制
- .gitignore
- 最佳实践
- 1. 最小权限原则
- 2. 输入验证和输出转义
- 3. 使用 HTTPS
- 4. 错误处理
- 5. 日志记录
- 常见安全问题
- SQL 注入
- XSS 攻击
- CSRF 攻击
- 密码泄露
- 文件上传漏洞
- 相关文档
安全指南
最后更新: 2026-01-27 11:02:47安全指南
本文档提供了 Unicode Framework 的安全最佳实践和常见安全问题的解决方案。
认证与授权
使用 JWT 认证
// ✅ 推荐:使用框架提供的 JWT 认证
use Unicode\Framework\Auth\JWT;
$jwt = new JWT($secret);
$token = $jwt->encode(['user_id' => $userId]);
// 验证 Token
try {
$payload = $jwt->decode($token);
} catch (\Exception $e) {
// Token 无效
}
Token 安全存储
// ✅ 推荐:使用 HttpOnly Cookie
setcookie('token', $token, [
'httponly' => true, // 防止 XSS 攻击
'secure' => true, // 仅 HTTPS
'samesite' => 'Strict',
]);
// ❌ 错误:存储在 localStorage(容易被 XSS 攻击)
localStorage.setItem('token', token);
密码哈希
// ✅ 推荐:使用 password_hash
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 验证密码
if (password_verify($password, $hashedPassword)) {
// 密码正确
}
// ❌ 错误:使用 md5 或 sha1
$hashedPassword = md5($password); // 不安全
数据验证
输入验证
// ✅ 推荐:验证所有用户输入
use Unicode\Framework\Validation\Validator;
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'age' => 'required|integer|min:18',
'name' => 'required|string|max:255',
]);
if (!$validator->validate()) {
return Response::json(['errors' => $validator->errors()], 422);
}
// ❌ 错误:不验证输入
$email = $request->post('email'); // 可能包含恶意数据
输出转义
// ✅ 推荐:在模板中自动转义
// Smarty 模板
{$user.bio|escape:'html'}
// ❌ 错误:直接输出未转义的数据
echo $user->bio; // 可能包含 XSS 代码
SQL 注入防护
使用查询构建器
// ✅ 推荐:使用查询构建器(自动参数绑定)
$users = db('users')
->where('email', $email)
->where('status', $status)
->get();
// ❌ 错误:直接拼接 SQL
$sql = "SELECT * FROM users WHERE email = '{$email}'";
$users = db()->query($sql); // SQL 注入风险
使用参数绑定
// ✅ 推荐:使用参数绑定
$users = db()->query(
'SELECT * FROM users WHERE email = ? AND status = ?',
[$email, $status]
);
// ❌ 错误:字符串拼接
$sql = "SELECT * FROM users WHERE email = '{$email}'";
使用 ORM
// ✅ 推荐:使用 ORM(自动防护)
$user = User::where('email', $email)->first();
// ❌ 错误:原生 SQL 拼接
$user = db()->query("SELECT * FROM users WHERE email = '{$email}'");
XSS 防护
模板自动转义
// ✅ 推荐:在配置中启用自动转义
// config/view.php
return [
'engines' => [
'smarty' => [
'escape_html' => true, // 自动转义 HTML
],
],
];
// 模板中
{$user.bio} // 自动转义
手动转义
// ✅ 推荐:需要输出 HTML 时手动转义
$safeHtml = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
echo $safeHtml;
// ❌ 错误:直接输出用户输入
echo $userInput; // XSS 风险
白名单过滤
// ✅ 推荐:使用白名单过滤 HTML
use HTMLPurifier;
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$cleanHtml = $purifier->purify($userInput);
CSRF 防护
生成 CSRF Token
// 生成 CSRF Token
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
// 在表单中
<input type="hidden" name="csrf_token" value="<?= $token ?>">
验证 CSRF Token
// 验证 CSRF Token
if ($request->post('csrf_token') !== $_SESSION['csrf_token']) {
return Response::json(['error' => 'Invalid CSRF token'], 403);
}
使用框架中间件
// 创建 CSRF 中间件
class CsrfMiddleware implements MiddlewareInterface
{
public function handle(Request $request, callable $next): Response
{
if ($request->method() === 'POST') {
$token = $request->post('csrf_token');
if ($token !== $_SESSION['csrf_token']) {
return Response::json(['error' => 'Invalid CSRF token'], 403);
}
}
return $next($request);
}
}
密码安全
密码哈希
// ✅ 推荐:使用 password_hash
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 验证密码
if (password_verify($password, $hashedPassword)) {
// 密码正确
}
密码强度要求
// ✅ 推荐:验证密码强度
use Unicode\Framework\Validation\Rules\Custom;
$rules = [
'password' => [
'required',
'string',
'min:8',
new Custom(function($value) {
return preg_match('/[A-Z]/', $value) &&
preg_match('/[a-z]/', $value) &&
preg_match('/[0-9]/', $value);
}, '密码必须包含大小写字母和数字'),
],
];
密码重置
// ✅ 推荐:使用安全的密码重置流程
// 1. 生成随机 Token
$token = bin2hex(random_bytes(32));
// 2. 存储 Token(带过期时间)
db('password_resets')->insert([
'email' => $email,
'token' => hash('sha256', $token),
'expires_at' => date('Y-m-d H:i:s', time() + 3600),
]);
// 3. 发送重置链接
$resetLink = "https://example.com/reset-password?token={$token}";
// 4. 验证 Token
$reset = db('password_resets')
->where('token', hash('sha256', $token))
->where('expires_at', '>', date('Y-m-d H:i:s'))
->first();
文件上传安全
文件类型验证
// ✅ 推荐:验证文件类型
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$uploadedFile = $request->file('avatar');
if (!in_array($uploadedFile->getMimeType(), $allowedTypes)) {
return Response::json(['error' => 'Invalid file type'], 422);
}
文件大小限制
// ✅ 推荐:限制文件大小
$maxSize = 5 1024 1024; // 5MB
if ($uploadedFile->getSize() > $maxSize) {
return Response::json(['error' => 'File too large'], 422);
}
安全存储
// ✅ 推荐:使用安全的文件名和存储路径
$filename = bin2hex(random_bytes(16)) . '.' . $extension;
$path = storage_path('uploads/' . date('Y/m/d') . '/' . $filename);
// 不要使用用户提供的文件名
// ❌ $filename = $request->post('filename');
文件内容验证
// ✅ 推荐:验证文件内容(不仅仅是扩展名)
$imageInfo = getimagesize($uploadedFile->getPath());
if ($imageInfo === false) {
return Response::json(['error' => 'Invalid image'], 422);
}
环境配置
环境变量
// ✅ 推荐:敏感信息使用环境变量
// .env
DB_PASSWORD=your-secure-password
JWT_SECRET=your-secret-key
API_KEY=your-api-key
// 代码中
$password = getenv('DB_PASSWORD');
$secret = getenv('JWT_SECRET');
// ❌ 错误:硬编码敏感信息
$password = 'my-password'; // 不安全
配置文件安全
// ✅ 推荐:配置文件不包含敏感信息
// config/database.php
return [
'password' => getenv('DB_PASSWORD'), // 从环境变量读取
];
// ❌ 错误:配置文件包含敏感信息
return [
'password' => 'my-password', // 不安全
];
.env 文件保护
<h1 id="推荐env-文件不提交到版本控制">✅ 推荐:.env 文件不提交到版本控制</h1>
<h1 id="gitignore">.gitignore</h1>
.env
.env.local
.env.*.local
最佳实践
1. 最小权限原则
// ✅ 推荐:只授予必要的权限
// 用户只能访问自己的数据
$user = User::find($userId);
if ($user->id !== $currentUserId) {
return Response::json(['error' => 'Forbidden'], 403);
}
2. 输入验证和输出转义
// ✅ 推荐:验证输入,转义输出
// 1. 验证输入
$validator = Validator::make($data, $rules);
// 2. 处理数据
$user = User::create($validator->getValidated());
// 3. 转义输出
return view('user/profile', [
'user' => $user, // 模板自动转义
]);
3. 使用 HTTPS
// ✅ 推荐:生产环境使用 HTTPS
if (getenv('APP_ENV') === 'production') {
if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit;
}
}
4. 错误处理
// ✅ 推荐:不暴露敏感错误信息
try {
// 操作
} catch (\Exception $e) {
// 开发环境:显示详细错误
if (getenv('APP_ENV') === 'development') {
error_log($e->getMessage());
}
// 生产环境:显示通用错误
return Response::json(['error' => 'An error occurred'], 500);
}
5. 日志记录
// ✅ 推荐:记录安全相关事件
use Unicode\Framework\Log\LogManager;
$logger = LogManager::getInstance();
// 记录登录失败
$logger->warning('Login failed', [
'email' => $email,
'ip' => $request->ip(),
]);
// 记录敏感操作
$logger->info('Password changed', [
'user_id' => $userId,
'ip' => $request->ip(),
]);
常见安全问题
SQL 注入
问题:直接拼接 SQL 语句 解决方案:使用查询构建器或参数绑定XSS 攻击
问题:直接输出用户输入 解决方案:模板自动转义或手动转义CSRF 攻击
问题:没有验证请求来源 解决方案:使用 CSRF Token密码泄露
问题:使用弱密码或明文存储 解决方案:使用password_hash 和强密码策略