Aikido

SQLでSELECT *を避ける方法:データ漏えいを防ぐ

パフォーマンス

ルール
避ける セレクト *  SQL クエリを避ける。
SELECT *  プロダクション コード を作成する アプリケーションは
アプリケーションを  スキーマ 変更 そして データ データ に依存する。

対応言語 45+

はじめに

使用 セレクト を使用したクエリは、アプリケーションが使用しないカラムも含めて、テーブルからすべてのカラムを取得します。データベーススキーマが進化し、新しいカラム(パスワードやPIIのような機密データを含む)が追加されると、クエリで セレクト は、コードを変更することなく、それらを自動的に取得し始める。これはセキュリティの脆弱性を生み出し、アプリケーション・ロジックの前提を崩すことになる。

なぜそれが重要なのか

パフォーマンスへの影響:不要なカラムを検索すると、クエリの実行時間、ネットワーク転送サイズ、メモリ消費量が増加します。5カラムしか必要ない50カラムのテーブルは、必要以上に10倍のデータを転送することになり、レスポンスタイムを低下させ、インフラコストを増加させます。

セキュリティーへの影響: テーブルに追加された新しいカラム(監査フィールド、内部フラグ、センシティブなユーザーデータ)は、以下の方法で自動的に公開されます。 セレクト クエリ。あなたのAPIは、パスワードハッシュ、SSN、またはそのエンドポイント用に意図されていなかった内部ビジネスデータをリークし始めるかもしれない。

コードの保守性: いつ セレクト スキーマの変更後にクエリが壊れる場合、コンパイル時ではなく実行時にエラーが発生します。NULLでない新しいカラムやリネームされたフィールドはプロダクション・エラーを引き起こす。明示的な列リストは依存関係を明確にし、スキーマが非互換に変更された場合にビルドを中断させます。

コード例

非準拠:

async function getUserProfile(userId) {
    const query = 'SELECT * FROM users WHERE id = ?';
    const [user] = await db.execute(query, [userId]);

    return {
        name: user.name,
        email: user.email,
        createdAt: user.created_at
    };
}

なぜ間違っているのか:これはpassword_hash、ssn、internal_notes、deleted_atのような潜在的にセンシティブなフィールドを含むすべてのカラムを取得します。スキーマが大きくなるにつれて、このクエリは遅くなり、より多くのデータが公開されます。

✅ 準拠:

async function getUserProfile(userId) {
    const query = `
        SELECT name, email, created_at
        FROM users
        WHERE id = ?
    `;
    const [user] = await db.execute(query, [userId]);

    return {
        name: user.name,
        email: user.email,
        createdAt: user.created_at
    };
}

結論

SQLクエリでは常に明示的なカラムリストを指定する。これにより、データ漏洩を防ぎ、パフォーマンスを向上させ、コードとスキーマの依存関係を明確にすることができる。カラム名を入力するためのわずかな先行コストで、セキュリティやパフォーマンスの問題全体を防ぐことができる。

よくある質問

ご質問は?

SELECT *はどのような場合に許容されるのか?

開発中やデバッグ中のアドホックなクエリのみで、本番コードでは決して使用しないでください。データ移行スクリプトや単発のレポートで、本当にすべてのカラムが必要な場合は、SELECT *が合理的です。アプリケーションコードでは、スキーマの変更は避けられないため、現在すべてのカラムが必要であっても、常に明示的なカラムリストを使用する。

SELECT *クエリを生成するORMについてはどうだろうか?

Configure your ORM to select specific fields. Most ORMs (Sequelize, TypeORM, Prisma, SQLAlchemy) support field selection: User.findOne({ attributes: ['name', 'email'] }) or prisma.user.findUnique({ select: { name: true, email: true } }). Always use these options to control what data is retrieved.

SELECT *はデータベースのパフォーマンスに大きな影響を与えますか?

特に幅の広いテーブルではそうだ。データベースはディスクからより多くのページを読み込む必要があり、インデックスをあまり効果的に使用できず、クエリの結果セットはデータベース・バッファ・プールでより多くのメモリを消費する。ネットワーク転送時間はデータサイズに比例して長くなります。TEXTカラムやBLOBカラムを持つテーブルの場合、その影響は深刻です。

最も多くのカラムが必要なクエリはどのように処理すればよいですか?

明示的に列挙する。IDEのオートコンプリートを使用するか、information_schemaにクエリを実行してカラムリストを生成します。ビューオブジェクトを作成したり、ユースケースごとに必要なカラムを定義したデータベースビューを使用するチームもあります。明示的なリストの明快さと安全性は、ちょっとした不便さを凌駕します。

コードベース内のSELECT *を検出するには?

コードベースからSELECT *(大文字小文字を区別しない)というパターンを検索する。多くの静的解析ツールやデータベース・クエリ・アナライザは、このようなフラグを立てることができます。コードレビューでは、アプリケーションコードに SELECT * を含む PR はすべて却下する。チームによっては、プレコミットフックやCIチェックを使って、これらのパターンを自動的に検出してブロックしている。

まずは無料で体験

コード、クラウド、ランタイムを1つの中央システムでセキュアに。
脆弱性を迅速に発見し、自動的に修正。

クレジットカードは不要。