drupal-security について
drupal-security は、Drupal のコードを作成・レビューする開発者向けのセキュリティスキルです。SQL インジェクション、XSS、アクセス制御の不足、安全でないファイル処理などを指摘し、安全な実装例とコミット前のチェックリストを示します。
主な機能
- 01SQL インジェクションリスクの特定
- 02レンダリング出力での XSS 防止
- 03権限と CSRF 対策の確認
- 04ファイルアップロード検証のレビュー
- 05認証情報の露出や危険なコードの検出
活用シーン
- 01Drupal のフォームとコントローラーのレビュー
- 02ユーザー入力とデータベース処理の確認
- 03ルートとアクセス権限の監査
- 04コミット前のファイルアップロードコード確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ madsnorgaard/ drupal-agent-resources --skill drupal-securityインストール後、Claude Code で/drupal-securityを入力して読み込まれたか確認します。
詳細
Drupal セキュリティエキスパート
コードを書いている段階で、脆弱性を事前に特定します。コードが完成してからではありません。
この機能が有効になる場面
- フォーム、コントローラー、プラグインの作成または編集
- ユーザー入力やクエリパラメーターの処理
- データベースクエリの作成
- ユーザーが提供したコンテンツのレンダリング
- アクセス制御の実装
重要なセキュリティパターン
SQL インジェクションの防止
クエリにユーザー入力を絶対に連結しないでください:
// 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 の防止
出力は必ずエスケープしてください。レンダーシステムを信頼してください:
// 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
管理者専用のコンテンツの場合:
use Drupal\Component\Utility\Xss;
// Filter but allow safe HTML tags
$safe = Xss::filterAdmin($user_html);
アクセス制御
必ず権限を確認してください:
// 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 の防止
フォームには CSRF トークンが自動的に含まれます。カスタム AJAX の場合:
// 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');
}
ファイルアップロードのセキュリティ
$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
}
機密データ
// 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';
注意すべき危険なパターン
以下のパターンを見つけたら、すぐに警告してください:
| パターン | リスク | 対策 |
|---|---|---|
| SQL での文字列連結 | SQL インジェクション | クエリビルダーを使用する |
変数を含む #markup | XSS | #plain_text を使用する |
accessCheck(FALSE) | アクセス制御の回避 | accessCheck(TRUE) を使用する |
ルートに _permission がない | 不正アクセス | 権限を追加する |
Twig の {{ var|raw }} | XSS | |raw を削除する |
| パスワードやキーのハードコード | 認証情報の漏えい | 環境変数を使用する |
eval() または exec() | コードインジェクション | 完全に避ける |
ユーザーデータへの unserialize() | オブジェクトインジェクション | JSON を使用する |
セキュリティレビューの確認事項
コードをレビューする際は、必ず以下を確認してください:
- 「このデータはどこから来ていますか?」(ユーザー入力は信頼できません)
- 「このデータはどこへ渡されますか?」(出力はエスケープします)
- 「誰がアクセスすべきですか?」(権限が必要です)
- 「悪意のある入力が含まれていたらどうなりますか?」(検証・サニタイズします)
セキュリティのクイックチェックリスト
コードをコミットする前に確認してください:
- すべてのユーザー入力が検証・サニタイズされている
- すべての出力が適切にエスケープされている
- ルートに権限要件が設定されている
- エンティティクエリで
accessCheck(TRUE)を使用している - 認証情報をハードコードしていない
- ファイルアップロードで種類と拡張子の両方を検証している
- フォームで Form API を使用している(CSRF 対策が自動で行われる)
- 機密データをログに記録していない
リソース
コメント
Markdown 対応