secure-development について
Hack23 ISMS Secure Development Policyに基づく、ソフトウェア開発チーム向けのセキュア開発スキルです。入力検証やアクセス制御から、テスト、依存関係、コードレビューまでを扱います。
主な機能
- 01入力検証と出力エンコードの規則
- 02認証とアクセス制御
- 03暗号化と設定管理の要件
- 04エラー処理とセキュリティテスト
- 05依存関係とコードレビュー
活用シーン
- 01WebアプリやAPIの安全な開発規則の策定
- 02認証・認可・入力処理の実装確認
- 03コミットやリリース前のセキュリティテスト計画
- 04依存関係の更新と機密設定の管理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ hack23/ homepage --skill secure-developmentインストール後、Claude Code で/secure-developmentを入力して読み込まれたか確認します。
詳細
セキュア開発スキル
目的
このスキルは、すべてのコード開発がHack23 ISMS セキュア開発ポリシーで定義されているセキュリティ・バイ・デザインの原則に従うようにします。Web アプリケーション、API、インフラストラクチャコード、スクリプトなど、すべてのソフトウェア開発活動に適用されます。
ルール
入力の検証
必須:
- すべてのユーザー入力を受け付け時に検証します(サーバー側での検証は必須です)
- 許可リストによる検証を使用します(許可する内容を定義し、許可しない内容は定義しません)
- データの型、長さ、形式、範囲を検証します
- XSS を防止するため、レンダリング前に出力をエンコードします
- クエリ、コマンド、ファイル操作で使用する前に入力をサニタイズします
- 無効な入力は、システムの詳細を公開しない明確なエラーメッセージとともに拒否します
禁止:
- クライアント側の検証のみに依存すること
- 検証せずに、外部ソース(API、データベース、ファイル)からのデータを信頼すること
- 主な防御手段としてブロックリストによる検証を使用すること
- 未加工のユーザー入力をブラウザーにそのまま返すこと
- エラーメッセージでスタックトレースやシステム情報を公開すること
認証と認可
必須:
- 公開されていないすべてのリソースに対して認証を適用します
- 管理者アクセスには多要素認証(MFA)を実装します
- 安全なセッション管理(HTTPOnly、Secure、SameSite Cookie)を使用します
- 強力なパスワードポリシー(12 文字以上、複雑さの要件)を適用します
- ログインに 5 回失敗した後、アカウントをロックする仕組みを実装します
- すべての認証イベント(成功と失敗)を記録します
- すべてのアクセス制御に最小権限の原則を適用します
- 入口だけでなく、アクセスするたびに認可を検証します
禁止:
- パスワードを平文または復号可能な暗号化方式で保存すること
- パスワードに脆弱なハッシュアルゴリズム(MD5、SHA-1)を使用すること
- セキュリティレビューを受けずに独自の認証方式を実装すること
- ユーザー間またはシステム間で認証情報を共有すること
- デフォルトで広範なアクセス権を付与すること
暗号化
必須:
- 共通鍵暗号には AES-256 を使用します
- 公開鍵暗号には RSA-2048 以上を使用します
- ハッシュ化には SHA-256 以上を使用します
- すべてのネットワーク通信で TLS 1.2 以上を適用します
- 暗号鍵を安全に保管します(コードではなく、鍵管理システムを使用します)
- 暗号学的に安全な乱数生成器を使用します(Math.random() は使用しません)
- ポリシーに従って暗号鍵をローテーションします
禁止:
- 暗号鍵、パスワード、シークレットをソースコードにハードコードすること
- 非推奨のアルゴリズム(DES、3DES、MD5、SHA-1、SSL、TLS 1.0/1.1)を使用すること
- 独自の暗号アルゴリズムを実装すること
- シークレットをバージョン管理に保存すること(シークレット管理ツールを使用してください)
- 脆弱または予測可能な初期化ベクトル(IV)を使用すること
安全なエラー処理
必須:
- 集中管理型のエラー処理を実装します
- 調査に十分な詳細情報とともに、セキュリティ関連のエラーをすべて記録します
- ユーザーには一般的なエラーメッセージを返します
- エラー時にはアクセスを拒否するなど、安全に失敗するようにします(安全でない状態にはフォールバックしません)
- finally ブロックまたは同等のパターンでリソースをクリーンアップします
- 通常と異なるエラーパターンを監視し、アラートを出します
禁止:
- スタックトレースをユーザーに公開すること
- エラーメッセージに機密データ(パスワード、鍵、トークン、個人識別情報)を含めること
- 機密データ(パスワード、クレジットカード番号、SSN)を記録すること
- 重大なセキュリティエラーの後も処理を続行すること
- セキュリティ例外を記録せずに抑制すること
セキュアな設定
必須:
- 機密性の高い設定には環境変数またはシークレット管理を使用する
- セキュアなデフォルト設定を実装する(デバッグモードを無効にし、セキュリティヘッダーを有効にする)
- セキュリティに関連するすべての設定オプションを文書化する
- 設定変更がセキュリティに及ぼす影響を確認する
- サービスアカウントと API キーには最小権限の原則を適用する
- 不要な機能、サービス、エンドポイントを無効にする
禁止:
- シークレットを含む設定ファイルをバージョン管理にコミットする
- 本番環境でデフォルトの認証情報を使用する
- 本番環境でデバッグ/詳細ログを有効にする
- 管理インターフェースをパブリックネットワークに公開する
- 過度に許可的な CORS ポリシーを使用する
セキュリティテスト
必須:
- コミットごとに静的アプリケーションセキュリティテスト(SAST)を実施する
- 本番環境へのデプロイ前に動的アプリケーションセキュリティテスト(DAST)を実施する
- テストスイートにセキュリティテストケースを含める
- 依存関係の脆弱性スキャンを実施する
- 認証および認可の制御をテストする
- インジェクションの脆弱性につながる入力検証をテストする
- セキュリティテストの結果を文書化する
禁止:
- セキュリティスキャンに合格していないコードをデプロイする
- 適切な根拠と承認なしに、セキュリティ上の指摘を無視または抑制する
- 本番環境でセキュリティ制御をテストする
- テスト環境で本番データを使用する
依存関係の管理
必須:
- 依存関係スキャンツール(Dependabot、Snyk など)を使用する
- セキュリティパッチを適用して依存関係を最新の状態に保つ
- 使用前に既知の脆弱性がないか依存関係を確認する
- 再現可能なビルドを確保するため、パッケージロックファイルを使用する
- 承認済みの依存関係とそのバージョンを文書化する
- 未使用の依存関係を削除する
禁止:
- 重大な脆弱性が既知の依存関係を使用する
- 放棄された依存関係、またはメンテナンスされていない依存関係を使用する
- 信頼できないソースから依存関係をダウンロードする
- セキュリティレビューなしに依存関係のバージョン制約を回避する
コードレビュー
必須:
- すべてのコード変更に対してピアレビューを必須とする
- コードレビューのプロセスにセキュリティレビューを含める
- PR パイプラインで自動セキュリティスキャンを使用する
- セキュリティに関する判断を PR のコメントに記録する
- 承認前にセキュリティ要件が満たされていることを確認する
禁止:
- 必要な承認を得ずにコードをマージする
- CI/CD パイプラインのセキュリティチェックを回避する
- セキュリティ上重要な変更を自分で承認する
例
例 1:セキュアな入力検証(JavaScript)
// GOOD: Allowlist validation with type checking
function validateUsername(username) {
// Define what IS allowed
const usernameRegex = /^[a-zA-Z0-9_-]{3,20}$/;
// Validate type
if (typeof username !== 'string') {
throw new Error('Invalid input type');
}
// Validate format and length
if (!usernameRegex.test(username)) {
throw new Error('Username must be 3-20 characters and contain only letters, numbers, hyphens, and underscores');
}
return username;
}
// BAD: No validation
function validateUsernameBad(username) {
return username; // Accepts anything!
}
// BAD: Blocklist validation
function validateUsernameBad2(username) {
if (username.includes('<') || username.includes('>')) {
throw new Error('Invalid characters');
}
return username; // Still vulnerable to many attacks
}
例 2:セキュアなパスワードハッシュ化(Node.js)
const bcrypt = require('bcrypt');
// GOOD: Use strong hashing with salt
async function hashPassword(password) {
// MUST use at least 12 rounds (2^12 iterations)
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);
return hashedPassword;
}
async function verifyPassword(password, hashedPassword) {
return await bcrypt.compare(password, hashedPassword);
}
// BAD: Using weak hashing
const crypto = require('crypto');
function hashPasswordBad(password) {
// NEVER use MD5 or SHA-1 for passwords!
return crypto.createHash('md5').update(password).digest('hex');
}
// BAD: Storing plaintext
function storePasswordBad(password) {
return password; // NEVER store passwords in plaintext!
}
例 3:セキュアなセッション管理(Express.js)
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
// GOOD: Secure session configuration
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET, // From environment, not hardcoded
name: 'sessionId', // Don't use default 'connect.sid'
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // MUST use HTTPS
httpOnly: true, // MUST prevent JavaScript access
maxAge: 1800000, // 30 minutes
sameSite: 'strict' // CSRF protection
}
}));
// BAD: Insecure session configuration
app.use(session({
secret: 'mysecret123', // Hardcoded secret!
cookie: {
secure: false, // Allows HTTP
httpOnly: false // Vulnerable to XSS
}
}));
例 4:SQL インジェクションの防止(Node.js)
const mysql = require('mysql2/promise');
// GOOD: Parameterized queries
async function getUserByEmail(email) {
const [rows] = await connection.execute(
'SELECT * FROM users WHERE email = ?',
[email]
);
return rows[0];
}
// BAD: String concatenation
async function getUserByEmailBad(email) {
// NEVER concatenate user input into SQL!
const query = `SELECT * FROM users WHERE email = '${email}'`;
const [rows] = await connection.execute(query);
return rows[0];
}
例 5:セキュアなエラー処理(Python)
import logging
# GOOD: Generic error messages to users, detailed logging
def process_payment(user_id, amount):
try:
# Process payment logic
charge_credit_card(user_id, amount)
return {"success": True, "message": "Payment processed"}
except CreditCardError as e:
# Log detailed error securely
logging.error(f"Payment failed for user {user_id}: {str(e)}",
extra={"user_id": user_id, "amount": amount})
# Return generic message to user
return {"success": False, "message": "Payment processing failed. Please try again."}
except Exception as e:
# Log unexpected errors
logging.exception(f"Unexpected error processing payment for user {user_id}")
# Return generic message
return {"success": False, "message": "An error occurred. Please contact support."}
# BAD: Exposing detailed errors
def process_payment_bad(user_id, amount):
try:
charge_credit_card(user_id, amount)
return {"success": True}
except Exception as e:
# NEVER expose stack traces or system details to users!
return {"success": False, "error": str(e), "trace": traceback.format_exc()}
例 6:セキュアな設定(環境変数)
// GOOD: Use environment variables for secrets
require('dotenv').config();
const config = {
database: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD, // Never hardcode!
database: process.env.DB_NAME
},
api: {
key: process.env.API_KEY,
secret: process.env.API_SECRET
},
// Secure defaults
security: {
httpsOnly: true,
hstsEnabled: true,
debugMode: false
}
};
// Validate required environment variables
const required = ['DB_HOST', 'DB_USER', 'DB_PASSWORD', 'API_KEY'];
required.forEach(key => {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
});
// BAD: Hardcoded secrets
const configBad = {
database: {
host: 'localhost',
user: 'admin',
password: 'P@ssw0rd123', // NEVER hardcode passwords!
database: 'production'
},
api: {
key: 'sk_live_abcdef123456' // NEVER commit API keys!
}
};
例 7:XSS の防止(HTML 出力エンコーディング)
// GOOD: Encode output before rendering
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function displayUserComment(comment) {
const safeComment = escapeHtml(comment);
document.getElementById('comment').innerHTML = safeComment;
}
// Or use a trusted library
const DOMPurify = require('dompurify');
function displayUserContent(content) {
const clean = DOMPurify.sanitize(content);
document.getElementById('content').innerHTML = clean;
}
// BAD: Direct HTML insertion
function displayUserCommentBad(comment) {
// NEVER insert unsanitized user input into HTML!
document.getElementById('comment').innerHTML = comment;
}
関連する ISMS ポリシー
このスキルは、以下の要件を実装します。
- セキュア開発ポリシー - セキュアなコーディング慣行に関する主要なポリシー
- アクセス制御ポリシー - 認証および認可の要件
- 暗号化制御ポリシー - 暗号化とハッシュ化の要件
- 情報セキュリティポリシー - 全体的なセキュリティフレームワーク
- データ分類ポリシー - データ取り扱いの要件
関連ドキュメント
- SECURITY_ARCHITECTURE.md - セキュリティ制御とアーキテクチャ
- THREAT_MODEL.md - 脅威分析と緩和策
- SECURITY.md - セキュリティ脆弱性の報告
コンプライアンス対応
ISO 27001:2022
- A.8.24 暗号化の使用
- A.8.25 セキュアな開発ライフサイクル
- A.8.26 アプリケーションセキュリティ要件
- A.8.28 セキュアコーディング
NIST Cybersecurity Framework
- PR.DS-2:転送中のデータを保護します
- PR.DS-6:完全性チェック機構でソフトウェアの完全性を検証します
- PR.IP-2:システムを管理するためのシステム開発ライフサイクルを導入します
CIS Controls
- Control 16:アプリケーションソフトウェアのセキュリティ
- 16.1 セキュアなアプリケーション開発プロセスを確立し、維持する
- 16.2 ソフトウェアの脆弱性を受け付け、対処するプロセスを確立し、維持する
- 16.10 アプリケーションアーキテクチャにセキュアな設計原則を適用する
適用
セキュアな開発ルールへの違反:
- 重大な違反(ハードコードされたシークレット、禁止アルゴリズムの使用):デプロイをブロックし、直ちに修正を求めます
- 高深刻度の違反(入力検証の欠如、脆弱な認証):マージ前の修正を求めます
- 中程度の深刻度の違反:スプリント内の修正を求めます
- 低深刻度の違反:技術的負債のチケットを作成します
すべての違反を追跡し、セキュリティ指標で報告する必要があります。
コメント
Markdown 対応