SQLインジェクションの理解と回避

マイケル・ギャリソン 一ヶ月前 6分で読める
Elevenlabs Text to Speech AudioNative Player を読み込んでいます...

Rockの管理に時間を費やしたことがあれば、カスタム・ページ、レポート、ダッシュボードなどの作成など、その強力なカスタマイズ機能を検討したことがあるでしょう。Rockはデータへの完全なアクセスを提供し、無限の可能性を可能にします。

しかし、この力には責任も伴う。ジュラシック・パーク』では、イアン・マルコムの有名な名言がある。これはロックのカスタマイズにも当てはまります。最も簡単な方法だけでなく、正しい方法でソリューションを導入しなければなりません。

安易な方法」による解決策のリスクの一例として、SQLインジェクションと呼ばれるものがある。これは、気づかないうちに驚くほど簡単に侵入してしまう脆弱性の一種です。この言葉は専門的で難しく聞こえるかもしれないが、これを理解することはすべてのロック管理者にとって不可欠である。それを分解してみよう。


SQLインジェクションとは何か?

想像してみてください:あなたは、メンバーの誰もが自分の配偶者の名前とEメールを見ることができるテンプレートを作成しています。結局のところ、何かを更新する必要があるときに、簡単に確認できるようにしたいですよね?

情報を取得する一つの方法は、SQLステートメントを使用することです。幸いなことに、基本的なSQLクエリは比較的簡単に学ぶことができ、ロック・コミュニティは常に、必要性を説明できる人なら誰でも、スニペットやポインターを提供してくれる。

そこで、公開ページにHTMLブロックを追加して、クエリを作り始めます。シンディ・デッカーをクエリにハードコードするのは今はいいのです。SQLコマンドを有効にして、このテンプレートを使います:

{% sql %}
SELECT
  [NickName]
  , [LastName]
  , [Email]
FROM
  [Person]
WHERE
  [Guid] = 'b71494db-d809-451a-a950-28898d0fd92c';
{% endsql %}

{% for row in results %}
  <p>{{ row.NickName }} {{ row.LastName }}: {{ row.Email }}</p>
{% endfor %}

責任ある管理者であるあなたは、PersonIdを使用することを避けてきました。PersonIdはシーケンシャルで予測可能なため、誰でもIDを循環させて連絡先情報を抽出することができるからです。その代わりに、簡単に推測できないGUIDを使用しています。テンプレートを実行すると、ページがロードされたときにシンディの詳細が表示されることが確認できます。

しかし、先に進む前に自問してほしい:このクエリが常に意図したとおりに実行されると信じていいのか?SQLクエリはロックのセキュリティをバイパスし、データベースへのフルアクセスを許可する。もし悪意のある誰かがこのクエリを改ざんして害を及ぼすことを防ぐにはどうすればいいのだろうか?

「自問自答してみよう:このクエリーが常に我々の意図した通りに動いてくれると信じていいのか?

このクエリでは、ロック管理者だけがブロック設定を編集できるようにします。このクエリは設定内で定義され、外部からの入力に依存しないので、その役割の全員を信頼するとして、信頼することができます。このため、この情報が必要なページで使用するのは一般的に安全です。

もちろん、すべての人にシンディの情報を見てもらいたいわけではない。 所有する の配偶者の情報を検索します。これを動的にするために、URLパラメータに基づいて検索を行うようにクエリを更新します。このようなページのURLは次のようになります: https://www.rocksolidchurchdemo.com/spouseinfo?SpouseGuid=b71494db-d809-451a-a950-28898d0fd92c (いや、このページはデモでは存在しないが、我々が何をしているかはわかるだろう)。

これは上で使ったのと同じテンプレートだが、ハードコードされたGUIDを、URLからテキストを取得するLavaと入れ替えた(変更した部分をハイライトした):

{% sql %}
SELECT
  [NickName]
  , [LastName]
  , [Email]
FROM
  [Person]
WHERE
  [Guid] = '{{ 'Global' | PageParameter:'SpouseGuid' }}';
{% endsql %}
{% for row in results %}
  <p>{{ row.NickName }} {{ row.LastName }}: {{ row.Email }}</p>
{% endfor %}

この変更を行ったので、別の有効なGUIDを提供するようにURLを変更すれば、代わりにその人の情報が表示される。

繰り返しになるが、このクエリーが常に自分の意図した通りに動いてくれると信じていいのだろうか?

IDの代わりにGUIDを使うことは、簡単なデータスクレイピングを防ぐのに役立ちますが、私たちはもう一つのリスクを見落としていました。URLは常に有効なGUIDを含むと想定していますが、もし誰かが予期せぬものを入力したらどうなるでしょうか?

これがSQLインジェクションの核心であり、信頼できない入力を取り込み、それをクエリの一部にすることである。

信頼できない入力を取り込み、それをクエリの一部とするとき、私たちは常にSQLインジェクションのリスクを負うことになる。

もし誰かがSpouseGuid=を設定したらどうなるか。OR 1=1 --」。?ちんぷんかんぷんに見えるかもしれないが、LAVAがそうであるように、これをクエリに突っ込んで、何が起こるか見てみよう。

