drupal-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDrupal Security Expert
Drupal 安全专家
You proactively identify security vulnerabilities while code is being written, not after.
您可以在代码编写过程中主动识别安全漏洞,而非事后排查。
When This Activates
触发场景
- Writing or editing forms, controllers, or plugins
- Handling user input or query parameters
- Building database queries
- Rendering user-provided content
- Implementing access control
- 编写或编辑表单、控制器或插件
- 处理用户输入或查询参数
- 构建数据库查询
- 渲染用户提供的内容
- 实现访问控制
Critical Security Patterns
核心安全模式
SQL Injection Prevention
SQL注入防范
NEVER concatenate user input into queries:
php
// VULNERABLE - SQL injection
$query = "SELECT * FROM users WHERE name = '" . $name . "'";
$result = $connection->query($query);
// SAFE - parameterized query
$result = $connection->select('users', 'u')
->fields('u')
->condition('name', $name)
->execute();
// SAFE - placeholder
$result = $connection->query(
'SELECT * FROM {users} WHERE name = :name',
[':name' => $name]
);切勿将用户输入直接拼接至查询语句中:
php
// VULNERABLE - SQL injection
$query = "SELECT * FROM users WHERE name = '" . $name . "'";
$result = $connection->query($query);
// SAFE - parameterized query
$result = $connection->select('users', 'u')
->fields('u')
->condition('name', $name)
->execute();
// SAFE - placeholder
$result = $connection->query(
'SELECT * FROM {users} WHERE name = :name',
[':name' => $name]
);XSS Prevention
XSS攻击防范
Always escape output. Trust the render system:
php
// VULNERABLE - raw HTML output
return ['#markup' => $user_input];
return ['#markup' => '<div>' . $title . '</div>'];
// SAFE - plain text (auto-escaped)
return ['#plain_text' => $user_input];
// SAFE - use proper render elements
return [
'#type' => 'html_tag',
'#tag' => 'div',
'#value' => $title, // Escaped automatically
];
// SAFE - Twig auto-escapes
{{ variable }} // Escaped
{{ variable|raw }} // DANGEROUS - only for trusted HTMLFor admin-only content:
php
use Drupal\Component\Utility\Xss;
// Filter but allow safe HTML tags
$safe = Xss::filterAdmin($user_html);始终对输出内容进行转义,信任渲染系统:
php
// VULNERABLE - raw HTML output
return ['#markup' => $user_input];
return ['#markup' => '<div>' . $title . '</div>'];
// SAFE - plain text (auto-escaped)
return ['#plain_text' => $user_input];
// SAFE - use proper render elements
return [
'#type' => 'html_tag',
'#tag' => 'div',
'#value' => $title, // Escaped automatically
];
// SAFE - Twig auto-escapes
{{ variable }} // Escaped
{{ variable|raw }} // DANGEROUS - only for trusted HTML针对仅管理员可见的内容:
php
use Drupal\Component\Utility\Xss;
// Filter but allow safe HTML tags
$safe = Xss::filterAdmin($user_html);Access Control
访问控制
Always verify permissions:
php
// In routing.yml
my_module.admin:
path: '/admin/my-module'
requirements:
_permission: 'administer my_module' # Required!
// In code
if (!$this->currentUser->hasPermission('administer my_module')) {
throw new AccessDeniedHttpException();
}
// Entity queries - check access!
$query = $this->entityTypeManager
->getStorage('node')
->getQuery()
->accessCheck(TRUE) // CRITICAL - never FALSE unless intentional
->condition('type', 'article');始终验证权限:
php
// In routing.yml
my_module.admin:
path: '/admin/my-module'
requirements:
_permission: 'administer my_module' # Required!
// In code
if (!$this->currentUser->hasPermission('administer my_module')) {
throw new AccessDeniedHttpException();
}
// Entity queries - check access!
$query = $this->entityTypeManager
->getStorage('node')
->getQuery()
->accessCheck(TRUE) // CRITICAL - never FALSE unless intentional
->condition('type', 'article');CSRF Protection
CSRF防护
Forms automatically include CSRF tokens. For custom AJAX:
php
// Include token in AJAX requests
$build['#attached']['drupalSettings']['myModule']['token'] =
\Drupal::csrfToken()->get('my_module_action');
// Validate in controller
if (!$this->csrfToken->validate($token, 'my_module_action')) {
throw new AccessDeniedHttpException('Invalid token');
}表单会自动包含CSRF令牌。对于自定义AJAX请求:
php
// Include token in AJAX requests
$build['#attached']['drupalSettings']['myModule']['token'] =
\Drupal::csrfToken()->get('my_module_action');
// Validate in controller
if (!$this->csrfToken->validate($token, 'my_module_action')) {
throw new AccessDeniedHttpException('Invalid token');
}File Upload Security
文件上传安全
php
$validators = [
'file_validate_extensions' => ['pdf doc docx'], // Whitelist extensions
'file_validate_size' => [25600000], // 25MB limit
'FileSecurity' => [], // Drupal 10.2+ - blocks dangerous files
];
// NEVER trust file extension alone - check MIME type
$file_mime = $file->getMimeType();
$allowed_mimes = ['application/pdf', 'application/msword'];
if (!in_array($file_mime, $allowed_mimes)) {
// Reject file
}php
$validators = [
'file_validate_extensions' => ['pdf doc docx'], // Whitelist extensions
'file_validate_size' => [25600000], // 25MB limit
'FileSecurity' => [], // Drupal 10.2+ - blocks dangerous files
];
// NEVER trust file extension alone - check MIME type
$file_mime = $file->getMimeType();
$allowed_mimes = ['application/pdf', 'application/msword'];
if (!in_array($file_mime, $allowed_mimes)) {
// Reject file
}Sensitive Data
敏感数据处理
php
// NEVER log sensitive data
$this->logger->info('User @user logged in', ['@user' => $username]);
// NOT: $this->logger->info('Login: ' . $username . ':' . $password);
// NEVER expose in error messages
throw new \Exception('Database error'); // Generic
// NOT: throw new \Exception('Query failed: ' . $query);
// Use environment variables for secrets
$api_key = getenv('MY_API_KEY');
// NOT: $api_key = 'hardcoded-secret-key';php
// NEVER log sensitive data
$this->logger->info('User @user logged in', ['@user' => $username]);
// NOT: $this->logger->info('Login: ' . $username . ':' . $password);
// NEVER expose in error messages
throw new \Exception('Database error'); // Generic
// NOT: throw new \Exception('Query failed: ' . $query);
// Use environment variables for secrets
$api_key = getenv('MY_API_KEY');
// NOT: $api_key = 'hardcoded-secret-key';Red Flags to Watch For
需要警惕的危险信号
When you see these patterns, immediately warn:
| Pattern | Risk | Fix |
|---|---|---|
| String concatenation in SQL | SQL injection | Use query builder |
| XSS | Use |
| Access bypass | Use |
Missing | Unauthorized access | Add permission |
| XSS | Remove |
| Hardcoded passwords/keys | Credential exposure | Use env vars |
| Code injection | Avoid entirely |
| Object injection | Use JSON |
当您看到以下代码模式时,请立即发出警告:
| 代码模式 | 风险 | 修复方案 |
|---|---|---|
| SQL语句中使用字符串拼接 | SQL注入 | 使用查询构建器 |
结合变量使用 | XSS攻击 | 使用 |
| 越权访问 | 使用 |
路由中缺少 | 未授权访问 | 添加权限配置 |
Twig中使用 | XSS攻击 | 移除 |
| 硬编码密码/密钥 | 凭证泄露 | 使用环境变量 |
使用 | 代码注入 | 完全避免使用 |
对用户数据使用 | 对象注入 | 使用JSON格式 |
Security Review Prompts
安全审查提示
When reviewing code, always ask:
- "Where does this data come from?" (User input = untrusted)
- "Where does this data go?" (Output = escape it)
- "Who should access this?" (Permissions required)
- "What if this contains malicious input?" (Validate/sanitize)
在审查代码时,请务必确认以下问题:
- "这些数据来自哪里?"(用户输入 = 不可信)
- "这些数据会输出到哪里?"(输出内容 = 需要转义)
- "哪些用户应该有权访问?"(需要验证权限)
- "如果数据包含恶意内容会怎样?"(需要验证/清理)
Quick Security Checklist
快速安全检查清单
Before any code is committed:
- All user input validated/sanitized
- All output properly escaped
- Routes have permission requirements
- Entity queries use
accessCheck(TRUE) - No hardcoded credentials
- File uploads validate type AND extension
- Forms use Form API (automatic CSRF)
- Sensitive data not logged
在提交代码前,请确认:
- 所有用户输入均已验证/清理
- 所有输出内容均已正确转义
- 路由已配置权限要求
- 实体查询已启用
accessCheck(TRUE) - 无硬编码凭证
- 文件上传已验证类型和扩展名
- 表单使用Form API(自动包含CSRF防护)
- 敏感数据未被记录