セキュリティベストプラクティス
2.5.x ✅ 4.0.x ✅
このドキュメントは、XOOPSモジュール開発者向けの包括的なセキュリティベストプラクティスを提供します。これらのガイドラインに従うことで、モジュールが安全であり、XOOPS インストールに脆弱性をもたらさないことを確保できます。
セキュリティの原則
Section titled “セキュリティの原則”すべてのXOOPS開発者は、以下の基本的なセキュリティ原則に従うべきです:
- 多層防御: 複数のセキュリティ管理層を実装する
- 最小権限の原則: 必要最小限のアクセス権を付与する
- 入力検証: ユーザー入力を決して信頼しない
- デフォルトでセキュア: セキュリティがデフォルト設定であるべき
- シンプルさを保つ: 複雑なシステムはセキュリティが困難
関連ドキュメント
Section titled “関連ドキュメント”- CSRF保護 - トークンシステムとXoopsSecurityクラス
- 入力サニタイズ - MyTextSanitizerと検証
- SQLインジェクション防止 - データベースセキュリティプラクティス
クイックリファレンスチェックリスト
Section titled “クイックリファレンスチェックリスト”モジュールをリリースする前に以下を確認してください:
- すべてのフォームにXOOPSトークンが含まれている
- すべてのユーザー入力が検証およびサニタイズされている
- すべての出力が正しくエスケープされている
- すべてのデータベースクエリがパラメータ化されたステートメントを使用している
- ファイルアップロードが適切に検証されている
- 認証と認可チェックが実装されている
- エラーハンドリングが機密情報を公開していない
- 機密設定が保護されている
- サードパーティライブラリが最新である
- セキュリティテストが実施されている
ユーザー認証の確認
Section titled “ユーザー認証の確認”// ユーザーがログインしているかを確認if (!is_object($GLOBALS['xoopsUser'])) { redirect_header(XOOPS_URL, 3, _NOPERM); exit();}ユーザー権限の確認
Section titled “ユーザー権限の確認”// ユーザーがこのモジュールにアクセスする権限があるかを確認if (!$GLOBALS['xoopsUser']->isAdmin($xoopsModule->mid())) { redirect_header(XOOPS_URL, 3, _NOPERM); exit();}
// 特定の権限を確認$moduleHandler = xoops_getHandler('module');$module = $moduleHandler->getByDirname('mymodule');$moduleperm_handler = xoops_getHandler('groupperm');$groups = $GLOBALS['xoopsUser']->getGroups();
if (!$moduleperm_handler->checkRight('mymodule_view', $item_id, $groups, $module->getVar('mid'))) { redirect_header(XOOPS_URL, 3, _NOPERM); exit();}モジュール権限の設定
Section titled “モジュール権限の設定”// インストール/更新関数で権限を作成$gpermHandler = xoops_getHandler('groupperm');$gpermHandler->deleteByModule($module->getVar('mid'), 'mymodule_view');
// すべてのグループに権限を追加$groups = [XOOPS_GROUP_ADMIN, XOOPS_GROUP_USERS, XOOPS_GROUP_ANONYMOUS];foreach ($groups as $group_id) { $gpermHandler->addRight('mymodule_view', 1, $group_id, $module->getVar('mid'));}セッションセキュリティ
Section titled “セッションセキュリティ”セッション処理のベストプラクティス
Section titled “セッション処理のベストプラクティス”- 機密情報をセッションに保存しない
- ログイン/権限変更後にセッションIDを再生成する
- セッションデータを使用する前に検証する
// ログイン後にセッションIDを再生成session_regenerate_id(true);
// セッションデータを検証if (isset($_SESSION['mymodule_user_id'])) { $user_id = (int)$_SESSION['mymodule_user_id']; // ユーザーがデータベースに存在することを確認}セッション固定の防止
Section titled “セッション固定の防止”// ログイン成功後session_regenerate_id(true);$_SESSION['mymodule_user_ip'] = $_SERVER['REMOTE_ADDR'];
// 後続のリクエストif ($_SESSION['mymodule_user_ip'] !== $_SERVER['REMOTE_ADDR']) { // 可能なセッションハイジャック試行 session_destroy(); redirect_header('index.php', 3, 'セッションエラー'); exit();}ファイルアップロードセキュリティ
Section titled “ファイルアップロードセキュリティ”ファイルアップロードの検証
Section titled “ファイルアップロードの検証”// ファイルが正しくアップロードされたかを確認if (!isset($_FILES['userfile']) || $_FILES['userfile']['error'] != UPLOAD_ERR_OK) { redirect_header('index.php', 3, 'ファイルアップロードエラー'); exit();}
// ファイルサイズを確認if ($_FILES['userfile']['size'] > 1000000) { // 1MBの制限 redirect_header('index.php', 3, 'ファイルが大きすぎます'); exit();}
// ファイルタイプを確認$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];if (!in_array($_FILES['userfile']['type'], $allowed_types)) { redirect_header('index.php', 3, '無効なファイルタイプ'); exit();}
// ファイル拡張子を検証$filename = $_FILES['userfile']['name'];$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];if (!in_array($ext, $allowed_extensions)) { redirect_header('index.php', 3, '無効なファイル拡張子'); exit();}XOOPSアップローダーの使用
Section titled “XOOPSアップローダーの使用”include_once XOOPS_ROOT_PATH . '/class/uploader.php';
$allowed_mimetypes = ['image/gif', 'image/jpeg', 'image/png'];$maxsize = 1000000; // 1MB$maxwidth = 1024;$maxheight = 768;$upload_dir = XOOPS_ROOT_PATH . '/uploads/mymodule';
$uploader = new XoopsMediaUploader( $upload_dir, $allowed_mimetypes, $maxsize, $maxwidth, $maxheight);
if ($uploader->fetchMedia('userfile')) { $uploader->setPrefix('mymodule_');
if ($uploader->upload()) { $filename = $uploader->getSavedFileName(); // ファイル名をデータベースに保存 } else { echo $uploader->getErrors(); }} else { echo $uploader->getErrors();}アップロードファイルの安全な保存
Section titled “アップロードファイルの安全な保存”// Webルートの外側にアップロードディレクトリを定義$upload_dir = XOOPS_VAR_PATH . '/uploads/mymodule';
// ディレクトリが存在しない場合は作成if (!is_dir($upload_dir)) { mkdir($upload_dir, 0755, true);}
// アップロードされたファイルを移動move_uploaded_file($_FILES['userfile']['tmp_name'], $upload_dir . '/' . $safe_filename);エラーハンドリングとロギング
Section titled “エラーハンドリングとロギング”セキュアなエラーハンドリング
Section titled “セキュアなエラーハンドリング”try { $result = someFunction(); if (!$result) { throw new Exception('操作に失敗しました'); }} catch (Exception $e) { // エラーをログに記録 xoops_error($e->getMessage());
// ユーザーに一般的なエラーメッセージを表示 redirect_header('index.php', 3, '後でもう一度試してください。'); exit();}セキュリティイベントのロギング
Section titled “セキュリティイベントのロギング”// セキュリティイベントをログに記録xoops_loadLanguage('logger', 'mymodule');$GLOBALS['xoopsLogger']->addExtra('セキュリティ', 'ユーザーのログイン試行に失敗: ' . $username);設定セキュリティ
Section titled “設定セキュリティ”機密設定の保存
Section titled “機密設定の保存”// Webルートの外側に設定パスを定義$config_path = XOOPS_VAR_PATH . '/configs/mymodule/config.php';
// 設定を読み込むif (file_exists($config_path)) { include $config_path;} else { // 設定が見つからない場合を処理}設定ファイルの保護
Section titled “設定ファイルの保護”.htaccessを使用して設定ファイルを保護します:
# .htaccessで<Files "config.php"> Order Allow,Deny Deny from all</Files>サードパーティライブラリ
Section titled “サードパーティライブラリ”ライブラリの選択
Section titled “ライブラリの選択”- アクティブにメンテナンスされているライブラリを選択する
- セキュリティ脆弱性を確認する
- ライブラリのライセンスがXOOPSと互換性があることを確認する
ライブラリの更新
Section titled “ライブラリの更新”// ライブラリバージョンを確認if (version_compare(LIBRARY_VERSION, '1.2.3', '<')) { xoops_error('ライブラリをバージョン1.2.3以上に更新してください');}ライブラリの分離
Section titled “ライブラリの分離”// 制御された方法でライブラリを読み込むfunction loadLibrary($file){ $allowed = ['parser.php', 'formatter.php'];
if (!in_array($file, $allowed)) { return false; }
include_once XOOPS_ROOT_PATH . '/modules/mymodule/libraries/' . $file; return true;}セキュリティテスト
Section titled “セキュリティテスト”手動テストチェックリスト
Section titled “手動テストチェックリスト”- すべてのフォームを無効な入力でテストする
- 認証と認可をバイパスしようとする
- ファイルアップロード機能を悪意のあるファイルでテストする
- すべての出力でXSS脆弱性をチェックする
- すべてのデータベースクエリでSQLインジェクションをテストする
脆弱性をスキャンするための自動ツールを使用します:
- 静的コード分析ツール
- Webアプリケーションスキャナー
- サードパーティライブラリの依存関係チェッカー
出力エスケープ
Section titled “出力エスケープ”HTMLコンテキスト
Section titled “HTMLコンテキスト”// 通常のHTMLコンテンツの場合echo htmlspecialchars($variable, ENT_QUOTES, 'UTF-8');
// MyTextSanitizerを使用$myts = MyTextSanitizer::getInstance();echo $myts->htmlSpecialChars($variable);JavaScriptコンテキスト
Section titled “JavaScriptコンテキスト”// JavaScriptで使用されるデータの場合echo json_encode($variable);
// インラインJavaScriptの場合echo 'var data = ' . json_encode($variable) . ';';URLコンテキスト
Section titled “URLコンテキスト”// URLで使用されるデータの場合echo htmlspecialchars(urlencode($variable), ENT_QUOTES, 'UTF-8');テンプレート変数
Section titled “テンプレート変数”// Smartyテンプレートに変数を割り当てる$GLOBALS['xoopsTpl']->assign('title', htmlspecialchars($title, ENT_QUOTES, 'UTF-8'));
// HTMLコンテンツとして表示すべき場合$GLOBALS['xoopsTpl']->assign('content', $myts->displayTarea($content, 1, 1, 1, 1, 1));#セキュリティ #ベストプラクティス #xoops #モジュール開発 #認証 #認可