セキュリティ脆弱性の予防
ソフトウェアの脆弱性はシステムに深刻な被害をもたらす可能性があります。悪意を持って悪用された場合、ウイルス感染、データ漏洩や破損のリスクに加え、直接的または間接的な経済的損失に直面する可能性もあります。では、これらの脆弱性をどのように予防すればよいのでしょうか?脆弱性の問題について深く掘り下げる前に、まず脆弱性とは何かを明確にする必要があります。
脆弱性とは?
脆弱性とは、ソフトウェア、システム、またはネットワークに存在するセキュリティ上の弱点や欠陥のことを指し、これらの弱点によってシステムが攻撃を受けたり、不正に使用されたりする可能性があります。コンピュータセキュリティの分野では、脆弱性は通常、プログラミングエラー、設計上の欠陥、または設定ミスに起因します。
オブジェクト関係マッピング(ORM)フレームワークにおいて、脆弱性とは通常、設計または実装におけるセキュリティ上の問題を指し、これによりアプリケーションがSQLインジェクション攻撃のリスクにさらされる可能性があります。
どのような場合にSQLインジェクション攻撃が引き起こされるのでしょうか?通常、以下の状況で発生します。
- テーブル構造部分:通常、テーブルフィールド、テーブル名などの固定内容を含みます。
- テーブルフィールドパラメータ/変数部分:様々な動的SQLパラメータを含みます。
通常、ORMにおけるSQLインジェクション脆弱性の発生は、上記の2つの部分がフロントエンドからのパラメータ受け渡しを許可していることが原因です。
脆弱性の予防方法
脆弱性が発生する主な原因がわかれば、テーブル構造とパラメータに関連するデータを適切に制御し、それらがフロントエンドに露出するのを防ぐことで、脆弱性攻撃を回避できます。
テーブルフィールド部分
テーブルフィールド部分は通常、バックエンドで制御されるべきです。しかし、システムによっては十分な柔軟性を保つために、フロントエンドが動的にデータベースフィールド名を渡すことを許可している場合があります。この方法はシステムの柔軟性の要求を満たしますが、非常に大きなSQLインジェクションのリスクに直面します。
リスクを回避したい場合は、システム設計者または開発者がフィールドの安全性を自ら制御する必要があり、フロントエンドが任意の文字列を渡して直接SQLフィールドに変換することを絶対に許可してはなりません。フィールドマッピングロジックを通じて攻撃を遮断し、フロントエンドインターフェースから渡されたフィールド内容が直接SQLコンパイル段階に入り、最終的なSQLを生成するのを防ぐべきです。
フィールドパラメータ/変数部分
フィールドパラメータ部分については、ORMフレームワークは通常、SQLインジェクション攻撃を防ぐためのプリコンパイルロジックを備えています。MyBatisでは、$
プレースホルダーではなく #
プレースホルダーを使用することで、SQLインジェクション攻撃を回避します。
MyBatis-Plusが関連するSQLを生成する際の基盤能力も同様にMyBatisに由来するため、同じように #
プレースホルダーを使用して攻撃を回避できます。ただし、このステップはMyBatis-Plusによって自動的に実行されます。
ユーティリティクラスを使用した予防
一般的に、上記の処理を行うことでSQLインジェクション攻撃を防ぐことができます。それでも不安な場合は、MyBatis-Plusが提供するユーティリティクラス SqlInjectionUtils.check(内容)
を使用して、文字列にSQLインジェクションが存在するか検証できます。存在する場合は対応する例外がスローされます。
// 自動SQLインジェクションチェックを有効化 (3.5.3.2+ バージョンでサポート)Wrappers.query().checkSqlInjection().orderByDesc("任意前端传入字段,我们推荐最好是白名单处理,因为可能存在检查覆盖不全情况")// 手動検証方式 (3.4.3.2+ バージョンでサポート)SqlInjectionUtils.check("任意前端传入字段,我们推荐最好是白名单处理,因为可能存在检查覆盖不全情况")
悪意のある脆弱性に関する説明
MyBatis-Plusに関連するコードとJarパッケージに対して、悪意のある者がCVE脆弱性を申請した事例があります。以下に、これらの脆弱性に関する公式声明を述べます。
注意! このような公式に認められていない「CVE脆弱性」
は、フレームワーク自体、ユーザー、プロジェクトの納品に非常に大きな影響を与えます。あなたの 他人を損なって自分も得しない行為
は、他の人に非常に大きな経済的損失をもたらします。
安全でない設計である場合は、最善の方法は issue または pull request
を通じて公式が速やかに修正するのを支援することです。
公式ドキュメントも 再三再四
、 SQLフラグメント
は必ず安全性をチェックする必要があると強調しています。どの ORMフレームワークも、JDBCを含めて
、 文字列を直接SQLに連結する
状況を許可しています。したがって、フロントエンドがSQLフラグメントを渡すことを許可しないことを強く推奨します。
CVE-2024-35548
詳細リンク:CVE-2024-35548
この「脆弱性」も、フロントエンドが SQLフラグメント
を渡すことによるSQLインジェクション攻撃に関するものです。フレームワークの QueryWrapper
と UpdateWrapper
のフィールド部分はサブクエリを許可するため、人為的にフロントエンドがSQLフラグメントを渡すことを許可すべきではありません。
このようなニーズがある使用者は、 SqlInjectionUtils.check(内容)
または xxWrapper.checkSqlInjection()
メソッドを使用してチェックできます。チェックを通過した場合、例外はスローされません。
フレームワークは、より厳格な条件コンストラクタである LambdaQueryWrapper
と LambdaUpdateWrapper
も提供しており、これらを使用することを推奨します。
CVE-2023-25330
詳細リンク:CVE-2023-25330
この「脆弱性」は、いわゆるマルチテナントプラグインが引き起こす脆弱性について説明しており、マルチテナントプラグインがSQLインジェクション攻撃を引き起こすとされています。これはどのように行われるのでしょうか?
この「脆弱性」の申請者は、悪意を持ってテナントIDをフロントエンドに公開し、フロントエンドがテナントIDを渡すことを許可し、それをコンテキストに保持させ、プラグイン実行段階で直接読み取って使用させています。
テナント分離に関する要件を扱ったことがあれば、通常の方法は、ユーザーがログインした後にバックエンド自身がユーザーに対応するテナントをクエリし、自らコンテキストを保持して、マルチテナントプラグインが正常に動作することを保証するものであることがわかります。
たとえテナント切り替え操作を行うとしても、フロントエンドから渡された切り替え先のテナントIDを、そのままプラグインに使用させることはありえず、切り替えが可能かどうかを检测する必要があります。
もしどうしても問題だと言うなら、それは使用時の考慮不足によるものであり、基盤フレームワークとして、使用者がこれらの機能をどのように使用するかを制約することはできません。何もかも基盤フレームワークに責任を負わせるなら、誰もが受け身の姿勢になるだけで、オープンソースなどやる必要はありません。
CVE-2022-25517
詳細リンク:CVE-2022-25517。元の脆弱性リポジトリは既に削除されているため、詳細分析はこちらからご覧いただけます。
この「脆弱性」はさらに滑稽で、テーブルフィールドをフロントエンドが渡せる一部として直接連結に使用し、そしてこれを無理やり脆弱性だと言い張っています。その理由は、MyBatis-PlusがString型のフィールドパラメータを公開しているため、SQL攻撃スクリプトを渡すために使用できるというものです。
私たちは、MyBatis-PlusがLamdbaQueryWrapperを提供し、LamdbaQueryWrapperを使用してType-Safeなクエリを実行できることを知っています。そして、大多数の人がこの方法を使用していると信じています。たとえ普通のQueryWrapperを使用したとしても、String型のフィールドがあるにせよ、それは決してフロントエンドから渡されるものではなく、すべてのフィールドをフロントエンドから渡すのであれば、バックエンドは何をするのでしょうか?いっそのことフロントエンドに直接SQLを書かせればよいでしょう。
真の脆弱性問題であれば、私たちは必ず積極的に修正します。しかし、上記2つのような低レベルな誤りを、私たちと事前にコミュニケーションを取ることなく、直接CVE脆弱性申請として提出することは、これらの脆弱性が善意の注意であると私たちに信じさせることは難しく、私たちから見れば、これは純粋に悪意のある行為です。