単純な問い合わせはこうなる:

SELECT [NickName], [LastName], [Email] FROM [Person]
WHERE [Id] = '' OR 1=1 --'.
(その -- はSQLのコメントを開始し、末尾の ').

やばい!クエリに生テキストを注入しているので、そのトラブルメーカーは、データベース内のIDが''、つまり1=1であるすべての人物を表示するようにクエリを乗っ取ったのだ。1は常に1に等しいので、私たちのクエリは今、牧師、スタッフ、すべての人の連絡先の詳細を公開しています。

さらに悪いことになる。フィルターを修正する代わりに、まったく新しいコマンドを挿入したらどうだろう: DROP TABLE [FinancialTransaction] (金融取引)?

そうなれば、財務記録はすべて消えてしまう


SQLインジェクションからの保護

SQLインジェクションがいかに些細で強力なものであるかがお分かりいただけたと思います。そこで、このような事態から身を守る方法についてお話ししましょう!

オプション1。

このSQLベースの攻撃を回避する最も一般的な方法は、...SQLを使わないことだろう!多くの場所では 可能性がある SQLを使えば、代わりにLavaを使って同じデータを得ることができる。この場合 PersonByGuid SQLを使用する代わりに、人物レコードを取得するためのLavaフィルター。

これは上のクエリと同じことをする:

{% assign spouse = 'Global' | PageParameter:'SpouseGuid' | PersonByGuid %}
<p>{{ spouse.NickName }} {{ spouse.LastName }}: {{ spouse.Email }}</p>

Lavaフィルターは読み取り専用で、エンティティのセキュリティを尊重しているため、ここでの唯一のリスクは、誰かが他の人のGUIDを推測したり取得したりした場合だけです。その場合でも、アクセスできるのはその一人の電話番号だけで、複数のレコードを閲覧するように拡大する方法はありません。

オプション2。

SQLコマンドを使用することが最善あるいは唯一の選択肢である場合、クエリで信頼できない入力を処理する最も安全な方法は、パラメータを使用することです。これらについてはSQLコマンドのドキュメントでSQLパラメータの見出しで説明されています。

例えば、ユーザー入力を直接挿入する代わりに、パラメーターとして割り当てる:

{% assign spouseGuid = 'Global' | PageParameter:'SpouseGuid' %}
{% sql SpouseGuid:'{{ spouseGuid }}' %}
SELECT
  [NickName]
  , [LastName]
  , [Email]
FROM
  [Person]
WHERE
  [Guid] = @SpouseGuid;
{% endsql %}
{% for row in results %}
  <p>{{ row.NickName }} {{ row.LastName }}: {{ row.Email }}</p>
{% endfor %}

これは、信頼できない入力が決してクエリ自体の一部にならないために機能する。その代わりに、GUIDが指定された値と一致するレコードを返すようにSQLに指示する参照として機能する。攻撃者の入力は変数をエスケープすることができません。 OR 1=1 --」。 は単にゼロの結果を返す。

オプション3。

上記の2つがどうしても使えない場合の3つ目の選択肢は、SanitizeSql Lavaフィルターに頼って、信頼できない入力を安全にすることです。理想的には、これらの値をSQL変数に変換するか、上記のようにSQLパラメータ内で使用することです。

最低限、クエリーでLavaを直接使用しなければならない場合、次のようになります。 WHERE [Guid] = '{{ 'Global' | PageParameter:'SpouseGuid' | SanitizeSql }}'

通常、値が特定のデータ型であるべき場合は、まず値をサニタイズし、次にSQLに型を強制させるのが最良の方法です。これにより、無効な入力が正しく変換されるか、空白の結果が返されるようになり、潜在的な悪用を防ぐことができます。

GUID値を期待する場合はこうなる:

{% sql %}
DECLARE @SpouseGuid UNIQUEIDENTIFIER = '{{ 'Global' | PageParameter:'SpouseGuid' | SanitizeSql }}';
SELECT
  [NickName]
  , [LastName]
  , [Email]
FROM
  [Person]
WHERE
  [Guid] = @SpouseGuid;
{% endsql %}
{% for row in results %}
  <p>{{ row.NickName }} {{ row.LastName }}: {{ row.Email }}</p>
{% endfor %}

(UNIQUEIDENTIFIERはSQL ServerではGUIDと呼ばれている)。

期待する値が整数(整数)であれば、次のようになります。 DECLARE @myId INT = '[あなたの溶岩]' のような非整数の文字がないことを確認する。 ' または -- その値によってクエリの性質を変えることができる。


結論

SQLインジェクションは、クエリの一部として外部のものを使用する場合、常に非常に現実的なリスクです。しかし、あなたはSQLインジェクションを見分ける方法を知り、Lavaでは対応できない場合にあなたのクエリを保護するツールを手に入れたのです。

この学校にあなたのような知識を持った開発者がいればいいのだが!

執筆 トライアンフ社 ミニストリーアナリスト、トレーナー

仕事を始めよう

あなたのRock RMSのアイデアを形にしてみませんか?

私たちがお手伝いします。

お問い合わせ