sql インジェクション・プ ルーフ pl/sql の記述方法 ·...

67
SQL インジェクション・プ ルーフ PL/SQL の記述方法 Oracle ホワイト・ペーパー 2008 12

Upload: others

Post on 18-Oct-2019

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

SQL インジェクション・プ

ルーフ PL/SQL の記述方法 Oracle ホワイト・ペーパー

2008 年 12 月

Page 2: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

ご注意

このホワイト・ペーパーは、弊社の一般的な製品の方向性に関する概要を示すも

ので、情報提供を唯一の目的とするものであり、いかなる契約にも組み込むこと

ができません。下記の事項は、マテリアルやコード、機能の提供を確約するもの

ではなく、また、購買を決定する際の判断材料とはなりえません。オラクルの製

品に関して記載されている機能の開発、リリース、および時期については、弊社

の裁量により決定いたします。

Page 3: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

SQL インジェクション・プルーフ PL/SQL の記述方法

概要 ...................................................................................................................... 1 はじめに .............................................................................................................. 2

このホワイト・ペーパーの定期的な改訂 ................................................. 3 SQL インジェクションの定義 .......................................................................... 4

新しい概念の紹介:SQL 構文テンプレート............................................. 4 コンパイル時固定 SQL 文テキストと実行時作成 SQL 文テキストを 区別する......................................................................................................... 6 静的 SQL 構文テンプレートと動的 SQL 構文テンプレートを区別する7

静的 SQL 構文テンプレートの定義...................................................... 7 動的 SQL 構文テンプレートの定義...................................................... 8

SQL インジェクションの定義 .................................................................. 11 SQL インジェクションはどのようにして発生するのか ............................ 12

例 1:ユーザー指定の列比較値 ................................................................ 12 例 2:ユーザー指定の表名 ........................................................................ 14 反例 3:ユーザー指定の where 句 ............................................................ 17 反例 4:意図が不明な SQL 構文テンプレート....................................... 17

SQL のリテラルまたは単純 SQL 名の安全性を保証する........................... 19 SQL のリテラルの安全性を保証する ...................................................... 19

SQL のテキスト・リテラルの安全性を保証する ............................. 19 SQL の日付リテラルの安全性を保証する ......................................... 20 SQL の数値リテラルの安全性を保証する ......................................... 23

単純 SQL 名の安全性を保証する ............................................................. 25 費用効率の高い、SQL インジェクションから保護するためのルール..... 28

PL/SQL API を介してのみクライアントにデータベースを公開する .. 28 可能な場合は、コンパイル時固定 SQL 文テキストを使用する.......... 28 可能な場合は、実行時作成 SQL 文テキストには静的 SQL 構文テンプ

レートを使用する....................................................................................... 30 SQL 構文テンプレート内の値プレースホルダの置換 ..................... 30 SQL 構文テンプレート内の単純 SQL 名プレースホルダの置換.... 31

動的SQL構文テンプレートを使用する必要性を動的テキストの必要性

と混同しない............................................................................................... 32 安全性を保証するための正式で十分な規定 ........................................... 33

静的テキスト ......................................................................................... 33 動的テキスト ......................................................................................... 34 安全な動的テキスト ............................................................................. 34 安全な SQL 文テキスト........................................................................ 35

実行時作成 SQL 文テキストの安全性を、実行コードの直前で確立する

...................................................................................................................... 36 シナリオ ............................................................................................................ 38

先頭および末尾に%文字を追加して、like 条件を作成する ................. 38 実行時まで多数の要素がわからない IN リスト ..................................... 39 サンプル・フォームによる問合せ ........................................................... 40 コールバック............................................................................................... 43

Page 4: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

既存コードの分析と強化 ................................................................................ 47 結論 .................................................................................................................... 48 付録 A: 変更履歴........................................................................................... 50 付録 B: このホワイト・ペーパーで紹介されている新しい専門用語の 定義 ........................................................................................................................ 51

一般 SQL 名 ................................................................................................. 51 エキゾチック SQL 名 ................................................................................. 51 コンパイル時固定 SQL 文テキスト ......................................................... 51 実行時作成 SQL 文テキスト ..................................................................... 51 SQL 構文テンプレート .............................................................................. 51 値プレースホルダ....................................................................................... 52 静的 SQL 構文テンプレート ..................................................................... 52 動的 SQL 構文テンプレート ..................................................................... 53 静的テキスト............................................................................................... 53 動的テキスト............................................................................................... 53 安全な動的テキスト................................................................................... 54 安全な SQL 文テキスト ............................................................................. 54 トップ・レベルの PL/SQL ブロック........................................................ 54

付録 C: SQL インジェクションの防止ルールの概要............................... 55 付録 D: 動的 SQL を実行する、そのほかのオラクル提供サブプログラム

............................................................................................................................ 58 DBMS_Utility.Exec_DDL_Statement() ......................................................... 58 DBMS_DDL.Create_Wrapped() ................................................................... 59 DBMS_HS_Passthrough ............................................................................... 59

DBMS_HS_Passthrough.Execute_Immediate()....................................... 59 DBMS_HS_Passthrough.Parse()............................................................. 59

OWA_Util ...................................................................................................... 59 OWA_Util.Bind_Variables() .................................................................... 60 OWA_Util.ListPrint()............................................................................... 60 OWA_Util.TablePrint() ............................................................................ 60

付録 E: 動的ポリモフィズムを使用するコールバックの実装を表すため

の自己完結型のコード .................................................................................... 61

Page 5: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

SQL インジェクション・プルーフ PL/SQL の記述方法

概要

インターネットで"SQL インジェクション"を検索すると、約 400 万件のヒットが

あります。このトピックは人々の興味を引くとともに、根拠のない恐れを抱かせ

るものです。このホワイト・ペーパーでは、SQL インジェクションについてわか

りやすく説明し、SQL インジェクションから保護されるデータベース PL/SQL プ

ログラムを作成する方法を簡潔に説明します。

PL/SQLサブプログラムが実行時にSQLを作成する場合のみ、SQLインジェクショ

ンの攻撃を受けるリスクが発生します。また、PL/SQL のコンパイル時に SQL を

凍結させる方法は、思っているより簡単です。さらに、実行時に SQL を作成する

必要があるまれなシナリオにおいて、リスクを防ぐためにルールが必要であるこ

とがわかります。これらのルールは簡単に説明でき、容易に理解できるものです。

SQL インジェクション・プルーフ PL/SQL の記述方法

1

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 6: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

はじめに このホワイト・ペーパーの作成時点では、インターネットで"SQL インジェクショ

ン"を検索すると、約 400 万件のヒットがあります。このトピックは人々の興味を

引くとともに、根拠のない恐れを抱かせるものです。このホワイト・ペーパーで

は、SQL インジェクションをわかりやすく説明し、SQL インジェクションから保

護されるデータベース PL/SQL プログラムを作成する方法を簡潔に説明します。

最新版を参照している必要がありま

す。各ページの上部にある URL を確

認してください。

説明する範囲は、データベースに格納されている PL/SQL ユニットに限定します。

クライアント側のプログラムを実装するために使用される C や Java などの言語の

説明についても同様の原則が当てはまりますが、そのようなプログラムのアクセ

ス制御はより難しいものになります。また、データベースへのアクセスをそのよ

うなクライアント側のプログラムのみを使用しておこなうようにするのは、さら

に難しいものになります。 説明を十分に理解していただくためには、読者はデータベースPL/SQLユニットか

らSQLが実行されるさまざまな方法について熟知している必要があります。この

トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices』1に記載されています。そのため、本書の前に、このホワイト・ペーパー

を読むことを推奨します。とくに、『Doing SQL from PL/SQL』ホワイト・ペーパー

では、SQLによるデータベースへの直接アクセスを禁止し、 小限のPL/SQL APIを介してのみクライアントにデータベースを公開するための戦略が説明されてい

ます。2この戦略を採用した場合、SQLインジェクションに対するプルーフィング

は、データベースPL/SQLだけが担当することになり、この形態における十分なソ

リューションが可能となります。 もちろん、定義されていないものは回避できないので、まず、"SQLインジェクショ

ンの定義"の項(4 ページ)で定義を示します。"SQLインジェクションはどのよう

にして発生するのか"の項(12 ページ)で、この定義を使用して、いくつかの有名

な脆弱コードの例を検証します。また、SQLインジェクションの定義を検証する

ために、いくつかの反例を使用して考察します。 この 2 つの項の説明により、SQLインジェクションは、PL/SQLサブプログラムが

無検査のユーザー入力3を使用して、実行時にテキストを作成する場合においての

み発生する可能性があることを理解していただけると思います。SQLインジェク

ションを回避する 良の方法は、すべてのSQL文のテキストが、そのSQL文を実

行するPL/SQLプログラムのソース・コードのみから作成されるようにすることで

す。 ただし、そのような完璧なアプローチが要件に合わない場合は、結局、ユーザー

が入力するテキストを安全に処理する必要があります。このトピックの詳細につ

いては、"SQLのリテラルまたは単純SQL名の安全性を保証する"の項(19 ページ)

を参照してください。 この 初の 3 つの項の内容は、次の項(28 ページ)"費用効率の高い、SQLインジェ

クションから保護するためのルール"の論理的根拠となっているので、内容を理解

するのに役立ちます。このホワイト・ペーパーが独自の貢献を果たすとすれば、

それは概念フレームワーク、および関係する専門用語の作成にあり、それらを使

用することで、ルールを簡潔かつ正確に記載できます。新たな専門用語は、適切

なポイントで紹介し、定義しています。

1. 『Doing SQL from PL/SQL:Best and Worst Practices』は、Oracle Technology NetworkのWebサイトに公

開されています。アドレスは次のとおりです。 www.oracle.com/technology/tech/pl_sql/pdf/doing_sql_from_plsql.pdf

2. この内容については、”PL/SQL APIを介してのみクライアントにデータベースを公開する”の項(28ページ)を参照してください。

3. この概念は、”動的テキスト”の項(34 ページ)で正式に定義されています。

SQL インジェクション・プルーフ PL/SQL の記述方法

2

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 7: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

しかし、まだ定義されていない用語を紹介する順番を、前の語句を受けずに決め

るのは難しいことです。したがって、付録B:"このホワイト・ペーパーで紹介さ

れている新しい専門用語の定義"(51 ページ)に新しい用語を一覧表示し、簡単な

定義を加え、このホワイト・ペーパー内で該当の用語が使用されている項への相

互参照を示してあります。 次の"シナリオ"の項(38 ページ)では、前述の項で定義された概念およびルール

をテストするために必要ないくつかのシナリオについて説明します。それらのシ

ナリオでは、ユーザーの入力により作成されるSQL文を使用する必要があるよう

に(初心者には)思えます。しかし、ユーザーの入力内容からSQL文を作成する

必要があるシナリオは、多くのプログラマーが考えるよりはるかに少なく、たい

ていは、ソース・コードのみからSQL文のテキストを作成する方法で問題なく実

装できます。それらのシナリオの一部を"シナリオ"の項で紹介します。

このホワイト・ペーパーでは、新しいインジェクション・プルーフPL/SQLコード

の記述方法に焦点を当てています。 後の"既存コードの分析と強化"の項(47 ペー

ジ)では、脆弱な可能性がある既存のコードについて簡単に説明します。

このホワイト・ペーパーで説明し、主張しているルールは、クイック・リファレ

ンスとして掲載4されています。詳細については、付録C:"SQLインジェクション

の防止ルールの概要"(55 ページ)を参照してください。

これらのルールは、インジェクションに対する保護を保証するもので、容易に理

解できます。さらに、プログラマーが見落としがちなエッジ・ケースにおけるセ

マンティックの正確さも保証されます。

このホワイト・ペーパーの定期的な改訂

このホワイト・ペーパーには、いくつかのスペルミスや文法上の誤りがある可能

性があります。そのため、定期的に改訂されることになります5。また、読者から

のフィードバックによって、一部の説明が改善されることがあります。そのため、

読み始める前に、ホワイト・ペーパーが 新版であることを確認してください。

新版は、ページのヘッダーに表示されているURLにアクセスして入手できます。

URL も変更されることがあります。ただし、次の URL からは Oracle Technical Network の PL/SQL Technology Center に常にアクセスできます。

http://www.oracle.com/technology/global/jp/tech/pl_sql/index.html

万が一、このホワイト・ペーパーが移動されている場合でも、このページから簡

単に見つけることができます。

4. このホワイト・ペーパーは、Adobe Framemaker 8.0 を使用して作成されています。Adobe Framemaker

8.0 の相互参照機能を使用することで、ソースの文章にあるテキストを参照先に含めています。そ

のため、読者はクイック・リファレンス・サマリーにある各ルールの表現が、それらが記載されて

いる箇所の表現と同じであることを確信できます。(ただし、このメカニズムではフォントのニュ

アンスは保持されません)。

5. このホワイト・ペーパーの変更履歴は、 後に記載されています。付録A:"変更履歴"(50 ページ)

を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

3

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 8: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

SQL インジェクションの定義

SQL インジェクションの定義を理解するには、SQL 言語の構文および構文の解析

方法に関する簡単な知識が必要になります。

次の 2 つのSQL文を考えてみましょう。Code_1およびCode_2、は次のとおりです。

-- Code_1 select c1 from t where c2 = 'Smith'

-- Code_2 select c1 from t wear c2 = 'Smith'

Oracle Databaseに接続するまでもなく、Code_1は構文的に正しく、Code_2には構

文エラーがあるのは明らかです6。次に、構文的には正しい 2 つのSQL文を考えて

みましょう。Code_3とCode_4です。

-- Code_3 select a1 from r where a2 = 'Jones' -- Code_4 select b1 from s where b2 = :1

トークン:1 はプレースホルダです。列b1 とb2 がある表sがアクセス可能な場合、

Code_4の解析はエラーにならず終了します。7

新しい概念の紹介:SQL 構文テンプレート

Code_1、Code_3、およびCode_4のSQL文は、同じSQL構文テンプレート、Template_1の異なるインスタンスです。8

-- Template_1

select &&1 from &&2 where &&3 = &4

Template_1で使われているSQL構文テンプレートの概念および表記法は、このホワ

イト・ペーパーのために考えられたものです。

この概念は、設計仕様書に記載する範囲に含まれます。設計仕様書には、

Template_1で使用されている表記法によって、特定の目的のために規定したSQL構文テンプレートを記載します。そして、実装者がこのホワイト・ペーパーで説

明されているプログラミング手法を使用することで、その設計を実装するコー

ル・サイトにおいて、規定したSQL構文テンプレートを使用したSQL文だけが発

行される仕組みを実現できます。

6. Code_2を実行しようとすると、常にORA-00933:SQL command not properly endedエラーとなります。

一方で、Code_1を実行しようとすると、ORA-00942:table or view does not existエラーが発生する場合

があります。ORA-00933 は構文エラーで、ORA-00942 はセマンティック・エラーです。現在のユー

ザーが表t(c1 varchar2(30)、c2 varchar2(30))にアクセスできる場合は、Code_1の解析はエラーな

く終わります。

7. このことは、適切に作成された PL/SQL 無名ブロックで、次の PL/SQL 文、 DBMS_Sql.Parse(Cur, 'select b1 from s where b2 = :1', DBMS_Sql.Native);

を実行して確認できます。

8. 通常の SQL 文と SQL 構文テンプレート間の違いを明確にするために、SQL 構文テンプレートは、

適度にスペースを挿入し、イタリック・フォントを使用して表します。

SQL インジェクション・プルーフ PL/SQL の記述方法

4

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 9: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

&構文デバイスは、値プレースホルダを示し、&&構文デバイス9は、単純SQL名プ

レースホルダを示します。SQL構文テンプレートの値プレースホルダは、通常の

SQL文の標準プレースホルダとは概念が異なります。SQL構文テンプレートの値

プレースホルダは、適切なSQLのリテラルまたはSQL文の標準プレースホルダの

いずれかを表します。

個々のSQL構文テンプレートは、特定のキーワード、特定の演算子、単純SQL名プレースホルダ、および値プレースホルダを組み合わせた特有の文字列です。単

純SQL名プレースホルダおよび値プレースホルダによって、特殊化したさまざま

なテンプレートが発生することが想定されます。そのため、SQL構文テンプレー

トの概念を拡大して、具体的な識別子、リテラル、および標準プレースホルダが

使用されている例も含めました。この点については、Template_2のSQL構文テンプ

レートに示してあります。各文は、Template_1で示されるもっとも一般的な形式

を特殊化したものです。 -- Template_2

select c1 from &&1 where c2 = &1

select &&1 from t where &&2 = 99

select c1 from &&1 where c2 = :1

select c1 from t where c2 = :1

特定のSQL文を特定のSQL構文テンプレートのインスタンスとして作成するとき

に自由にできるのは、値プレースホルダと単純SQL名プレースホルダにおけるテ

キストの置換10だけです。

• 値プレースホルダは、規定された構文ルール12に従い、標準プレースホルダ

11、

またはデータ型がテキスト、日付、数値である適切なSQLのリテラルで置き

換えることができます。

9. ここでの&の使用方法と、SQL*Plus スクリプト言語での&の使用方法を混同しないでください。た

だし、&を使用するという選択は、SQL*Plus での使用に配慮しておこないました。いずれの場合も、

&を使った書式は"実際に"処理がおこなわれる前にテキストの置換が発生することを表します。

10. "テキストの置換"と言っても、SQL 構文テンプレートは、Replace()などを使用したプログラムによ

る PL/SQL ソース・コードの値によるテキストの置換を表しているのではありません。そうではな

く、PL/SQL プログラムが実行時に使用する実際の SQL 文のサンプルを読んだユーザーが、テキス

トの置換によって実現される(設計仕様書に規定されている)SQL 構文テンプレートのインスタン

ス化であると確認できるということが重要です。 この置換の監査において、空白は重要ではありません。設計仕様書では、自由に SQL 構文テンプ

レートをレイアウトできます。プログラムでは、自由にさまざまなレイアウトを使用できます。両

方のスタイルの通常のコメントは、空白の特殊ケースにすぎません。ただし、設計仕様書では、"*/+"で始まり、"/*"で終わる特殊なコメントが規定されており、これは SQL ヒントを表します。このコ

メントは、プログラムがこの SQL 構文テンプレート用にインスタンス化する実際の SQL 文で忠実

に再現する必要があります。たとえば、キーワードの場合と同様、SQL ヒントは、インスタンス化

時の置換の対象にはなりません。

11. 値プレースホルダを標準プレースホルダまたは SQL のリテラルで自由に置き換えられるというの

は、形式的に興味深いだけです。これは、考えを明確にするのに役立ちます。実際のプログラムで、

実行時に値プレースホルダを標準プレースホルダで置き換えるように指定することはまずありま

せん。そのような設計は、心配の種になります。

12. これらのルールは、『SQL Language Reference』ブックに規定されています。

SQL インジェクション・プルーフ PL/SQL の記述方法

5

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 10: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

• 単純SQL名プレースホルダは、単純SQL名13でのみ置き換えることができます。

• そのほかの要素に置き換えることはできません。

とくに、このホワイト・ペーパーで使用している概念の定義では、修飾SQL名を

形成するために使用する文字"."および"@"は、SQL構文テンプレートの明示的な部

分である必要があります。そのため、修飾SQL名は単純SQL名プレースホルダ14を

置き換えるためには使用できません。

コンパイル時固定 SQL 文テキストと実行時作成 SQL 文テキストを 区別する

コンパイル時固定SQL文テキストという用語は、実行時に変更できないSQL文のテ

キストを意味し、ソース・コードを読み取ることで確実に確かめられるものとし

て定義しています。より正確に言うと、PL/SQLの静的varchar2 表現15であるSQL

文のテキストです。PL/SQLの静的varchar2 表現の値は実行時には変更できず、コ

ンパイル時に事前に計算されます。

埋込みSQLのSQL文テキストは、PL/SQLコンパイラによって作成され、実行時に

変更することはできません。したがって、埋込みSQLでは、コンパイル時固定SQL

文テキスト16のみが実行されます。

とはいえ、動的 SQL を実行するいかなる PL/SQL メソッドも、特定のコール・サ

イトにおいて、コンパイル時固定 SQL 文テキストだけを実行するように簡単に調

整できます。必要なのは、文のテキストを PL/SQL の静的 varchar2 表現として作

成することだけです。

実行時作成 SQL 文テキストという用語は、コンパイル時固定 SQL 文テキストでは

ない SQL 文のテキストという意味で定義しています。 これから説明しますが、コンパイル時固定SQL文テキストと実行時作成SQL文テ

キストを区別することが、SQLインジェクションの説明では重要になります。ま

た、SQLテキストの実行に使用するメソッドよりもSQLテキストのプロパティに

焦点を当てることがより重要になります。混乱を避けるために、実行メソッドは、

埋込みSQL17、ネイティブ動的SQL、およびDBMS_Sql API

18と呼びます。

埋込み SQL は、常にコンパイル時固定 SQL 文テキストを実行します。動的 SQLは、コンパイル時固定 SQL 文テキストまたは実行時作成 SQL 文テキストを実行

します。

13. 単純SQL名は、DBMS_Assert.Simple_Sql_Name()ファンクションが例外を起こすことなく返すものと

して定義するのがもっとも簡単です。例は、SCOTT(これは、Scottなどと同様に処理されます)と

“My Table”です。SCOTTはこのホワイト・ペーパーで一般SQL名と呼ぶ名前の例で、My Tableはエ

キゾチックSQL名の例です。これらの概念については、“例 2:ユーザー指定の表名”の項(14 ペー

ジ)を参照してください。 DBMS_Assert.Simple_Sql_Name()については、"単純SQL名の安全性を保証する"の項(25 ページ)を

参照してください。 14. このスクリプトのアプローチについては、"単純SQL名の安全性を保証する"の項(25 ページ)で説

明します。 15. PL/SQLの静的varchar2 表現という用語は、『PL/SQL Language Reference』ブックに定義されていま

す。また、このドキュメントで、PL/SQLの静的varchar2 定数は、constantキーワードを使用して宣

言され、PL/SQLの静的varchar2 表現を使用して初期化される変数として定義されています。定義

は再帰的であり、PL/SQLの静的varchar2 定数は、PL/SQLの静的varchar2 表現を構成するために使

用できます。この概念については、"静的テキスト"の項(33 ページ)を参照してください。 16. このテキストが、PL/SQL の静的 varchar2 表現(ここでは、ユニットのソース・テキスト)よりむ

しろ異なる部分(コンパイル済みの PL/SQL ユニットのテキスト)で特徴づけられているという事

実は、コンパイル時固定 SQL 文テキストの概念の確立にとってはあまり重要ではありません。

SQL インジェクション・プルーフ PL/SQL の記述方法

6

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 11: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

静的SQL構文テンプレートと動的SQL構文テンプレートを区別する

PL/SQL サブプログラムは、SQL 構文テンプレートがコンパイル時に凍結されて

いる SQL 文を実行することがあります。しかし、SQL 文を実行時に作成する必要

があることもあり、その場合に準拠する必要がある一連の SQL 構文テンプレート

が、設計仕様ドキュメントに明示的に記載するには多くなりすぎることがありま

す。この項では、この重要な相違点について説明します。

静的 SQL 構文テンプレートの定義

Code_5に示す定義者権限19のファンクションf()について考えてみましょう。

-- Code_5 function f(PK in t.PK%type, Wait_Time in pls_integer) return t.c1%type authid Definer is c1 t.c1%type; Stmt constant varchar2(32767) := 'select c1 from t where PK = :b for update wait ' || Wait_Time; begin execute immediate Stmt into c1 using PK; return c1; end f;

このファンクションの目的は、表tにおいて、主キー(PK)が特定の値の行に対す

る列c1 の値を返し、その行をロックして今後更新されないようにすることにあり

ます。このファンクションは、ほかのセッションによって該当行が現在ロックさ

れている場合に失敗したり、ロックされている場合にいつまでも待機したりする

べきではありません。したがって、コール元が 大待機時間を指定できるように

する必要があります。ただ実際には、SQL構文において、タイムアウト時間20を決

める値のプレースホルダの使用は認められていません。

17. 馴染みのある静的 SQL という用語ではなく埋込み SQL を使用したいと思います。後述しますが、

動的 SQL は静的テキストを実行するために使用できます。静的という用語は、過度に使用されて

います。

18. DBMS_Sql API以外のプロシージャAPIでは、制限されている種類のSQL文の完全な実行がサポー

トされています。また、SQLテキストの一部を受けとって無検査のSQLテキストを連結し、実行

時作成SQL文テキストを実行することもできます。それらのAPIについては、付録D:"動的SQLを実行する、そのほかのオラクル提供サブプログラム"(58 ページ)を参照してください。これらの

APIを安全に使用するためのルールは、ネイティブな動的SQLおよびDBMS_Sql APIを安全に使用す

るためのルールと同じです。そのため、このホワイト・ペーパーでは、これらのAPIに関してはこ

れ以上説明しません。

19. 実行者権限ユニットは安全で、定義者権限ユニットは危険であるという考えがあるように思えます。

これは認識の甘い考え方です。どちらが正しい選択であるかは、サブプログラムの目的によって異

なります。この例では、f()の目的は、表の所有者の権限を使用して特定の表から選択をおこなうこ

とにあります。f()の実行権限は、該当の表の所有者ではなくて、その表に直接的な権限を保持しな

いユーザーに付与される可能性が高く、その権限は表へのアクセスが制限されたものになります。

その一方で、実行者権限は、サブプログラムの目的が、サブプログラムを呼び出すユーザーの権限

を使用するパラメータ化された強力な処理を実行することである場合に適切です。そのようなサブ

プログラムが定義者権限で、Sys!などによって所有されている場合は、当然リスクが生じます。

SQL インジェクション・プルーフ PL/SQL の記述方法

7

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 12: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

手動の検査でも、f()はコンパイル時固定SQL文テキストを実行していないものの、

SQL構文テンプレートがコンパイル時に凍結されている(Template_321を参照)SQL

文を実行することは明らかです。 -- Template_3

select c1 from t where PK = :b for update wait &1

そのような SQL 構文テンプレートを静的 SQL 構文テンプレートと呼びます。

定義付けの結果として、コンパイル時固定 SQL 文テキストは常に、静的 SQL 構

文テンプレートに従うことになります。しかし、実行時作成 SQL 文テキストは、

静的 SQL 構文テンプレートに従うことも、従わないこともあります。

動的 SQL 構文テンプレートの定義

Code_6のプロシージャx.p()を考えてみましょう。 -- Code_6 package x is type cw is varray(20) of boolean; procedure p(PK in "My Table".PK%type, Wanted in cw); end x;

このプロシージャの目的は、エキゾチックSQL名がMy Table22である表において、

主キー(PK)が特定の値の行に対する、すべての列のうち任意のサブセットの値

をレポートすることにあります。x.p()をコールするコードの作成者は、My Tableの目的、そのすべての列の名前と意味を理解しています。コードの作成者は、と

くに、外部のドキュメントに記載されているそれらの列の順序を把握しています。

IN仮パラメータWantedのn番目の要素によって、My Tableのn番目の列がレポート

に含まれるかどうかが決まります。

たとえば、機能仕様書に従うと、レポート内の列の順序は表の外部ドキュメント

における順序と同じである必要があるとします。表に N 個の列がある場合、selectリストの組合せの数は、N から 1 つの項目を選択し、次に N から 2 つの項目を選

択する要領で N から N-1 の項目まで選択し、 後にすべての項目を選択するやり

方で得られる数の合計になります。

これは23、有名な 2

N-1 で表せます。3 列の場合は 7 つの異なるselectリストがあり、

10 列の場合は 1,023 の異なるselectリスト、20 列の場合は 100 万を超える異なる

selectリストがあります。数は、列の数が増えるに従い飛躍的24に増加します。

20. このコードは、指導を目的としたものです。これは、実際のアプリケーションでは使用するべきで

はない設計です。実行される各 select 文は、それまでのいずれの文ともテキストの内容が異なって

いる可能性が高いので、ハード・パースが急増することになります。妥協案として、コール元が、

たとえば、次の 4 つの値から待機時間を選択できるようにします。zero、short、long、および infinite。対応する仮パラメータによって選択肢を表現できます。そして、select 文は、4 つある待機時間ご

との PL/SQL 静的 varchar2 表現のいずれかを使用して作成できます。

21. これは、目的のタイムアウト値はpls_integerで表され、この型の値がvarchar2 に連結されると、

To_Char()の単一引数オーバーロードを使用してこのデータ型に自動的に変換されるという事実に

依存しています。この変換の結果、適切なSQL 数値リテラルになります。整数値なので、小数点

記号が使用されることはありません。2 番目の実引数(書式モデルを決定)は、環境には左右され

ません。デフォルトでは、小数点記号の使用が要求されますが、桁区切り記号(,)は要求されま

せん。3 番目の実引数は、小数点記号と桁区切り記号に使用する文字を決定します。これは、次の

文を使用して設定できるので、影響はありません。 alter session set NLS_Numeric_Characters therefore has no effect. これは、細かいですが重要なポイントです。IN仮パラメータWait_Timeのデータ型がnumberだった

場合は、StmtのSQL構文テンプレートは予測できないものとなっていたでしょう。この点について

は、"SQLの数値リテラルの安全性を保証する"の項(23 ページ)を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

8

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 13: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

プロシージャx.p()は、非常に一般的な要件の実装、つまり、カスタマイズ可能な

レポート25をモデリングします。また、基本となる表に 20 を超える列があること

は、珍しいことではありません。そのため、考えられるすべての文をコンパイル

時固定SQL文テキストとして提供することは不可能です。そのため、必要なSQL文は、プログラムで作成する必要があります。

パッケージxの本体p()の実装をCode_7に示します。Col_List()内部ファンクション

によってselectリストが作成されます。この設計では、column listはおそらく 良の

表現ではありません(Code_8を参照)。selectリストは、実際には、選択した列の

右側の空白を埋めた値を連結することで作成された単一の項目だからです。この

アプローチにより、実行時まで構成がわからないselectリストに対して複雑な

DBMS_Sql APIを使用する代わりに、単純なexecute immediateを使用することがで

きます。 -- Code_7 procedure p(PK in "My Table".PK%type, Wanted in cw) is function Col_List return varchar2; Stmt constant varchar2(32767) := 'select ' || Col_List() || ' Report from "My Table" where PK = :b'; Report varchar2(32767); function Col_List return varchar2 is ... end Col_List; begin execute immediate Stmt into Report using PK; DBMS_Output.Put_Line(Report); end p;

Stmt は、constant キーワードを使用して宣言されており、これは、宣言の一部とし

て初期化されることを意味しています。これは、必須ではありませんが、強く推

奨されるアプローチです。

Rule_1

SQL 文をプログラムで作成する必要がある場合、コードには通常、中間結果

の変数を使用する必要があります(少なくとも、使用することによるメリッ

トがあります)。変数を定数として宣言する目的で、宣言内で値を割り当て

ます。

SQL 文をプログラムで作成する場合、

中間結果に使用される変数を定数と

して宣言するとコードのレビューア

が理解しやすくなります。

割り当てるには、ネストされたブロック文または前方宣言ファンクションを

使用する必要がある場合があります。この手法を使用することで、コード・

レビューが楽になります。これは、変数の値が初期割当て時と使用時で変化

しないとわかっているからです。

22. エキゾチックSQL名My Tableは、このような名前もありえることを意識してもらうために使用して

います。この項で説明しているコードについては、示されているとおり、使用時に二重引用符で囲

むこと以外に説明することはありません。通常のPL/SQLソース・コード(仮パラメータPKの宣言

内)およびPL/SQLの静的varchar2 表現でのみ使用されます。この点については、"サンプル・フォー

ムによる問合せ"の項(40 ページ)を参照してください。

23. これを確認するにはインターネット検索をおこなう必要がありますが、これは読者の課題としてお

きます。

24. 飛躍的に増大するという表現は、多くの場合比喩的に使用されますが、ここでは文字どおり正確に

使用されています。

25. ほかの一般的な要件としては、任意の列のサブセットの比較基準(いわゆる、例示問合せパラダイ

ム)をユーザーが指定できるようにすることです。この点については、"動的SQL構文テンプレー

トを使用する必要性を動的テキストの必要性と混同しない"の項(32 ページ)および"サンプル・

フォームによる問合せ"の項(40 ページ)を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

9

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 14: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

万全を期すため、Col_List()ファンクションの実装をCode_8に示します。 -- Code_8 function Col_List return varchar2 is type cn is varray(20) of varchar2(30); Col_Names constant cn := cn('c1', 'c2', 'c3', 'c4', ..., 'c20'); Seen_One boolean := false; List varchar2(32767); begin for j in 1..Wanted.Count() loop if Wanted(j) then List := List || case Seen_One when true then '||' else '' end || 'Rpad('||Col_Names(j)||', 10)'; Seen_One := true; end if; end loop; return List; end Col_List;

Listの構成元である要素は、すべてPL/SQLの静的varchar2 表現26です。

型コンストラクタvarray(20)を使用しているので、My Table表には列が 20 あるとし

ます。x.p()で実行する可能性がある 100 万を超える異なるSQL文のセットは、そ

れぞれを点検するには数が多すぎます。そのため、プログラマー(および監査者)

は、Col_Listファンクションより戻される可能性がある値の自明の予測から、可能

なメンバーはどのようなものかを判断し、そこから考えられるSQL構文テンプ

レート27を推理する必要があります。この例では、20 の異なるSQL構文テンプレー

トがあります。Template_4に一部を示します。 -- Template_4

select Rpad(&&1, 10) Report from "My Table" where PK = :b

select Rpad(&&1, 10)||Rpad(&&2, 10)||Rpad(&&3, 10) Report from "My Table" where PK = :b

select Rpad(&&1, 10)||Rpad(&&2, 10)|| ...||Rpad(&&20, 10) Report from "My Table" where PK = :b

このような各 SQL 構文テンプレートを、動的 SQL 構文テンプレートと呼びます。

監査者は、テンプレートを構成する PL/SQL ソース・コードを少し調べることで、

確信をもって静的 SQL 構文テンプレートを記述できます。動的 SQL 構文テンプ

レートは、特定のコール・サイトでの実行のために作成されるそのような大量の

テンプレートのセットの 1 つです。こういったコール・サイトでは、すべてのテ

ンプレートを記述するにはそのセットは大きすぎますが、それにもかかわらず、

確実に表現できます。

26. PL/SQLの静的varchar2 表現という用語は、『PL/SQL Language Reference』ブックに定義されていま

す。この用語の意味については、"静的テキスト"の項(33 ページ)を参照してください。

27. 正規表現構文を使用して、ありうる SQL 文および SQL 構文テンプレートを記述することは可能 です。

SQL インジェクション・プルーフ PL/SQL の記述方法

10

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 15: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

Rule_2 SQL 構文テンプレートという用語が

意味する内容、および静的 SQL 構文

テンプレートと動的 SQL 構文テンプ

レート間の違いを理解します。

SQL 構文テンプレートが意味する内容を理解します。この理解を、実行時作成

SQL 文テキストを構成するコードの設計に応用します。静的 SQL 構文テンプ

レートと動的 SQL 構文テンプレートの違いを理解します。

SQL インジェクションの定義

SQL インジェクションは、PL/SQL サブプログラムにより引き起こされる可能性

があり、そのサブプログラムによって、特定のコール・サイトで、サブプログラ

ムの作成者がそのサイトに指定したテンプレートとは異なる SQL構文テンプレー

トで SQL 文が実行されることです。

この危険な結果は、作者がSQL構文テンプレート内の値プレースホルダまたは単

純SQL名プレースホルダを置き換える予定のテキストを攻撃者が提供する場合に

生じます。攻撃者のテキストは引用符構文を破壊し、SQL構文の断片として解析

されます。これが、"インジェクション"という用語が使用されている理由です。

つまり、攻撃者が提供する断片が、プログラマーが意図していた文に挿入されて

しまいます。攻撃者の目的が達成されるのは、結果として作成された通常と異な

るSQLが文法的に正しいために、検出されることなく実行されて、意図しない結

果を生成する場合です。次の項、"SQLインジェクションはどのようにして発生す

るのか"では、書き方が悪いコードが攻撃されやすいことを示す例をいくつか提供

します。

SQL インジェクションが、埋込み SQL を使用するサイトからは発生しないことは

明らかです。また、コンパイル時固定 SQL 文テキストを実行する動的 SQL を使

用するサイトからも発生しません。

挿入されたテキストは、サブプログラムの仮パラメータの 1 つから直接渡される

ことがあります(第 1 次攻撃)。または、作成する SQL 文のコンポーネントを取

得するためにサブプログラムが読み込み、信頼している表を介して間接的に取り

込まれることもあります。その表には、攻撃者が仕組んだローグ値が挿入されて

います(第 2 次攻撃)。

Rule_3 SQL インジェクションは、意図しない

SQL 構文テンプレートでの SQL 文の

実行であり、そのリスクは、実行時作

成 SQL 文テキストが動的 SQL を使用

して実行されるときにのみ生じるこ

とを理解します。

SQL インジェクションという用語の定義方法について、意図しない SQL 構文テ

ンプレートでの SQL 文の実行という点から理解します。その結果、実行時作成

SQL 文テキストを、動的 SQL を使用して実行する場合のみ、SQL インジェク

ションの攻撃を受けやすいことを理解します。

SQL インジェクション・プルーフ PL/SQL の記述方法

11

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 16: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

SQL インジェクションはどのようにして発生するのか

これは、例を示したほうがわかりやすいと思います。例を示すこと

で、SQLインジェクションの定義を厳格にすることもできます。この項で

は、プログラマーがコンパイル時固定SQL文テキスト28を使用しない理由について

は触れません。あとで説明しますが、実行時作成SQL文テキストが必要なユース

ケースは確かにあります。次に示すものは、適切ではありませんが、SQLインジェ

クションの手法を表しています。

例 1:ユーザー指定の列比較値

Code_9に示すPL/SQLの断片について考えてみましょう。 -- Code_9 ... q constant varchar2(1) := ''''; SQL_VC2_Literal constant varchar2(32767) := q||Raw_User_Input||q; begin Stmt := 'select c2 from t where c1 = '||SQL_VC2_Literal; execute immediate Stmt bulk collect into v; ...

Raw_User_Input には、列 t.c2 の予想される値であるとプログラマーが考える

PL/SQL のテキスト値が保持されているはずです。これは SQL インジェクション

の攻撃を受けやすいでしょうか。"いいえ"と答えたくなるかもしれません。なぜ

なら、SQL 構文テンプレートは、コンパイル時に次のように固定されているよう

に思えるからです。 -- Template_5

select c1 from t where c2 = &1

Raw_User_Input に次の値が設定されている場合、 -- Value_1 Smith

Stmt は次のようになります。 -- Value_2 select c1 from t where c2 = 'Smith'

すべて問題ないように思えます。しかし、Raw_User_Input に次の値が設定されて

いる場合、 -- Value_3 O'Brien

PL/SQL 変数 Stmt の値は、次のようになります。 -- Value_4 select c1 from t where c2 = 'O'Brien'

これは、PL/SQL のテキスト値としては平凡なものですが、SQL 文としては構文

的に正しくないので(一重引用符の数の不一致)、解析時に ORA-00933:SQL command not properly ended エラーとなります。

28. もちろん、Template_5の目的は埋込みSQLを使用して達成できます(これは、コンパイル時に生成

され、実行時にプレースホルダを含むコンパイル時固定SQL文テキストを使用します)。したがっ

て、Code_9は、"可能な場合は、コンパイル時固定SQL文テキストを使用する"の項(28 ページ)で

主張している原則に違反することになります。ただし、すでに確認したように、埋込SQLで問題な

い場合にネイティブ動的SQLが使用されているコードの例もあります。説明はときに、出来の悪い

プログラムの説明に過ぎない場合があります。また、場合によっては、不満をもつ悪意のある従業

員によってこういったプログラムが作成されることがあります。

SQL インジェクション・プルーフ PL/SQL の記述方法

12

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 17: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

プログラマーは、PL/SQLのテキスト値を始まりと終わりを一重引用符29で囲んで

SQLのテキスト・リテラルを作成するつもりでいました。

しかし、O'Brienがテキスト列に適した値であることを忘れたために、PL/SQLのテ

キスト値には、内部に一重引用符が合法的に含まれる可能性があります。SQLのテキスト・リテラルをPL/SQLのテキスト値から作成するためのルールでは、内部

にある一重引用符は 2 つ重ねてエスケープしてから、始まりと終わりを一重引用

符30で囲む必要があります。

プログラマーはこのことを忘れてしまったために、単独の内部一重引用符で SQLのテキスト・リテラルを閉じることになり、Brien''は 2 つの SQL トークン、“Brien”および“'”として解析されます。

これは平凡なバグに思えるかもしれません。また、当然回避されるべきものです。

ただし、このようなバグが存在する場合、永久に検出されない可能性があり、

Raw_User_Input に巧妙かつ悪意をもった値が仕掛けられると、結果はひどいもの

になります。

Raw_User_Input に次の値が設定されているとします。 -- Value_5 ' union select Username c1 from All_Users --

Stmt は次のようになります。 -- Value_6 select c1 from t where c2 = '' union select Username c1 from All_Users --'

プログラムによって追加される、終わりの一重引用符になるはずのものに注目し

ます。引用符全体の数が合っていない場合は構文エラーとなりますが、Value_5が単一行コメントの開始点となる--トークンで終わっているため、この一重引用符は

無効になります。

結果として、正常な SQL 文になります。Oracle Database では、空の文字列は NULLと同じであり、NULL との等式比較では、結果は常に NULL となり、t から選択さ

れる行はないため、SQL 文は次のように省略できます。 -- Value_7 select Username c1 from All_Users

このSQL文は、Template_5で示したSQL構文テンプレートとはまったく異なるイン

スタンスであり、プログラマーが意図したものではありません。したがって、Stmtを実行するサブプログラムは、SQLインジェクションの攻撃を受けやすいことが

わかりました。

29. ここでのポイントは、もちろん、プログラマーが PL/SQL プログラムのソース・コードを書いてい

る点であり、その目的は、実行時に、ほかのプログラム(SQL 文)のソース・テキストを作成して、

実行することにあります。

30. Oracle Database 10gでは、SQLおよびPL/SQL用に代わりの引用符構文、いわゆる、代替引用符メカ

ニズムが導入されています(ユーザー定義の引用符メカニズム、またはq-quote構文と呼ばれること

もあります)。この目的は、値自体に一重引用符が含まれている場合のユーザビリティを向上する

ことにあります。次にPL/SQLの例を示します。v varchar2(80) := q'{You can't do that}';始まりの“q'”と終わりの“'”は、変更できません。ユーザーは、内側の括弧(この例では、{および})を選択し

ます。このメカニズムは避ける必要があることを、"SQLのテキスト・リテラルの安全性を保証す

る"の項(19 ページ)で確認します。

SQL インジェクション・プルーフ PL/SQL の記述方法

13

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 18: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

これは、SQLインジェクションの標準的な例です。実行時作成SQL文テキストを

発行するプログラムは、すべてこのような攻撃を受けやすいという根拠のない不

安を感じている人がいます。ただし、次の漫画31で示されているように、そのよう

な攻撃は、単純な予防プラクティスをおこなうことで常に防ぐことができます。

この例における正しいプラクティスは、明らかです。PL/SQLのテキスト値がSQLのテキスト・リテラルに変換された場合、結果は始まりと終わりの一重引用符で

囲まれた文字列になる必要があります。引用符の間には、単独の一重引用符も、

奇数の数の一重引用符も指定できません。この詳細については、"SQLのリテラル

の安全性を保証する"の項(19 ページ)を参照してください。

例 2:ユーザー指定の表名

Code_10に示すPL/SQLの断片を考えてみましょう。 -- Code_10 ... Stmt := 'select c1 from '||Raw_User_Input||' where c2 = ''Smith'''; execute immediate Stmt bulk collect into v; ...

Raw_User_Input には、プログラマーが表またはビューの名前として期待する値が

保持されているはずです。これは SQL インジェクションの攻撃を受けやすいで

しょうか。例 1 と同様、"いいえ"と答えたくなるかもしれません。なぜなら、SQL構文テンプレートは、コンパイル時に次のように固定されているように思えるか

らです。 -- Template_6 select c1 from &&1 where c2 = 'Smith'

Raw_User_Input に次の値が設定されている場合、 -- Value_8 t

Stmt は次のようになります。 -- Value_9 select c1 from t where c2 = 'Smith'

31. この漫画のオリジナルは、http://xkcd.com/327/にあります。脚注には、"…これらの絵は自由にコピー

して共有することができます(ただし、販売はしないでください)"と書かれています。

SQL インジェクション・プルーフ PL/SQL の記述方法

14

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 19: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

やはり、すべて問題ないように思えます。しかし、こういった方法32を利用するプ

ログラマーはほとんどいないことを思い出してください。次のSQL*Plusスクリプ

トの実行はエラーなく終了します。 -- Code_11 drop table "a /" / create table "a /"("?" number, "a'b" number, " " number) / insert into "a /"("?", "a'b", " ") values (1, 2, 3) / select * from "a /" where "?" = 1 / select '['||Table_Name||']' x from User_Tables union select '['||Column_Name||']' x from User_Tab_Cols where Table_Name = 'a /' order by x /

SQL識別子は、データベース文字セット内の 1 文字以上の連続で構成できます(表

示に必要なのが 30 バイト未満の場合)33。Code_10の 後のselectでは、次の行を

含む出力が生成されます。 [ ] [?] [a /] [a'b]

カタログ・ビューで、どのように値がメタデータになっているかを確認してくだ

さい。また、これらのビューはいつ、どのように問い合わされるのかということ

や、ルールはそのほかの表やビューのものと変わらないということにも注目して

ください。とくに、識別子には単独の一重引用符が合法的に含まれている可能性

があるため、メタデータ問合せから識別子を表すために SQL のテキスト・リテラ

ルを作成する際には同様の注意を払う必要があります。

SQL では通常、すべての識別子を二重引用符で囲む必要があると考えるとわかり

やすいと思います。二重引用符は、識別子がアルファベット文字で始まっていて、

英数字、下線、#、または$だけが使用されている場合のみ(ユーザビリティを高

めるために)省略できます。二重引用符が省略されている場合、SQL パーサーは

識別子を大文字にします。

この違いを表すための専門用語は確立されていません。ここでは、一般 SQL 名と

いう用語を使用して、A..Z の範囲のアルファベットの大文字で始まり、A..Z の範

囲の大文字、下線、#、または$のみを使用する名前を示します。一般 SQL 名は、

SQL 文内で二重引用符で囲む必要はありません。また、囲まれていない場合、名

前が大文字であるか小文字であるかは関係ありません。しかし、二重引用符で囲

まれている場合もあります。囲まれている場合は、SQL パーサーによって文字の

大小が保持されるので、すべて大文字で書く必要があります。このホワイト・ペー

パーでは、エキゾチック SQL 名という用語を使用して、一般 SQL 名のルールに違

反する名前を示します。したがって、その名前は SQL 文内で二重引用符を使用し

て囲む必要があります。

32. DBA および開発者向けの GUI ツールは、ますます一般的なものとなってきています。それらのツー

ルを使用すれば、SQL を直接入力しなくてもオブジェクトを作成できます。そのため、 近では、

以前よりもエキゾチック SQL 名(明細項目など)が多く見られるようになっています。 33. 1 つだけ例外として、次の DDL 文があります。

create table "a""b"(n number) これは、ORA-03001:unimplemented feature エラーとなります。識別子には二重引用符を含めること

ができません。Oracle Database では、もうそのような識別子は認められていません。メッセージの

テキストは、今後の Oracle Database のバージョンでは、また認められる可能性があることを示唆し

ているように思えますが、これは誤りです。そのような予定はありません。

SQL インジェクション・プルーフ PL/SQL の記述方法

15

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 20: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

次に、Template_6の単純SQL名プレースホルダの値がtではなくa/だとした場合、

Raw_User_Inputの値は次のようになります。 -- Value_10 a /

Stmt は次のようになります。 -- Value_11 select c1 from a / where c2 = 'Smith'

これは構文的には誤りであり、解析時に ORA-00933:SQL command not properly ended エラーとなります。

プログラマーは、PL/SQLのテキスト値からSQL識別子を作成するつもりでしたが、

エキゾチックSQL名になる可能性があることを失念していました。PL/SQLのテキ

スト値からSQL識別子を作成するためのルールにおいて、この点を考慮する必要

があります。この詳細については、"単純SQL名の安全性を保証する"の項(25 ペー

ジ)を参照してください。

これも、やはり平凡なバグのように思えます。また、永久に検出されないことが

あります。(エキゾチック SQL 名を使用するケースは、ほとんどありません)。

繰り返しますが、Raw_User_Input に対して、巧妙かつ悪意をもって考えられた値

が与えられると、このバグによりひどい結果が生じます。

Raw_User_Input に次の値が設定されているとします。 -- Value_12 t where 1=2 union select Username c1 from All_Users --

Stmt は次のようになります。 -- Value_13 select c1 from t where 1=2 union select Username c1 from All_Users -- where c2 = 'Smith'

このコードで想定しているのは、一般 SQL 名だけです。しかし、コール元はエキ

ゾチック SQL 名を提供しています。これは、結果が合法的な SQL 文になるよう

に巧妙に設計されています。例 1 と同様、この SQL 文は次のように省略できます。 -- Value_14 select Username c1 from All_Users

このSQL文も、Template_6で示したSQL構文テンプレートとはまったく異なるインス

タンスであり、プログラマーが意図したものではありません。つまり、Stmtを実行

するサブプログラムはSQLインジェクションの攻撃を受けやすいといえます。

この例における正しいプラクティスも明らかです。SQL 識別子を作成するために

PL/SQLのテキスト値が使用される場合、変換時に一般SQL名とエキゾチックSQL名間の違いを認識し、大文字と小文字を適切に処理して、 終的な結果を二重引

用符で囲む必要があります。

SQL インジェクション・プルーフ PL/SQL の記述方法

16

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 21: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

反例 3:ユーザー指定の where 句

Code_12に示す次のPL/SQLの断片を考えてみましょう。 -- Code_12 ... Stmt := 'select c1 from t where '||Raw_User_Input; execute immediate Stmt bulk collect into v; ...

PL/SQL 識別子に適切な名前がつけられており、意図する SQL 構文テンプレート

が特定されていないと仮定します。そのため、形式的にはこれは、任意の SQL 文

を実行できる仕様の SQL*Plus と同様、SQL インジェクションの説明の範囲外に

なります。

プログラマーは、Stmt でインスタンス化できる一連の SQL 構文テンプレートにつ

いて、何らかの考え(たとえば、表 t の列の任意の組合せのそれぞれに対するリ

テラルとの等式比較)をもっていたかもしれません。しかし、文字列が適切な SQLのリテラルまたは SQL 識別子なのかを確認するのとは異なり、完結していると想

定される where 句が、意図した一連の SQL 構文テンプレートのインスタンス化の

1 つであることを確認するのは非常に難しいものです。

PL/SQLプログラムの機能仕様書が、Code_12で示唆されているような強力な柔軟

性を求めている場合は、拒絶するべきです。ただし、いずれにしてもデータベー

スに直接接続することができ、同じ表に対して任意のselect文を実行できるユー

ザーのみがこのPL/SQLプログラムを実行可能な場合は除きます。 この点については、"可能な場合は、実行時作成SQL文テキストには静的SQL構文

テンプレートを使用する"の項(30 ページ)を参照してください。

反例 4:意図が不明な SQL 構文テンプレート

Code_13に示す次のプロシージャを考えてみましょう。 -- Code_13 procedure Make_DBA(Raw_User_Input in varchar2) authid Definer -- Current_User is Double_Quote_Test constant char(3) := '%"%'; begin if Raw_User_Input like Double_Quote_Test then Raise_Application_Error(-20000, 'Interior " is illegal'); end if; declare Username constant varchar2(32767) := '"'||Raw_User_Input||'"'; Stmt constant varchar2(32767) := 'grant DBA to ' || Username || ' identified by x with Admin Option'; begin DBMS_Output.Put_Line(Stmt); execute immediate Stmt; end; end Make_DBA;

SQL インジェクション・プルーフ PL/SQL の記述方法

17

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 22: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

内部に二重引用符34があるRaw_User_Inputの値が入力されるとエラーが発

生し、内部に二重引用符をもたない値は二重引用符35で囲まれるため、SQL構文

テンプレートは常に以下のような形式になります。

-- Template_7 grant DBA to &&1 identified by x

結果として、SQLインジェクション36が発生する危険はありません。そのため、詳

細については、このホワイト・ペーパーでは取り上げません。

しかし、このSQL構文テンプレートの意図は、いくつかの重要な分析の必要性を

示すことにあります37。Make_DBA()は実行者権限のプロシージャであるため、管

理オプションが設定されたDBAロールをすでに保持しているユーザーのみ、エ

ラーなく実行できます。そのため、名目上は、このプロシージャに対する実行権

限をパブリックに付与しても安全なはずです。ただし、ささいなことですが、考

慮すべき点がもう 1 つあります。データベース内のあるサブプログラムの所有者

が管理オプション付きロールをもつ場合、そのサブプログラムがインジェクショ

ン攻撃を受けやすい条件下では、実行するSQLを注入できなくても、このプロシー

ジャ38を起動させることによるインジェクションが可能になります。Make_DBA()

のようなプロシージャが存在することで、セキュリティの監査は明らかに厳しく

なります。

34. 単純 SQL 名に二重引用符を含めることはできません。そのような名前を SQL 文に使用しようとす

ると、ORA-03001:unimplemented feature エラーとなります。

35. この例にある未完成のプログラミング方法は、適切なアプローチ("単純SQL名の安全性を保証する

"の項(25 ページ)を参照)に関する説明を先取りしないようにするために使用されています。適

切なアプローチは、DBMS_Assert.Simple_Sql_Name()を使用することです。

36. Raw_User_Input の分析のユーザビリティに疑問をもつ人もいると思います。名前が一般 SQL 名の

SCOTT であるユーザーを指定する場合は、すべて大文字で記述する必要があります。これは、SQLの決まりごとではありません。ただし、この例ではユーザビリティには注目していません。この例

は、SQL インジェクションの説明をわかりやすくするように設計されています。

37. コードを単純にするために、パスワードは明示的な識別子として指定しています。しかし、ユーザー

入力によって指定されたパスワード(二重引用符で囲まれているだけでなく、厳しいパスワードの

強度テストにパスする必要があります)でも、この例のポイントは変わりません。

38. これは、自律型トランザクションプラグマで定義されている実行者権限のファンクション内に組み

込む必要があります。

SQL インジェクション・プルーフ PL/SQL の記述方法

18

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 23: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

SQL のリテラルまたは単純 SQL 名の安全性を保証する

動的テキスト39で置き換えることができるSQL構文テンプレート内の要素は次の 2

種類だけです。1 つは値プレースホルダで、SQLのリテラルで置き換えることがで

き、もう 1 つは単純SQL名プレースホルダで、単純SQL名で置き換えることがで

きます。ただし、"例 1:ユーザー指定の列比較値"の項(12 ページ)および"例 2:ユーザー指定の表名"の項(14 ページ)で確認したように、まさに、この置換をお

こなうことでSQLインジェクションのリスクが生じます。したがって、この項で

は、そのようなリスクを防ぐためのアプローチについて説明します。

SQL のリテラルの安全性を保証する

SQLのリテラルには次の 3 種類があります。テキスト、日時、および数値40です。

各リテラルは個別に注目する必要があります。

SQL のテキスト・リテラルの安全性を保証する

SQLのテキスト・リテラルとして値、O’Brienが指定されている場合、これは、

Code_14 Code_15に示すデフォルトのメカニズム、または に示す代替引用符メカニ

ズム41を使用して記述できます。

-- Code_14 ...where Last_Name = 'O''Brien' -- Code_15 ...where Last_Name = q'{O'Brien}'

代替引用符メカニズムを使用すると、内部に単独の一重引用符がある値が読みや

すくなりますが、安全なSQLのテキスト・リテラル42を作成するために使用を禁止

する必要があります。

DBMS_Assertパッケージ43では、ファンクションEnquote_Literal()を公開しています。

このファンクションには、単一の仮パラメータStr(データ型はvarchar2、モード

はIN)があり、戻されるデータ型はvarchar2 です。入力が適切なSQLのテキスト・

リテラルの場合、出力は入力と同一となります。しかし、入力が適切なSQLのテ

キ ス ト ・ リ テ ラ ル で は な い 場 合 は 、 事 前 に 定 義 さ れ た 例 外 エ ラ ー

Standard.Value_Errorになります44。

以下に、PL/SQL のテキスト値から安全な SQL テキスト・リテラルを作成するた

めのルールを示します。

39. 動的テキストという用語の詳細については、"動的テキスト"の項(34 ページ)を参照してください。

ここでは、その直接的な意味を推測するだけで十分です。つまり、動的テキストとは、その構成を

PL/SQLの静的varchar2 表現までしかさかのぼることができないテキストのことです。

40. これらの項目の構文定義については、『SQL Language Reference』ブックを参照してください。

41. 代替引用符メカニズムのサポートは、Oracle Database 10g で導入されています。

42. 理由は簡単です。Oracle Database 11g の DBMS_Assert パッケージでは、この構文の安全性をアサー

トする機能が提供されていないためです。

43. DBMS_Assert パッケージは、Oracle Database 11g で初めて文書化されました。それ以来、次の各バー

ジョンにバックポートされています。8.1、9.2、10.1、および 10.2。これは、 新のパッチセット

で普通に利用可能な場合と、CPU で利用可能な場合があります。詳細については、Oracle Supportにお問い合わせください。

SQL インジェクション・プルーフ PL/SQL の記述方法

19

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 24: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

• PL/SQL テキスト値内にある各単独の一重引用符は、2 つの連続する一重引用

符で置き換える

• 値の先頭の前に 1 つの一重引用符を、値の 後の後に 1 つの一重引用符を連

結する

• 結果がDBMS_Assert.Enquote_Literal()で安全であることをアサートする45

3 番目のルールは、重要なルールです。これによって、インジェクションに対す

る安全性が保証されます。 初のルールでは、実行時の面倒なエラーを回避でき

ます。

SQL の日付リテラルの安全性を保証する

SQL の日付リテラルは、日付データ型の値への変換が可能であるという追加の条

件に準拠する必要がある SQL のテキスト・リテラルです。そのため、当然ですが、

SQL の日付リテラルの安全性は、SQL のテキスト・リテラルと完全に同じ方法で、

DBMS_Assert.Enquote_Literal()でアサートされる必要があります。

この単純なルールが忘れられた場合、または単一引数のTo_Char(d)(dは日付デー

タ型)と単一引数のTo_Date(t)(tはテキストデータ型)のドキュメント化された

動作が忘れられた場合に何が起こるかを確認するのは興味深いことです。この場

合のプロシージャについて考えてみましょう。 初の数行をCode_16に示します。 -- Code_16 procedure p is q constant varchar2(1) := ''''; d constant date := To_Date('2008-09-22 17:30:00', 'yyyy-mm-dd hh24:mi:ss'); Stmt constant varchar2(32767) := 'select t.PK from t where t.d > ' || q||d||q; ... begin execute immediate Stmt bulk collect into Results; ...

作成者は、示されているコードから、単一引数の To_Char()は、d が Stmt に連結さ

れるときに PL/SQL によって自動的に呼び出され、単一引数の To_Date()は、Stmtの実行時に自動的に呼び出されることを理解しているかもしれません。また、作

成者は、単一引数の To_Char()および単一引数の To_Date()の出力はいずれも

NLS_Date_Format パラメータの環境設定によって影響を受けることさえ記憶して

いるとも考えられます。そして、2 番目の変換は、 初の変換への対抗手段であ

るため、何も気にすることはないと判断しているかもしれません。

しかし、このような理解は単純すぎます。少なくとも、この作成者のプログラム

には一般的なバグがあるため、データの精度が失われ、問合せの結果が信頼でき

ないものになる可能性があります。ここで表tのデータは、Code_17に示すSQL*Plusスクリプトで移入されたものだとします。

44. 1 つ例外があり、入力の内部の一重引用符が正しくペアになっており、始まりも終わりも一重引用

符ではない場合、Enquote_Literal()によりこれらの引用符が追加されます。これは不適切な設計で

あり、このファンクションを純粋なアサータとして機能させることで、もっとよいものにできたは

ずだと感じる人(たとえば、Bryn Llewellyn)もいます。しかし、これは既に作成されたファンク

ションであり、下位互換性を考慮すると変更することはできません。このホワイト・ペーパーでは、

常に実行可能な、純粋なアサータとして使用するアプローチを推奨します。 45. このホワイト・ペーパー内のすべてのコード例では、このパッケージの名前について修飾子をつけ

ないパブリック・シノニムを使用するのではなく、常に Sys.DBMS_Assert のように所有者によって

修飾しています。この手段は安全のためには欠かせないものです。これにより、Oracle 提供の

DBMS_Assert パッケージを明示的に示し、PL/SQL ユニットと同一スキーマ内のプライベート・シ

ノニムまたはプライベート・パッケージに単純名が置き換わらないようにします。

SQL インジェクション・プルーフ PL/SQL の記述方法

20

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 25: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

-- Code_17 alter session set NLS_Date_Format = 'yyyy-mm-dd hh24:mi:ss' / begin insert into t(PK, d) values(1, '2008-09-23 17:30:00'); insert into t(PK, d) values(2, '2008-09-21 17:30:00'); commit; end; /

また、p()は、Code_18に示すSQL*Plusスクリプトを使用して実行されると仮定し

ます。 -- Code_18

alter session set NLS_Date_Format = 'dd-Mon-yy hh24:mi:ss' / begin p(); end; / alter session set NLS_Date_Format = 'yyyy' / begin p(); end; / p()の 初の実行では、表 t が移入されたときの設定とは異なる NLS_Date_Formatの設定で実行されますが、それでも予想どおりの結果が得られ、PK=1 の行だけが

選択されます。(これは、日付が格納されたときの精度を保持しているためです)。

ただし、2 番目の実行では、ほぼ確実に予想しない結果が得られ、PK=1 の行と

PK=2 の行の両方が選択されます。これは、PL/SQL 変数 d の値を詳細な指定なく

テキスト型に変換し、そのあとに再び日付データ型に変換したため、結果として

2008-01-01 00:00:00 のように表示されるためです。これは、もう意図した精度を

保持しているとはいえません。

"例 1:ユーザー指定の列比較値"の項(12 ページ)にある例に関する説明を理解

している人は、プロシージャp()は、一般的なバグがあるばかりでなく、SQLイン

ジェクションの攻撃を受けやすいと知っても驚かないでしょう。p()は、Code_19に示すSQL*Plusスクリプトを使用して実行されると仮定します。 -- Code_19 alter session set NLS_Date_Format = '"'' and Scott.Evil()=1--"' / begin p(); end; /

Value_15に示す値が、Stmtに割り当てられます。 -- Value_15 select t.PK from t where t.d > '' and Scott.Evil()=1--'

これは、意図したSQL構文テンプレートのインスタンスではありません。つまり、

SQLインジェクションが発生しています。これに関しては、通常のケースとは異

なる説明になります。単独の一重引用符が実行時に作成されるSQL文に挿入され

ていますが、この場合、直接的ではなく、NLSの環境パラメータを介して間接的46

に挿入されています。当然、すべての予想は外れることになります。

46. この攻撃の形式は、David Litchfieldによるラテラル(側面からの)SQLインジェクションと呼ばれ

ています。これに関する記事は、インターネット上で参照できます。アドレスは次のとおりです。 www.databasesecurity.com/dbsec/lateral-sql-injection.pdf

SQL インジェクション・プルーフ PL/SQL の記述方法

21

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 26: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

悪意のあるファンクションの起動が挿入され、p()が追加する終わりの一重引用符

が、一行のコメントの開始点となるトークンによって取り除かれます。そして、

Scott.Evil()が起動されます。

ここまでの説明はわかりにくく思えたかもしれませんが、結論は簡単に理解でき、

容易に実装できるものです。以下に、PL/SQL の日付値から安全な SQL の日付リ

テラルを作成するためのルールを示します。

• dateデータ型の入力に対し、To_Char(d、Fmt)の 2 パラメータ・オーバーロー

ドを使用して、SQLの日付リテラル47、tを作成します(これは、当然PL/SQL

varchar2 になります)。精度を得るために機能仕様書で要求されている値と

一致する値をFmtに使用する。

• この値の先頭の前に 1 つの一重引用符を、値の 後の後に 1 つの一重引用符

を連結する。

• 結果が DBMS_Assert.Enquote_Literal()で安全であることをアサートする。

• To_Date(t、Fmt)の 2 パラメータ・オーバーロードを使用し、Fmt には t を作成するために使用したのと同じ値を使用して、SQL 文の日付条件を作成する。

3 番目のルールは、重要なルールです。これによって、インジェクションに対す

る安全性が保証されます。また、 初の 2 つのルールと 4 番目のルールにより、

実行時の面倒なエラーを回避できます。

このアプローチを実装したプロシージャp_Safe()の 初の数行をCode_20に示しま

す。 -- Code_20 procedure p_Safe(d in date) is q constant varchar2(1) := ''''; -- Choose precision according to purpose. Fmt constant varchar2(32767) := 'J hh24:mi:ss'; Safe_Date_Literal constant varchar2(32767) := Sys.DBMS_Assert.Enquote_Literal(q||To_Char(d, Fmt)||q); Fmt_Literal constant varchar2(32767) := q||Fmt||q; Safe_Stmt constant varchar2(32767) := ' insert into t(d) values(To_Date(' || Safe_Date_Literal || ', ' || Fmt_Literal || '))'; begin execute immediate Safe_Stmt; ...

p_Safe()がSysdate()を使用して呼び出されると、Value_1648に示すような値が

Safe_Stmtに割り当てられます。 -- Value_16 insert into t(d) values(To_Date('2454723 18:01:05', 'J hh24:mi:ss'))

47. date は唯一の日付データ型ではありません。同じ論理がタイムスタンプ・リテラルなどにも当ては

まります。

48. ユリウス日 2454723 は、2008 年 9 月 13 日です。

SQL インジェクション・プルーフ PL/SQL の記述方法

22

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 27: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

Safe_Stmt の構成は、NLS_Date_Format パラメータに対する変更の効果に影響され

ません。

SQL の数値リテラルの安全性を保証する

SQLのテキスト・リテラルの特別な種類であるSQLの日付リテラルとは異なり、

SQLの数値リテラルには特別な構文があります。数値リテラルでは、常に小数点

記号としてドット(.)が使用され、桁区切り記号が含まれることはありません。

次にいくつか例を示します(一重引用符で囲む必要はありません)。49

42 -1 +6.34 0.5 -123.4567 25e-03 25f +6.34F 0.5d -1D

SQL 構文では、国の違いは認識できません。ただし、数値データ型に対する

To_Char()ファンクションのオーバーロードの出力では認識できます。このファン

クションには、1 つ、2 つ、および 3 つの仮パラメータがある 3 つのサブオーバー

ロードがあります。

3 つの仮パラメータがあるオーバーロードの場合、Fmt と呼ばれる 2 番目の仮パラ

メータで書式モデルを指定し、NLSparam と呼ばれる 3 番目の仮パラメータで概念

(小数点記号、桁区切り記号、通貨文字など)として使用される実際の文字を指定

します。

2 つの仮パラメータがあるオーバーロードの場合、2 番目の仮パラメータで書式モ

デルを指定します。とりわけ、このオーバーロードが使用される場合、NLSparam(のコンポーネント)の値は、NLS_Numeric_Characters、NLS_Currency、および

NLS_ISO_Currency の各環境パラメータによって決まります。さらに、1 つの仮パ

ラメータのオーバーロードが使用される場合、Fmt の値には固定のデフォルト値

が設定されます。この値は、環境で制御することはできません。しかし、NLSparamの値は、依然として環境によって決まり、引き続き影響をもちます。

Code_21に示すinsert文を使用して、表tにデータが移入されていると仮定します。

-- Code_21 insert into t(n) values (123456.789)

49. これらは、『SQL Language Reference』ブックから得た情報です。

SQL インジェクション・プルーフ PL/SQL の記述方法

23

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 28: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

Code_22にSQL*Plusのスクリプトを示します。 -- Code_22 select n from t / alter session set NLS_Numeric_Characters = ',.' / alter session set NLS_Currency = 'NOK ' / select To_Char(n, 'L999G999G999D999') n from t / select n from t / select To_Char(n, 'TM', 'NLS_Numeric_Characters = ''!.''') n from t / alter session set NLS_Numeric_Characters = '''.' / select 'c1 = '||n x from t /

このスクリプトから次の出力が得られます。 123456.789

NOK 123.456,789

123456,789

123456!789

c1 = 123456'789

SQL の数値リテラルは、慎重に作成しないと、構文エラーがある SQL 文を生成し

てしまう危険性があります。つまり、意図したものとは異なる SQL構文テンプレー

トが生成されます。単独の一重引用符を挿入する機能は、とくに注意が必要です。

これは、ラテラル SQLインジェクションのもう一つの例であり、教訓は明白です。

以下に、PL/SQL の数値から安全な PL/SQL の数値リテラルを作成するためのルー

ルを示します。

• 3 つの仮パラメータがあるTo_Char()オーバーロードによる明示的な変換を使

用する。このオーバーロードでは、Fmtの値を指定する必要があります。また、

1 つの仮パラメータのオーバーロードを使用するときのデフォルト値となる

値を明示的に指定します。これは'TM’です。50

• 1 つまたは 2 つの仮パラメータがあるオーバーロードを使用するときに

NLS_Numeric_Characters パラメータのデフォルト値となる値を明示的に指定

する。これは、'.,'です。

この点については、"安全な動的テキスト"の項(34 ページ)を参照してください。

このオーバーロード用の簡単なTo_Char(x f、n)が紹介されています。

50. 'TM'は、いわゆるテキスト 小数値書式モデルです。これは、出力が 64 文字を超えない場合は、

固定表記法で可能な文字の 小数を戻します。出力が 64 文字を超える場合、数は、モデルが'TMe’だったものとして戻されます。'Tme''は、科学表記法で可能な文字の 小数を戻します。機能仕様

書でこのことが推奨されている場合、'TMe'モデルを明示的に指定しても安全です。実際、そのモ

デルが、機能仕様書に記載されている精度を得るための要件を考慮して、NLSparam 引数の値とと

もに意図的に選択されている場合は、すべての書式モデルが安全です。

SQL インジェクション・プルーフ PL/SQL の記述方法

24

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 29: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

単純 SQL 名の安全性を保証する 51

エキゾチックSQL名 がO’Brienのユーザーが存在し、そのユーザーのスキーマに

一般SQL名がPROCのプロシージャと、一般SQL名がPKGのパッケージが存在する

とします。このパッケージには、エキゾチックSQL名がDo itのプロシージャがあ

るとします。さらに、このデータベースに対するリンクが異なるデータベースに

存在し、一般SQL名がLNKであるとします。以下に異なるコンテキストで、SQL文で使用することができる、さまざまな修飾SQL名の例をいくつか示します。

Proc "O'Brien".Pkg Pkg."Do it" Proc@"Lnk" "O'Brien".Pkg."Do it" "O'Brien".Pkg@lnk "O'Brien".Pkg."Do it"@LNK

次は、修飾 SQL 名の一般的な形式です。 a [ .b [ .c ]][ @dblink ]

項目 a、b、c、および DBlink は単純 SQL 名です。単純 SQL 名は、一般 SQL 名ま

たはエキゾチック SQL 名のいずれかであり、エキゾチック SQL 名は二重引用符

で囲む必要があります。これは、単に構文の規定にすぎません。a.b は、パッケー

ジ a 内の要素 b(たとえば、Proc."Do it")またはユーザーa が所有するオブジェク

ト b(たとえば、"O’Brien".Pkg)を示します。

単純SQL名と句読記号文字(.および@)を組み合わせて修飾SQL名を作成できる

機能により、SQL文のコンテキストの一般化を強化できます。ただし、このホワ

イト・ペーパーで説明してきたように、SQLを表現する権限は、SQL文を実行す

るサブプログラム52の作成者だけが 大限に利用できるようにする必要がありま

す。この論理は、修飾SQL名の作成にも同様に当てはまります。このような理由

から、句読記号文字(.および@)をSQL構文テンプレートの一部とし、単純SQL名プレースホルダが修飾SQL名で置き換えられないようにすることを主張します。

このルールについては、"安全なSQL文テキスト"の項(35 ページ)を参照してく

ださい。

使い慣れた修飾SQL名の構文を単一の実引数で表現することができれば、コール

元が処理するオブジェクトを選択できるサブプログラムのユーザビリティが向上

するという意見もあると思います。そのような要件に対応するための簡単な方法

があります。Oracle提供のプロシージャDBMS_Utility.Name_Tokenize()を使用すると、

修飾SQL名をその単純SQL名に分割できます。セマンティックは必要ありません。

つまり、示されたオブジェクトは存在する必要がありません。そのため、a.bがパッ

ケージaの要素bであっても、ユーザーaが所有するオブジェクトbであっても、そ

れらに関する情報はありません。Code_23は、その起動方法を示しています。

51. 一般SQL名とエキゾチックSQL名を作成するためのルールは、『SQL Language Reference』ブックに

記載されていますが、"例 2:ユーザー指定の表名"の項(14 ページ)で説明したとおり、これらの

専門用語はこのホワイト・ペーパーのために作成したものです。

52. 正確にいうと、"静的テキスト"の項(33 ページ)で定義しているトップ・レベルPL/SQLブロック

という用語をサブプログラムの代わりに使用する必要があります。

SQL インジェクション・プルーフ PL/SQL の記述方法

25

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 30: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

-- Code_23 procedure p(Qualified_SQL_Name in varchar2) is a varchar2(32767); b varchar2(32767); c varchar2(32767); DBlink varchar2(32767); Dummy pls_integer; begin DBMS_Utility.Name_Tokenize( Qualified_SQL_Name, a, b, c, DBlink, Dummy);

ユーティリティの 初の仮パラメータ(実際の Qualified_SQL_Name が使用されま

す)は、モード IN であり、残りの仮パラメータ(実際の a、b、c、DBlink、およ

び Dummy が使用されます)は、モード OUT です。a の戻り値が NULL になるこ

とはありません。b、c、および DBlink の値が NULL になる場合もありますが、bが NULL 以外の値の場合を除き、c が NULL 以外の値になることはありません。

Dummy には、有用な情報は含まれません。これは常に Length(Qualified_SQL_Name)と等しくなります。

コール元が処理するオブジェクトを選択できるサブプログラムのよりよい API 設計は、修飾 SQL 名を構成するために使用されるそれぞれの単純 SQL 名の明確な

仮パラメータを提供することです。ただし、アプローチの選択は、このホワイト・

ペーパーの内容においては重要ではありません。

SQL構文テンプレート内の単純SQL名プレースホルダの置換を単純SQL名との間

でのみ認めることで、プログラマーには、リモート・データベース内のオブジェク

トを参照するかどうかなど、設計の際に慎重な決定をおこなうことを求めていま

す。このようにすることで、明らかに設計の安全性が向上します。53

以下は、PL/SQL の数値から安全な単純 SQL 名を作成するためのルールです。

• SQL 文に使用されるのと同じ構文を使用するにより、コール元が、PL/SQL の

数値に仮の名前を提供できるように API を設計する。一般 SQL 名は、二重引

用符で囲まずに指定できます(その場合は、大文字と小文字の区別がないも

のとして処理されます)。エキゾチック SQL 名は、二重引用符で囲んで指定

する必要があります。 • DBMS_Assert.Simple_Sql_Name()を使用して名前の安全性を保証する。

DBMS_Assert.Simple_Sql_Name()は純粋なアサータです。つまり、入力と同じ内容

が出力されるか、DBMS_Assert.Invalid_Sql_Name例外が発生するかのどちらかにな

ります。入力が二重引用符で囲まれていない場合、一般SQL名であるとみなされ

ます。この場合、一般SQL名のルールに違反していると、例外エラーになります。

入力が二重引用符で囲まれている場合は、エキゾチックSQL名であるとみなされ

ます。この場合、その名前に単独の二重引用符または連続する奇数個の二重引用

符が含まれている場合のみ、例外エラーになります。ただし、正しく指定された

偶数個の二重引用符だけを含む名前は、使用するとエラーになります。54

53. DBMS_Assertのファンクションを不適切に使用しているプログラムがいかにインジェクションの攻

撃を受けやすいかについては、パブリック・インターネット・フォーラムで議論されています。例

として、ホワイト・ペーパー『Bypassing Oracle DBMS_Assert』(Alexander Kornbrust著)に、この

点に関する議論が記載されているので参照してください。このホワイト・ペーパーでは、オブジェ

クト識別の安全性は、常にDBMS_Assert.Simple_Sql_Name()でのみアサートされると主張しています。

当然、パッケージのその他のファンクションも、 終チェックの前に、通常のユーティリティの値

に使用できます。

SQL インジェクション・プルーフ PL/SQL の記述方法

26

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 31: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

後に、DBMS_Assert パッケージには、想定されるファースト・クラス・オブジェ

クトまたはスキーマが実際に存在するかどうかを確認するためのファンクション

があります。これらは、非常によく管理されたシナリオで使用するには便利なユー

ティリティかもしれませんが、SQL インジェクションのリスクを防ぐという点で

は何の価値もありません。Oracle Database は、マルチユーザー、マルチアクセス

環境であることで有名です。そのため、オブジェクトの存在チェックの結果によっ

てアクションが異なる状況において、そのアクションの実行時にはチェックした

結果がすでに正しくないことがあります。

54. この試みにより ORA-03001:unimplemented feature エラーとなります。

SQL インジェクション・プルーフ PL/SQL の記述方法

27

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 32: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

費用効率の高い、SQL インジェクションから保護するための

ルール ベスト・プラクティスのすべてのルールと同様、以下は目標を達成するための唯

一の方法を規定するものではありません。これらのルールを推奨するのは、記述

するのが簡単で、わかりやすく、手動のコード・レビュー時に監査しやすい55から

ですが、もっとも大事なのは、これらのルールを守ることによって、PL/SQLデー

タベース・コードがSQLインジェクションから保護されるからです。ルールによっ

て自由が制限されることがありますが、すべてのアプリケーションの合理的な要

件をサポートするのに十分な自由は残っています。

PL/SQL API を介してのみクライアントにデータベースを公開する

簡単にいうとパラダイムは、クライアントが接続できるデータベース・ユーザー

を、1 ユーザーのみにすることです56。このユーザーは、プライベート・シノニム

のみを所有できます。また、それらのシノニムはPL/SQLユニットのみを示すこと

ができます。必然的に、このルールを適用することで、そこで示されたPL/SQLユニットは、クライアントが接続できないユーザーによって所有されることになり

ます。クライアントが接続できるユーザーには、それらのPL/SQLユニットのみに

対して実行権限が付与されます。

このアプローチは、正式に API を定義します。つまり、パブリックに付与された

権限を使用してアクセス可能なオブジェクトは、API の一部であるという注意事

項の対象となります。(各ユーザーが適切な権限を保持している場合は、必要に

応じて、異なる種類のクライアント・エンドユーザーによるアクセスのために、

そのような複数のユーザーにスキームを拡張することができます)。

API で定義する PL/SQL ユニットは、それぞれの目的に従い、定義者権限または実

行者権限となります。 このパラダイムは、SQLインジェクションを防ぐための責任をそれが属す場所に

定めます。つまり、SQLを実行する全体的なアプリケーション・スタックのサブ

システム内に定めます。また、安全性を証明可能な唯一のアプローチを提供しま

す。このパラダイムは、データ整合性の強化に関与するすべてのコードをデータ

ベースに配置するという考えが自然に発展したものです。57

PL/SQL API を介してのみクライアン

トにデータベースを公開します。 Rule_4

PL/SQL API を介してのみクライアントにデータベースを公開します。クライア

ントがアプリケーションのそのほかの種類のオブジェクト(とくに表とビュー)

に直接アクセスできないように、権限を慎重に制御します。

可能な場合は、コンパイル時固定 SQL 文テキストを使用する

SQL構文テンプレートだけではなく、SQL文のテキスト全体がコンパイル時に固

定される場合、その文を発行するサイトがSQLインジェクションから保護されて

いることは容易に証明できます。つまり、文のテキストは固定されているので、

それがSQL構文テンプレートだということです。動的テキスト58が安全に作成され

ているかどうかについて論じる必要もなく、監査のタスクも取るに足りないもの

になります(つまり、相応に安価なものになります)。

55. これらにより、今後の Oracle Database のリリースでは、少なくとも部分的に機械的な監査ができる

ようになる可能性があります。 56. そのほかのすべてのユーザーのパスワードは厳重に保護され、クライアント側のコードを実装する

エンジニアには公開されません。 57. 副次的な利点として、このパラダイムにより、設計者はデータの整合性ロジックを実装するために

トリガーを使用する必要がなくなります。すべてのデータの変更を PL/SQL サブプログラムを介し

ておこなう場合、PL/SQL サブプログラムがデータの整合性ロジックを直接実装できます。

SQL インジェクション・プルーフ PL/SQL の記述方法

28

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 33: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

埋込みSQLを使用している場合は、コンパイル時固定SQL文テキストが保証され

ますが、ほかにも保証する方法はあります。Code_24に別の方法を示します。 -- Code_24 declare Stmt constant varchar2(80) := 'alter session set NLS_Date_Format = ''AD yyyy-mm-dd hh24:mi:ss'''; begin execute immediate Stmt; ... end;

ここでは、一部の日付値を一覧表示するレポートでは、特定の方法で表示するこ

とをアプリケーションの機能仕様書で求められていることが想像できます。59

PL/SQLの静的varchar2 表現60のみを使用する、代入文がある定数を使用すると、

コンパイル時にSQL文テキストをほぼ確実に固定できます。監査者は、Stmtの宣言

から、それをexecute immediateの引数として使用するまでのコードについて調べる

必要はありません。ユニットにStmtを変更するコードが含まれている場合、PL/SQLコンパイラはコンパイルをおこなわないからです。

そのため、特定のサイトで SQL を実行するための方法の議論と、そのサイトで実

行される SQL 構文テンプレートの議論はわける必要があります。埋込み SQL で

は、select、insert、update、delete、merge、lock table、commit、rollback、savepoint、set transaction といった種類の文のみサポートしています。

そのほかのすべての種類の文に対しては、動的SQLのためのPL/SQLのメソッドの

いずれかを使用する必要があります。また、それらのメソッドでは、execute

immediate文を使用することで、ほぼすべてのケースに対応できます。61

通常のアプリケーション・コードにおいて、埋込み SQL がサポートしない種類の

SQL 文を必要とするようなユースケースもありますが、それらが通常のアプリ

ケーション・コードにおいて発生することは、システム・コードの場合と比べる

と非常にまれです。

Rule_5 通常のアプリケーション・コードの設

計仕様書では、SQL を実行するため

に、埋込み SQL 以外の任意の方法を

使用するすべての提案を擁護してい

ます。

通常のアプリケーション・コードの設計仕様書に、埋込み SQL 以外のものを使

用することが提案されている場合、仕様書を精査し、論理的根拠について慎重

に検討することをお勧めします。設計次第で、優れたセキュリティを保持でき

ます。しかし、セキュリティのためのコードは明示的に記述する必要があります。

その逆もまた正しい選択です。つまり、埋込みSQLがサポートする種類の文を実

行し、文のテキストをコンパイル時に固定できる場合は、動的SQLは必要ありま

せん。したがって、このような場合には常に埋込みSQLを使用するべきです。こ

れで、なぜCode_9の例が、この項で説明したルールに違反しているのかを理解で

きたと思います。

58. 動的テキストの概念については、"動的テキスト"の項(34 ページ)を参照してください。

59. たとえば、日付を表すための表記規則がすでに確立しているスプレッドシートにインポートすると

いう特定の目的のために、マークアップ形式でレポートを生成する可能性が考えられます。

60. PL/SQLの静的varchar2 表現という用語は、『PL/SQL Language Reference』ブックに定義されていま

す。この用語の意味については、"静的テキスト"の項(33 ページ)を参照してください。

61. execute immediate で十分でないケースは非常にまれです。また、そのようなケースでは、セキュリ

ティに関して特別な配慮が必要です。たとえば、DDL 文をリモート・データベースで実行する必

要がある場合は、DBMS_Sql API が必要です。

SQL インジェクション・プルーフ PL/SQL の記述方法

29

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 34: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

ただし、実用的な配慮点から、このルールの記述を和らげる必要があります。特

定のサイトから発行される可能性がある多数のSQL構文テンプレートは、場合に

よってはコンパイル時に固定されますが、それらの数はあまりに多いので、(す

べてコンパイル時に固定されるコンポーネントで組み立てられますが、プログラ

ム的に生成されるため)コンパイル時に埋込みSQL文として設定することはでき

ません。このユースケースについては、"静的SQL構文テンプレートと動的SQL構文テンプレートを区別する"の項(7 ページ)、および"サンプル・フォームによる

問合せ"の項(40 ページ)を参照してください。

Rule_6 可能な場合は、コンパイル時固定 SQL文テキストを使用します。PL/SQL の

静的 varchar2 表現とともに埋込み

SQLまたは execute immediateを使用

します。

可能な場合は、コンパイル時固定 SQL 文テキストを使用します。埋込み SQLがサポートする種類の SQL 文の場合は、埋込み SQL を使用します。それ以外

は、PL/SQL の静的 varchar2 表現だけを使用して構成された、単一の PL/SQLの constant 引数とともに execute immediate を使用します。使用できないという

結論に達した場合は、機能仕様書と設計仕様書を同僚とともに慎重に再検討し、

コンパイル時固定 SQL 文テキストを使用できない理由を設計仕様書で具体的

に説明します。

可能な場合は、実行時作成 SQL 文テキストには静的 SQL 構文テンプ

レートを使用する

Rule_6の意図的な違反を正当化するために使用できるシナリオの要件はわずかし

かありません。それぞれの要件は、設計仕様書を精査し、慎重に検討する必要が

あります。

SQL 構文テンプレート内の値プレースホルダの置換

この要件により、異なる 2 つの根拠が生じます。

• 必要なパラメータ化のために標準プレースホルダにバインディングするため

の SQL 構文はありません。

• 適な実行計画を推奨するには、リテラル値が必要です62。

プレースホルダへのバインディングがサポートされない理由は 3 つあります。

• 埋込みSQLがサポートする種類のSQL文である場合でも、select... for update文でタイムアウト時間を決定するような値をパラメータ化するという要件があ

ります63。SQLは、この目的での標準プレースホルダの使用をサポートしてい

ません。

62. このユースケースは、以前はデータ・ウェアハウス内の情報を公開するためのアプリケーションの

実装で生じていました。このようなケースでは、表は巨大になり、問合せは複雑になります。また、

同時ユーザー数は比較的少なくなります。リテラルを使用するのではなく、プレースホルダにバイ

ンドして、いわゆる SQL ハード・パースの頻度を減らすという昔ながらの知識は使用されていま

せんでした。解析時間は実行時間と比較するとわずかなものであり、競合は重要ではありません。

条件でリテラルを使用すると、統計情報を取得するためのより具体的な利点が得られるため、許容

できる短い実行時間を実現するために、これは時として重要な方法でした。 ただし、Oracle Database 11g では、バインド・ピーキングが強化されています。現在では、 適な

実行計画を得るために、SQL 文にリテラル値をエンコードする必要はありません。また、リテラル

を優先すべき理由もありません。

SQL インジェクション・プルーフ PL/SQL の記述方法

30

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 35: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

• 動的SQLを必要とする種類のSQL文を実行し、それを何らかの方法でパラメー

タ化するという要件があります64。例として、この要件を拡張したものが

Code_24で示した実装になります。NLS_Date_Formatは、 初に検出された条

件に応じて実行時に設定する必要があると仮定します。

• SQL文(またはwhere句などの文の一部)は、付録D:"動的SQLを実行する、

そのほかのオラクル提供サブプログラム"(58 ページ)に一覧表示されている

サブプログラムのいずれかで実行され、そのサブプログラムはプレースホル

ダへのバインディングをサポートしていません。

値プレースホルダの置換に使用する動的テキストの安全性は、"SQLのリテラルの

安全性を保証する"の項(19 ページ)で確立した原則に従い、保証する必要があり

ます。この内容の概要については、Rule_10(35 ページ)を参照してください。

17 ページのCode_12に示してあるインジェクション可能な実装につながると思わ

れる機能仕様書について検討してみましょう。fromリストでは 1 項目だけを指定

するので、要件によって、ユーザーが項目の列のサブセットの一部を指定し、そ

れぞれに対して、比較するための値を提供することが求められている可能性があ

ります。また、条件の組合せが、均一に相互的なor(条件のいずれかを満たす)

または相互的なand(すべての条件を満たす)のいずれかに制約される可能性があ

ります。10 を超える列があると、異なるSQL構文テンプレートの数は数千になる

ため、ユーザーにwhere句を文字列として提供させる理由が容易にわかると思いま

す。しかし、そのような文字列が、実際に使用可能な数千のテンプレートの 1 つ

であるかをチェックするアルゴリズムを定義する一般的な方法はありません。許

容できるアプローチは 1 つだけあります。その詳細については、"動的SQL構文テ

ンプレートを使用する必要性を動的テキストの必要性と混同しない"の項(32 ペー

ジ)を参照してください。

Rule_7 SQL 構文テンプレート内の値プレー

スホルダの置換を提案する設計の正

当性を主張します。 (静的 SQL 構文テンプレートであろうと、動的 SQL 構文テンプレートであろう

と)SQL 構文テンプレート内の値プレースホルダの置換を提案している設計仕

様書は、疑って見る必要があります。また、このアプローチについては、納得

のいく議論をおこなってから、承認する必要があります。

SQL 構文テンプレート内の単純 SQL 名プレースホルダの置換

このユースケースの一般的な特徴は、アプリケーションをオブジェクト(一般的

には表)に対して実行する必要がある点です。オブジェクトの形状や目的は設計

仕様書に記載されていますが、その ID は実行時までわかりません。

このシナリオに対する安全なアプローチは、"単純SQL名の安全性を保証する"の項

(25 ページ)に規定されています。これは、新しい各設計仕様書を書く際に、コ

ンパイル時に固定されるSQLテキスト文だけを使用することにより、(表へのア

クセスを)埋込みSQLで実装できるような、代替のアプローチが可能かどうかを

判断するためにも役立ちます。

63. このユースケースを実装するためのコードは、Code_5(7 ページ)に示してあります。

64. 埋込み SQL がサポートしない種類の SQL 文では、標準プレースホルダの使用は認められません。 ただし、通常のアプリケーション・コードで、埋込み SQL がサポートしない種類の SQL を実行す

る必要があるケースはほとんどありません。このようなニーズは、特別な種類のプログラムでのみ

生じます。高度なレベルの機能仕様書は、慎重にレビューして、提案されているアプローチが必須

であることを確認する必要があります。

SQL インジェクション・プルーフ PL/SQL の記述方法

31

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 36: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

各スキーマが同じ名前と目的のオブジェクトをもつ一連のスキーマが存在し、そ

の中の 1 つにあるデータを操作することを目的としたユーティリティについて検

討してみましょう。自然なアプローチは、Unserenv 名前空間内の Current_Schemaに対する Sys_Context()ファンクションから返される値を、修飾 SQL 名にエンコー

ドすることにより、各オブジェクトにアクセスできるようにし、また、それらの

文を動的 SQL で実行する方法です。しかし、理想的なソリューションは、オブジェ

クトにアクセスする SQL 文を実行者権限ユニットにカプセル化することです。そ

うすることで、非修飾の名前を使用する埋込み SQL 使用して、問題なくアクセス

を実行できます。コードでは、プログラムによって、または単純に Current_Userに従って Current_Schema を変更することにより、目的のオブジェクトをポイント

できます。

場合によっては、アプリケーションで(PL/SQL コレクションで安全に保持できる

以上の)大量のデータを処理する必要がありますが、そのデータをセッションの

有効期間を超えて保持する必要はありません。従来、アプリケーションでは、名

目上の一時表のプールとともに管理システムを使用して、特定のセッションで使

用するためにそのような表を割り当て、その後回収していました。これは、表の

名前が実行時までわからないことを意味しています。Oracle8i Database では、グ

ローバルな一時表を導入しています。グローバルな一時表は、埋込み SQL を使用

してアクセスできますが、各セッションは別のセッションのデータにアクセスで

きないので、この用途には 適です。

ただし、SQL構文テンプレート内の単純SQL名プレースホルダを単純SQL名で置換

する必要があるユースケースもいくつか存在します。そのようなユースケースは、

大まかにシステム・ソフトウェアとして参照されるものの設計において生じる傾

向があります。具体的な例としては、ODCIフレームワークを使用するドメイン索

引の実装が挙げられます。異なる各索引は、一般的に、索引名から一連の表の名

前を取得したうえで、それらの表を使用します。ODCIクライアント・コードは、

Oracleシステムによってコールされると、対象となる現在の索引の名前から、その

ような表の名前を実行時に取得する必要があります。65

Rule_8 SQL 構文テンプレート内の単純 SQL名プレースホルダの置換を提案する

設計の正当性を主張します。 (静的 SQL 構文テンプレートであろうと、動的 SQL 構文テンプレートであろう

と)SQL 構文テンプレート内の単純 SQL 名プレースホルダの置換を提案してい

る設計仕様書は、疑って見る必要があります。また、このアプローチについて

は、納得のいく議論をおこなってから、承認する必要があります。

動的 SQL 構文テンプレートを使用する必要性を動的テキストの必要

性と混同しない

静的SQL構文テンプレートを使用して履行できない機能仕様書の種類については

すでに簡単に説明してあり66、安全な実装の概念についてはコードとともに示して

あります67。それらの要件は、実行時まで構成がわからないselectリストを処理す

ることでした。たいていの要件では、値と比較演算子(exact equals、like、less thanなど)の両方を提供することで、随意にリストと連動して、各列の制限基準を指

定することになります。また、多くの場合、使用する列、使用する順番、結果を

ソートするための各列の昇順または降順を指定する必要もあります。

65. Oracle Text はこの方法で機能します。

66. "動的SQL構文テンプレートの定義"の項(8 ページ)を参照してください。

67. 8 ページのCode_6、9 ページのCode_7、および 10 ページのCode_8を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

32

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 37: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

実行時まで構成がわからないwhere句を処理するための要件を考慮すると、経験の

浅いプログラマーは、コンパイル時まで要件がわからない場合にプレースホルダ

にバインディングするのは不可能だという結論に達することがあります68。経験豊

富なプログラマーであれば、DBMS_Sql APIは、まさにこれを実行するのに必要な

プリミティブを提供してくれることに気づきます。このアプローチは、"サンプ

ル・フォームによる問合せ"の項(40 ページ)に示してあります。

コーディングに必要な労力は、かなりのものです。しかし、労力が多いからといっ

て、SQL 文に直接リテラルをエンコードすることにして、バインディングをやめ

ることを正当化はできません。

つまり、SQL 文に直接リテラルをエンコードするための要件(このユースケース

では、実行計画の向上が唯一の理由)は、実行時に初めて構成が明らかになる一

連のプレースホルダへのバインディングを実装するための要件とはまったく異な

ります。

Rule_9 動的 SQL 構文テンプレートを使用す

る必要性をテンプレート内の値プ

レースホルダを置換する必要性と混

同しないようにします。 実行時に初めて構成が明らかになる一連のプレースホルダにバインドするため

の要件(これは DBMS_Sql API で完全にサポートされています)と、 適な問

合せ実行パフォーマンスを追及するために、直接エンコードされているリテラ

ルを使用するための要件を混同しないようにします。

安全性を保証するための正式で十分な規定

ここまでの説明は、SQLインジェクションの攻撃を受けやすい PL/SQLアプリケー

ションにいかに一般的なバグが含まれているかを示すためのものでした。そのた

め、SQL インジェクションに対して保護されるという利点がある、バグのないコー

ドを記述する方法に焦点を当てていました。この項では、既存のコードを検査す

ることで、SQL インジェクションに対する保護を保証することを目的とする監査

者の観点から説明します。

まず、いくつかの定義を確立する必要があります。

静的テキスト

静的テキストは、次のいずれかです。

• 『PL/SQL Language Reference』ブックに定義されているPL/SQLの静的varchar2

表現69、または

• 静的テキスト項目の任意の連結によって形成される表現、または

• 静的テキストとともに明白に割り当てられているローカル変数の値。70

この定義は、意図的に再帰的になっています。変数にはいくつかの静的テキスト

を割り当てることができ、その変数(厳密にはその値71)を使用して、別のより大

きい静的テキストを作成できます。タワーは必要に応じていくらでも高くできま

すが、その基盤はPL/SQLの静的varchar2 表現だけで構成されている必要がありま

す。

68. このことは、execute immediate を使用してこれをおこなうコードを書いてみるだけで理解できます。

using 句はコンパイル時に固定されるため、このタスクは実行できません。

SQL インジェクション・プルーフ PL/SQL の記述方法

33

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 38: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

連結は、実行時まで結果がわからないテストによって制御される場合があります。

しかし、ありうるすべての連結結果は前述の 3 つの箇条書きで記載してあるルー

ルに従った静的テキストのみであることは、人間がおこなう普通の検査を通じて

明らかにする必要があります。これが、明白に割り当てられているということの

意味です。(静的なテキストのタワーの上に立っているときに、基盤を確認でき

る必要があるという人もいるかもしれません)。 Code_25に、36 ページのCode_26に示したf()ファンクション内の宣言の抜粋を示し

ます。 -- Code_25 Tab_1 constant varchar2(32767) := 'Tab_1'; Tab_2 constant varchar2(32767) := 'Tab_2'; ... Tab constant varchar2(32767) := case b when true then Tab_1else Tab_2 end;

b は仮パラメータなので、Tab の値は実行時までわからず、通常は、f()が呼び出さ

れるたびに異なります。それにもかかわらず、Tab は前述の定義に従い静的テキ

ストとなります。

動的テキスト

動的テキストとは、静的テキストではないすべてのテキストです。わかりやすい

例としては、仮パラメータ、パッケージのトップ・レベルで宣言されている constantキーワードのない変数、および SQL 文を実行することで、または

Utl_File.Get_Line()に対する Buffer 仮パラメータの実引数として割り当てられる通

常の変数などがあります。

安全な動的テキスト

安全な動的テキストは、以下のいずれかの出力です。

• DBMS_Assert.Enquote_Literal()、または

• To_Char(x f, 'NLS_Numeric_Characters = ''.,''')、x は数値データ型の変数、f は明

示的な書式モデル'TM'、または

• DBMS_Assert.Simple_Sql_Name()。

69. 以下は、Oracle Database 11g エディションのブックに記載されているリストです。

— 単純なリテラル、'abcdef'など

— 単純なリテラルの連結、'abc'||'def'など

— null リテラル

— To_Char(x)、x は pls_integer の静的表現

— To_Char(x f, n)、x は pls_integer の静的表現

f と n は PL/SQL の静的 varchar2 表現 — x||y、x および y は PL/SQL の静的 varchar2 表現 または pls_integer の静的表現 このリストは拡張され、パッケージの仕様内で宣言されている constant で、PL/SQL の静的 varchar2表現で割り当てられているものもが含まれています。 pls_integer の静的表現も同じブック内で定義されています。 PL/SQL の静的 varchar2 表現の値はわかっているため、コンパイル時に固定されています。

70. ローカル変数は、現在のトップ・レベルの PL/SQL ブロック内で宣言されている変数です。トップ・

レベルの PL/SQL ブロックは、次のいずれかです。スキーマレベルのファンクション、スキーマレ

ベルのプロシージャ、パッケージ本体またはタイプ本体内のトップ・レベルで定義されているファ

ンクションまたはプロシージャ、パッケージの初期化ブロック、またはトリガーの実装。したがっ

て、パッケージまたはパッケージ本体のトップ・レベルで宣言された変数は、定義上はローカル変

数ではありません。

SQL インジェクション・プルーフ PL/SQL の記述方法

34

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 39: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

以下では、To_Char(x f, 'NLS_Numeric_Characters = ''.,''')を示すために、短縮形の

To_Char(x f, n)を使用します。このルールのバックグラウンドについては、"SQLの数値リテラルの安全性を保証する"の項(23 ページ)を参照してください。

安全な動的テキストの安全性は、現在のトップ・レベルの PL/SQL ブロック内で

確立する必要があるという動的テキストの定義に従っています。

安全な SQL 文テキスト

安全な SQL 文テキストは、静的テキストと安全な動的テキストを任意に連結した

ものです。(静的テキストの定義に使用しているのと同様の表現を使用した丁寧

な定義が必要となります。中間結果や 終結果にはローカル変数を使用できます。

また、可能なすべての連結の結果が、安全な SQL 文テキストになることが明らか

にである必要があります。)

これで、安全を保証するために必要な規定を定義できます。 動的 SQL は、静的テキストと安全な

動的テキストを連結したもののみ実

行できます。安全な動的テキストは、

オラクルが提供する次の 3つのファン

クションのうちの 1 つの出力です。

Simple_Sql_Name()、Enquote_Literal()、および To_Char(x f, n)。

Rule_10

PL/SQL のテキスト表現で表されている SQL 文が、動的 SQL のための PL/SQLの API のいずれかを使用して実行される場合、その表現は安全な SQL 文テキス

トである必要があります。安全な SQL 文テキストは、静的テキストと安全な動

的テキストを連結したものです。静的テキストは、PL/SQL の静的 varchar2 表

現でのみ構成されます。動的テキストは、静的テキストではないすべてのテキ

ス ト で す 。 安 全 な 動 的 テ キ ス ト は 、 オ ラ ク ル が 提 供 す る

DBMS_Assert.Simple_Sql_Name()、DBMS_Assert.Enquote_Literal()、To_Char(x f, n)といった 3 つのファンクションのうちの 1 つの出力です。

安全な SQL 文テキストの定義は、結果として生成される SQL 文のセマンティッ

クには注目していないので、十分なものではありません。

それにもかかわらず、このルールは有用です。それは、今後のOracle Databaseのバー

ジョンではPL/SQLコンパイラの機能が強化され、Rule_10の違反を検出できるであ

ろうことが容易に想像できるためです。ルールに違反しているアプリケーショ

ン・コードは、明らかに疑わしいので調べる必要があります。

また、作成されるSQL構文テンプレートが、プログラマーが意図したテンプレー

トのいずれかになることも保証する必要があります。安全性を保証するための十

分な規定を提供するためには、Rule_10にRule_11を追加する必要があります。

Rule_11 SQL 名の安全性を保証するには、

Simple_Sql_Name()を使用します。文

字列または日時リテラルの安全性を

保証するには、Enquote_Literal()を使

用します。数値リテラルの安全性を保

証するには、To_Char(x f, n)を使用し

ます。

DBMS_Assert.Enquote_Literal()または To_Char(x f, n)によって作成される安全な

動的テキストは、SQL のリテラルを対象とする SQL 文内のスポットでのみ使用

する必要があります。DBMS_Assert.Simple_Sql_Name()で作成される安全な動的

テキストは、単純な SQL 名を対象とする SQL 文内のスポットでのみ使用する

必要があります。

より厳しいRule_11への順守をいかに機械的に監視するかを考えるのは容易では

ありません。これには、各SQLを動的に実行するたびに、SQL構文の完全なナレッ

ジを実装済みのサブシステムによって、実行時にチェックする必要があります。

71. 変数とその(現在の)値との違いは、変数の宣言で constant キーワードを使用する場合には意味が

なくなります。ネスト化されたブロック文を適切に使用すれば、この方法で静的テキストを作成す

るために使用されるすべての変数の宣言が常に可能になります。このプラクティスにより、監査者

の仕事がとても楽になるので強く推奨します。

SQL インジェクション・プルーフ PL/SQL の記述方法

35

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 40: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

Rule_10いずれにしても、Oracle Database 11gでは、 とRule_11の両方を、人による

通常のコード・レビューで監視する必要があります。

Code_26に、Rule_10およびRule_11を守っている例72を示します。ファンクション

f()を使用すると、Template_8に示されているSQL構文テンプレートに準拠するSQL文を常に作成できます。 -- Template_8

select PK from &&1 where VC2 = &2

-- Code_26 function f(b in boolean, VC2 in varchar2) return number is Quote constant varchar2(1) := ''''; Doubled_Quote constant varchar2(2) := Quote||Quote; Start_ constant varchar2(32767) := 'select PK from '; Tab_1 constant varchar2(32767) := 'Tab_1'; Tab_2 constant varchar2(32767) := 'Tab_2'; Where_ constant varchar2(32767) := ' where VC2 = '; Tab constant varchar2(32767) := case b when true then Tab_1 else Tab_2 end; SQL_Text_Literal constant varchar2(32767) := Quote||Replace(VC2, Quote, Doubled_Quote)||Quote; Stmt constant varchar2(32767) := Start_|| Tab|| Where_|| Sys.DBMS_Assert.Enquote_Literal(SQL_Text_Literal); PK number; begin execute immediate Stmt into PK; return PK; end f;

bにtrue、VC2 にO’Brienの値が設定されている実引数でf()を呼び出すと、Value_17に示す安全なSQL文テキストが作成されます。 -- Value_17 select PK from Tab_1 where VC2 = 'O''Brien'

実行時作成 SQL 文テキストの安全性を、実行コードの直前で確立する

安全な動的テキスト73のために確立された定義では、安全な動的テキストは、動的

SQLを使用して連結されるSQL文を実行する、同じトップ・レベルのPL/SQLブロッ

ク内で作成する必要があると主張されています。これは、今後考えられる機械的

なチェックを可能にするためには十分な条件です。ただし、すべての

DBMS_Assert.Simple_Sql_Name()、DBMS_Assert.Enquote_Literal()、またはTo_Char(x f, n)がSQL文を構成するコード内でコールされ、直後にそのSQL文を実行する

PL/SQL文が記述されている場合、監査者の仕事は非常に楽になります。

72. この例では、SQL構文テンプレート内の値プレースホルダを置換するので、このアプローチは、"SQL

構文テンプレート内の値プレースホルダの置換"の項(30 ページ)で説明されている優れた実行計

画の可能性を増すため、標準プレースホルダにバインディングすることを優先するよう意図的に選

択されていると考える必要があります。

73. "安全な動的テキスト"の項(34 ページ)を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

36

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 41: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

Code_26は、この原則を守っています。

Rule_12 SQL 文を安全なものにする

Simple_Sql_Name()、Enquote_Literal()、またはTo_Char(x f, n)をコールするコードは、SQL 文を実

行するコードに近いものにします。

SQL 文を実行する PL/SQL 文の直前のコードで、実行時作成 SQL 文テキストの

安全性を確立することで、監査者の仕事を楽にします。

SQL インジェクション・プルーフ PL/SQL の記述方法

37

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 42: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

シナリオ 以下の各要件のシナリオは、埋込み SQL を使用することで確実に実装できます。

ただし、( 初のシナリオのケースでは)動的 SQL が危険な形で使用されている

実際の本番コードを確認できます。これは、作成者が埋込み SQL のソリューショ

ンをプログラムする方法を知らなかったためです。

先頭および末尾に%文字を追加して、like 条件を作成する

Code_27に、like条件を実装する埋込みSQL文を示します。 -- Code_27 select t.PK, t.c1 bulk collect into Results from t where c1 like '%'||x||'%';

上記のコードから、実行時作成 SQL 文テキストが必要であるという、おかしな作

り話の出所を推測するのは不可能です。

PL/SQLコンパイラがこのソース・コードから作成するSQL文を確認すると、役立

つかもしれません。これを監視するもっとも簡単な方法は、自分専用の開発デー

タベースで、Code_27に示されているコードをプロシージャにカプセル化し、それ

を実行して、関係のない結果を排除できる程度に選択的な制限を使用して、v$Sql

ビューに問合せをおこなう方法です74

Code_28にそのような問合せを示します。 。

-- Code_28 select Sql_Text from v$Sql where Lower(Sql_Text) not like '%v$sql%' and Lower(Sql_Text) like 'select%t.pk%t.c1%'

次は、Code_28の出力です。 SELECT T.PK, T.C1 FROM T WHERE C1 LIKE '%'||:B1 ||'%'

標準プレースホルダ:B1 が、埋込み SQL 文内で変数 x が使用されている位置に確

立されていることに注意してください。PL/SQL コンパイラは、バインディングを

おこなうための適切なコードを生成します。

ホワイト・ペーパーの『Doing SQL from PL/SQL:Best and Worst Practices』に詳細に

説明されているとおり、PL/SQLの埋込みSQLおよびPL/SQLの動的SQLは、いずれ

も実行時に同じ方法で処理されます。つまり、SQL文のテキストは、PL/SQLの実

行時システムにより、実行のためにSQLサブシステムに送信されます。違いは、

PL/SQLコンパイラは、コンパイル時に埋込みSQL文からSQL文のテキストを生成

して、ユニットのコンパイル済みコードとともに格納しますが、動的SQLで実行

されるSQL文は実行時に作成される点にあります。これはユニットを実行すると

きになります。いうまでもなく、このホワイト・ペーパーでは、この構成を、PL/SQLの静的varchar2 表現を設定したPL/SQL constantの初期化より難しいものにしては

ならないと主張してきています。75

埋込み SQL では、like 条件はサポートできないと考えていたプログラマーは、自

分の考えを説明するように求められた場合に、表現'%'||:B1||'%'は SQL 内では正し

くないと説明したでしょう。しかし、実際にはこの表現に間違いはありません。

74. これは、テスト表に通常とは異なる名前を設定することで簡単に準備できます。代替手段は、共有

プールをフラッシュしてからテスト・プロシージャを実行することです。

75. PL/SQL コンパイラを 適化することで、コンパイル時にこれが可能なことがわかっています。

SQL インジェクション・プルーフ PL/SQL の記述方法

38

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 43: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

実行時まで多数の要素がわからない IN リスト

このユースケースについては、ホワイト・ペーパーの『Doing SQL from PL/SQL:Best and Worst Practices』を参照してください。便宜上、ここではアカウントを再作成

してあり、このホワイト・ペーパーの内容に合わせるために多少表現を変えてあ

ります。

可能性の低い理由のために、where句で 5 つの項目があるINリストを使用する問合

せをおこなう必要がある場合、これを埋込みSQL文で記述し、あとでより可能性

が高そうなユースケースを反映するために要件を変更する場合に生じる課題に気

づかないことがあります。Code_29に、ありそうもない文を示します。

-- Code_29 select a.PK, a.v1 bulk collect into b.Results from t a where a.v1 in (b.p1, b.p2, b.p3, b.p4, b.p5);

Code_30は、より現実的な文の意図を表現しています。 -- Code_30 select a.PK, a.v1 bulk collect into b.Results from t a where a.v1 in (b.ps(1), b.ps(2), b.ps(3), b.ps(4), b.ps(5), b.ps(6), b.ps(7), b.ps(8), b.ps(9), ... );

問題はすぐにわかります。つまり、索引の値にリテラルを使用するコレクション

内の各要素に対する明示的な参照を記述するのは難しく、記述できたとしても、

テキストは管理できないほど大量になります。そのため、"いかに数が多くても、

このコレクション内のすべての要素"いう概念を表す構文が必要です。そのような

構文は存在しており、埋込みSQLでもサポートされています。Code_31に例を示し

ます。 -- Code_31 ps Strings_t; begin select a.PK, a.v1 bulk collect into b.Results from t a where a.v1 in (select Column_Value from table(b.ps));

ただし、このことはあまり知られていないように思えます。これはおそらく、table

演算子を使用しているためです76。psのデータ型は、スキーマレベルで宣言する必

要があります。Code_32にそれを作成するSQL*Plusスクリプトを示します。 -- Code_32 create type Strings_t is table of varchar2(30)/

table 演算子のことを知らないプログラマーが、実行時に SQL 文のテキストを作成

する方法で機能要件を満たすことは珍しいことではありません。この場合、この

実装は面倒で非効率的なものとなり、 悪の場合は SQL インジェクション攻撃を

受けやすくなります。

76. table 演算子の使用方法については、『Object-Relational Developer’s Guide』の"Manipulating Individual

Collection Elements with SQL"の項を参照してください。『PL/SQL Language Reference』では"PL/SQL Language Elements"の項にのみ table 演算子の記述がありますが、説明はありません。

SQL インジェクション・プルーフ PL/SQL の記述方法

39

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 44: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

サンプル・フォームによる問合せ

このシナリオは、"動的SQL構文テンプレートの定義"の項(8 ページ)で説明した

シナリオを補完するものです77。そのシナリオでは、機能仕様書で、固定された限

定条件(主キーID)によるカスタマイズ可能なレポート形式が求められていまし

た。この項では、機能仕様書で、レポート形式は固定でよいものの、限定条件は

カスタマイズ可能であるこを求められています。これにより、動的SQL構文テン

プレート内の値プレースホルダをSQLリテラルで置換するのを避ける場合は、プ

レースホルダにバインドする必要があり、プレースホルダの数、およびそれらの

プレースホルダを表すデータ型が実行時までわからない場合の設計が必要になり

ます。

構成が実行時までわからない select リストを処理するための要件、または構成が

実行時までわからない order by 句を処理するための要件のいずれでも、SQL イン

ジェクションのリスクを生じることに関する説明に影響を与えるような設計の相

違はありません。したがって、コードの説明を簡潔なものにするため、この項で

は、where 句がコンパイル時にわからない場合にコードを記述する方法だけを説明

します。

この項では、静的テキストだけを使用して、動的SQL構文テンプレートのインス

タンスとしてSQL文を作成する方法と、このことを示唆するバインディングの実

装方法についてのみ説明します。78

Code_33に示すプロシージャyx.p()について考えてみましょう。 -- Code_33 package x is type t1 is Record( Val varchar2(4000), Exact boolean := false); type t2 is varray(20) of t1; procedure p(Cols in t2); end x;

このプロシージャの目的は、カスタマイズ可能なwhere句を満たす行の主キーPKの値を一覧表示することです。8 ページのCode_6と同様、x.p()をコールするコー

ドの作成者は、My Table79の目的、およびこの表のすべての列の名前とその意味を

理解しています。これらのすべて、とくに名前は、設計仕様書で定められていま

す。作成者はとりわけ、各列が外部のドキュメントに記載されている順序をよく

理解しています。IN仮パラメータのColumnsのn番目の要素によって、My Tableのn番目の列がwhere句で使用されるかどうか、およびどのように使用されるかが決ま

ります。ValがNULLではない場合、n番目の列は含まれます。この場合、Exactで等式比較とlike比較のどちらを使用するかを指定します。

77. このシナリオを実装するコードは、8 ページのCode_6、9 ページのCode_7、および 10 ページのCode_8

に示してあります。

78. 実行時にorder by句を構成する必要性は、SQL文の構成にのみ影響を与え、バインディングにも、

結果をフェッチする方法にも影響を与えません。実行時にselectリストを構成する必要性は、バイ

ンディングには影響を与えませんが、結果をフェッチする方法には部分的に影響を与えます。この

アプローチは 9 ページのCode_7に示されています。ここでは、必要なすべての列を単一のテキスト

項目に連結することでこの影響を回避しています。しかし、場合によっては、各selectリスト項目

はそれぞれの変数内にフェッチする必要があり、列のデータ型も考慮する必要があります。

DBMS_Sql APIではこれをサポートできますが、その技術は、このホワイト・ペーパーのテーマに

は何の関係もありません。

79. エキゾチック名 My Table は、この例の指導目的で意図的に選択されています。

SQL インジェクション・プルーフ PL/SQL の記述方法

40

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 45: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

パッケージxの本体にp()を実装する例をCode_34に示します。Where_Clause()内部

ファンクションによってwhere句が作成されます。

-- Code_34 procedure p(Cols in t2) is type t3 is varray(200) of "My Table".PK%type; Results t3; type cn is varray(20) of varchar2(30); Col_Names constant cn := cn('c1', 'c2', 'c3', 'c4'); function Where_Clause return varchar2; Stmt constant varchar2(32767) := 'select PK from "My Table"' || Where_Clause() || ' order by PK'; function Where_Clause return varchar2 is ... end Where_Clause; begin DBMS_Output.Put_Line(Stmt); declare nc integer := DBMS_Sql.Open_Cursor(Security_Level=>2); rc Sys_Refcursor; Dummy number; begin DBMS_Sql.Parse(nc, Stmt, DBMS_Sql.Native); for j in 1..Cols.Count() loop if Cols(j).Val is not null then DBMS_Sql.Bind_Variable(nc, ':b'||j, Cols(j).Val); end if; end loop; Dummy := DBMS_Sql.Execute(nc); rc := DBMS_Sql.To_Refcursor(nc); fetch rc bulk collect into Results; for j in 1..Results.Count() loop DBMS_Output.Put_Line(Results(j)); end loop; close rc; end; end p;

Rule_1このホワイト・ペーパーの 9 ページにある の推奨事項に従い、Stmtはconstantキーワードを使用して宣言されています。これは、宣言の一部として初期化され

ることを意味します。また、Stmtへの割当の先頭は次のようになります。 Stmt constant varchar2(32767) := 'select PK from "My Table"' || ...

エキゾチックSQL名My Tableは、二重引用符で囲まれていますが、その安全性は

DBMS_Assert.Simple_Sql_Name()によっては保証されません。しかし、これは完全

に安全です。その理由は、PL/SQLの静的varchar2 表現内で発生するため、そのス

ペリングはPL/SQLソース・テキストによって確実に定められているからです80。

同じ論理は、Col_Names静的定数を初期化するリスト('c1'、'c2’、'c3'、'c4')にも

適用されます。列の名前は、設計仕様書に指定されています。また、x.p()は、My Tableとその列に反して作用するように指定されています。すべての列には一般SQL名

がつけられているので、各列を二重引用符で囲む必要はありません81。

80. Stmt の初期化で DBMS_Assert.Simple_Sql_Name()を使用しても問題はありません。ただし、これは明

らかに不必要なので、このファンクションを使用すると、あとでコードを保守する人が混乱する可

能性があります。

SQL インジェクション・プルーフ PL/SQL の記述方法

41

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 46: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

こ の 例 に お け る 、 課 題 の バ イ ン デ ィ ン グ に 関 す る 主 要 な 機 能 は 、

DBMS_Sql.Bind_Variable()です。これはプロシージャなので、実行時に出現するの

と同じ数だけループ内で呼び出すことが可能で、実行時にのみわかる実引数の値

が設定されます。バインドされる要素は PL/SQL ユニットのソース・コードに記

入されているため、execute immediate 文および open rc for 文は、コンパイル時に固

定されるバインディング要件のみサポートします。

設計では、selectリストはコンパイル時にわかるという事実を利用しています。設

計では、DBMS_Sql.To_Refcursor()82を使用して、DBMS_Sql APIのレジームからネ

イティブ動的SQLのレジームに移動して、単一のfetch... bulk collect文で結果を

フェッチします。 DBMS_Sql.Open_Cursor() のコールでは、仮パラメータ

Security_Level83に実際の値である 2 が使用されています。この指定により、この方

法で開かれたカーソルを使用するすべての操作は、そのカーソルを開いたユー

ザーと同じまたはより多くの有効なロールのセットをもつユーザーによっておこ

なわれる必要があります84。

万全を期すため、Where_Clause()ファンクションの実装をCode_35に示します。 -- Code_35 function Where_Clause return varchar2 is Clause varchar2(32767); Seen_One boolean := false; begin for j in 1..Cols.Count() loop if Cols(j).Val is not null then Clause := Clause || case Seen_One when true then ' and ' else ' where ' end || Col_Names(j) || case Cols(j).Exact when true then ' = :b'||j else ' like ''%''||:b'||j||'||''%''' end; Seen_One := true; end if; end loop; return Clause; end Where_Clause;

この設計は、10 ページのCode_8に示されているColumn_List()ファンクションの設

計とよく似ています。また、Clauseを構成している要素は、この設計でもすべて

PL/SQLの静的varchar2 表現です。

Column_List()ファンクションの場合と同様、x.p()で実行する可能性がある 100 万を

超える異なる SQL 文のセットは、それぞれを点検するには数が多すぎます。その

ため、プログラマー(および監査者)は、可能なメンバーについて論理的に考え、

81. リストを('“C1”'、'“C2”'、'“C3”' '“C4”')と記述しても、SQL インジェクションに対する

安全性の精度には何の影響も及ぼしません。(この記述には曲線の二重引用符を使用しています。

これは、ソース・コードで使用しなければならないフォントで使用される直線の二重引用符は、見

た目では 2 つの直線の一重引用符と区別できないためです。) 82. このファンクション、および対応する DBMS_Sql.To_Cursor_Number()は、Oracle Database 11g で導

入されています。 83. Security_Level 仮パラメータを備えた DBMS_Sql.Open_Cursor()の新しいオーバーロードは、Oracle

Database 11g で導入されています。 84. これにより、David Litchfieldがカーソル・インジェクションと名づけた別の種類の脆弱性が排除さ

れます。彼の記事は次のアドレスで入手できます。

www.databasesecurity.com/dbsec/cursor-injection.pdf

SQL インジェクション・プルーフ PL/SQL の記述方法

42

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 47: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

そこから、可能性のあるSQL構文テンプレートを推測する必要があります。この

例には、非常に多くの異なるSQL構文テンプレートがあります。Template_9に一部

を示します。 -- Template_9

select PK from "My Table" where c1 like '%'||:b1||'%' order by PK select PK from "My Table" where c1 = :b1 order by PK select PK from "My Table" where c1 like '%'||:b1||'%' and c3 = :b3 and c4 = :b4 order by PK

これらのテンプレートでは、値プレースホルダも単純 SQL 名プレースホルダも使

用しないため、動的テキストを使用する必要はなく、同様に、その安全性を保証

する必要もありません。このアプローチは、明らかに SQL インジェクションの攻

撃を受けにくいものです。間違ったメンテナンス・サイクルにおいて、表または

その列のいずれかの名前を変更し、それに応じて x.p()の実装を更新しなかった場

合は、実行時に通常のセマンティック・エラーで失敗します。

コールバック

コールバックの要件は、コンパイル済みのPL/SQLユニットが、再コンパイルする

ことなく、IDが実行時までわからないサブプログラムをコールするように何らか

の方法で指示されるということです。IDはわかっていなくても、仮パラメータの

署名、およびそれらの意味はわかっています85。このシナリオは、ISVアプリケー

ションでは比較的よくあるものです。ISVアプリケーションでは、顧客が指定した

方法で、一般的な特徴をもつ処理を実行するためのコードを顧客が提供できます。

ここでの課題は、コールされるサブプログラムの単純な形状を指定することで示

すことができます。 function Callback(Input in integer) return integer

Code_36に示されている"わかりやすい"設計では、動的SQLを使用しています。 -- Code_36 procedure p(Name in varchar2) is ... Safe_Name constant varchar2(32767) := Sys.DBMS_Assert.Simple_Sql_Name(Name); Stmt constant varchar2(32767) := 'begin :x := '||Safe_Name||'(:n); end;';begin execute immediate Stmt using out x, in n;

ここで、pは再コンパイルしてはならないユニットです。P.Nameの実際の値は、実

行時に、たとえば、スキーマレベル表から選択して取得されます86。

このアプローチは、このホワイト・ペーパーで規定しているルールに従っている

ため、SQL インジェクションから保護されます。ただし、あとで説明しますが、

よりよいアプローチがあります。

85. これは、表の ID は実行時までわからないが、各列の名前と目的はわかっているシナリオに似ています。

86. ISV の説明には、顧客は要求される形と任意の名前で、コールバック・ファンクションを作成する

必要があることが記載されています。この例を簡素化するために、これを指定したスキーマでおこ

なう必要があると仮定します。次に、顧客は指定した表に行を挿入して、コールバックに使用して

いる名前を指定する必要があります。

SQL インジェクション・プルーフ PL/SQL の記述方法

43

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 48: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

そのアプローチでは、(バーチャル・ディスパッチとしても知られる)動的ポリ

モフィズムを利用しています87。これは、オブジェクト指向プログラミングの基礎

の 1 つであり、サブタイピングとともに使用されます。コールされるサブプログ

ラムは、抽象データ型のnot finalメンバー・メソッドとして 初に記述されます。

Code_37にこの例を示します。 -- Code_37 type Hook authid Current_User is object( Dummy number, not instantiable member function Callback( self in Hook, Input in integer ) return integer ) not final not instantiable

そのほかの構文上の要素は、type の定義で必要となります。

• type には少なくとも 1 つのデータ属性があります。それは、実装において何

の意味ももたないことを強調するために"Dummy number"として宣言されます。

• 要素 not final を指定することで、type を Hook の下に作成できます。

• Callback()メソッドは、not instantiable として宣言されています。これは、type階層のこのレベルでは実装はおこなわれないためです。Hook は、実際の実装

を説明するサブタイプのスーパータイプとなります。Hook には not instantiableメソッドがあるため、Hook 自体も not instantiable として宣言する必要があり

ます。

• type の各メンバー・メソッドには、潜在的な 初の仮パラメータ self がありま

す。これは、デフォルトでは IN OUT モードです。代わりに IN として宣言す

ることもできますが、OUT としては宣言できません。IN OUT がデフォルトな

のは、一般的に、メンバー・メソッドはデータ属性を設定するために使用さ

れるからです。IN OUT 仮パラメータは、nocopy ヒントを使用している場合を

除き、値で渡されます。属性 Dummy が参照されることがない、この特殊なユー

スケースの場合、ベスト・プラクティスは、self を明示的に IN として設定す

ることです。モードが IN の仮パラメータは、参照で渡されます。

• 修飾子 authid current_userは、実行者権限でスーパータイプを確立するために、

可能性のあるベスト・プラクティスに従い使用されます。実行者権限か定義

者権限かの選択は、実際のプロジェクトでは、全体的な要件を考慮しておこ

なわれます。

87. この項で抜粋したコードの一覧については、付録E:"動的ポリモフィズムを使用するコールバック

の実装を表すための自己完結型のコード"(61 ページ)を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

44

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 49: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

前述したように、コールされるサブプログラムの実装は、Hook のサブタイプで表

現されます。 -- Code_38 type My_Implementation under Hook( overriding member function Callback( ... input in integer) return integer ) type body My_Implementation is overriding member function Callback( ... input in integer) return integer is begin ... return ...; end Callback; end;

要素 overriding member は、スーパータイプ内の仮想宣言に対する実際の実装とし

て、メソッドを確立します。

前述の構成の素晴らしい点は、スーパータイプHookの変数(たとえば、Obj)は、

サブタイプMy_Implementationのインスタンスである値を実行時に与えることがで

き、明らかにスーパータイプの仮想メソッドであるObj.Callback()の起動が、サブ

タイプで宣言されている一致内容をオーバーライドすることを意味する点です。

これにより、動的ケース文と呼ぶ文を使用できます。この文のレッグは文をコン

パイル後に決定でき、しかも、文を再コンパイルすることなく、新しいコンパイ

Code_39ル・ユニットを作成することで決定できます。これはまるで、 のp()で新

しいレッグを取得して、p()を再コンパイルすることなく、新しいコンパイル・ユ

ニットにCallback3()を確立するだけで、そのcase表現内でCallback3()を起動するよ

うなものです。 -- Code_39 function Callback1(input in integer) return integer is begin return input*2; end Callback1; function Callback2(input in integer) return integer is begin return input*2; end Callback2; procedure P(Which pls_integer) is ... begin x := case Which when 1 then Callback1(n) when 2 then Callback2(n) ... end; ...

Code_40 Code_36に、動的ポリモフィズムを使用して、 の目的を実装する方法を示

します。 -- Code_40 procedure p(Obj in Hook) is ... begin ... x := Obj.Callback(n); ...

SQL インジェクション・プルーフ PL/SQL の記述方法

45

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 50: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

p.Obj の実際の値は、実行時に、たとえば、スキーマレベル表から選択して取得さ

れます。今回は、varchar2 列を使用して実装の名前を保持するのではなく、列に

はデータ型 Hook があり、サブタイプ My_Implementation のインスタンスとともに

移入されます。

Code_36に示されている動的SQLのアプローチでは、静的SQL構文テンプレートを

使用して、単純SQL名プレースホルダを単純SQL名で安全に置き換えています。

このコードが安全であることを証明するには、監査者にある程度の努力が求めら

れます。Code_40に示されている動的ポリモフィズムを使用するアプローチでは、

SQLは一切使用していないので、安全なのは明らかです。

さらに、動的ポリモフィズムを使用するこのアプローチには次の 2 つの利点があ

ります。

• テストでは、代表的な例として、コールバック本体の実行が、PL/SQL から

SQL へ、さらに PL/SQL コンテキストへの切り替えのコストと比べると相対

的に速い場合、動的ポリモフィズムのアプローチは、動的 SQL のアプローチ

よりも約 10 倍速いことが実証されています。

• 動的ポリモフィズムのアプローチを使用する場合、Callback()の仮パラメータ

には任意の PL/SQL データ型を設定できますが、動的 SQL のアプローチを使

用する場合、それらのデータ型は SQL が認識できるデータ型のみに限定され

ます。

SQL インジェクション・プルーフ PL/SQL の記述方法

46

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 51: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

既存コードの分析と強化

Oracle Database 11g では、この作業の大半は手動でおこないます。動的 SQL を実

行するそのようなトップ・レベルの PL/SQL ブロックを検索するには、非定型メ

ソッドを使用する必要があります。

プロシージャAPIが使用されているすべてのサイトを検索するのは簡単な作業で

す88。PL/Scope

89を使用すると、見落としや誤認をすることなく、各サイトを特定

できます。ただし、ネイティブな動的SQLを発行するサイトの検索に対応するメ

ソッドはありません90。ソース・コードを手動で検索する以外に方法はありません。

そのようなサイトのPL/SQLユニットには、executeとimmediateの両方の単語、また

はopenとforの両方の単語が含まれます。そのため、User_Sourceの機械的な検索は、

関係するユーザーの観点から見ると、一部のユニットを排除してしまうように思

えます。

関係のあるユニットは、一度特定されると、関連する各トップ・レベルの PL/SQLブロックのために保持されるので、このホワイト・ペーパーで主張しているルー

ルに従っているかどうかを判断するために、ソース・コードを手動で調べること

ができます。

ルール違反が発見される場合、その違反は、このホワイト・ペーパーで説明して

いる原則に従って作成されていない、安全ではない動的テキストで構成される実

行時に作成されるSQL文を使用して、SQL文が実行される場所にあります91。

運がよければ、監査者は、静的 SQL 構文テンプレートの使用が想定されていた場

所に、動的テキストが安全ではない形で使用されていることを簡単に確認できま

す。この場合、実行時作成 SQL 文テキストを実行する PL/SQL 文の直前に、

DBMS_Assert.Simple_Sql_Name()、DBMS_Assert.Enquote_Literal()、または To_Char(x f, n)を追加的にコールすることで、脆弱性を排除できます。

SQL キーワードと演算子が動的テキストとして提供されるという前提に基づき設

計されているような運の悪いケースでは、設計の根本的な見直ししかソリュー

ションはありません。しかし、少なくとも脆弱性は特定されます。

88. 関連するサブプログラムは、付録D:"動的SQLを実行する、そのほかのオラクル提供サブプログラ

ム"(58 ページ)に一覧表示されています。

89. PL/Scope は Oracle Database 11g で導入されています。PL/Scope の詳細については、『Oracle Database Advanced Application Developer’s Guide』の"Using PL/Scope"の章を参照してください。

90. 強化要件 6913337 は、これをおこなう方法を提供するように求めています。

91. とくに、"SQLのリテラルまたは単純SQL名の安全性を保証する"の項(19 ページ)および"安全性を

保証するための正式で十分な規定"の項(33 ページ)を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

47

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 52: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

結論 92

このホワイト・ペーパーでは、いくつかの概念 を慎重に定義し、名前をつけまし

た。以下の概念はSQLインジェクションおよびSQLインジェクションを回避する

ための適切な議論をサポートするのに欠かせないものです93。

• 一般 SQL 名とエキゾチック SQL 名

• コンパイル時固定 SQL 文テキストと実行時作成 SQL 文テキスト

• 値プレースホルダ、単純 SQL 名プレースホルダ

• SQL 構文テンプレート、静的 SQL 構文テンプレート、動的 SQL 構文テンプレー

• 静的テキスト、動的テキスト、安全な動的テキスト、安全な SQL 文テキスト

• トップ・レベルの PL/SQL ブロック

また、これらの概念は、以下の確立された概念および用語を注意して使用するこ

とで成立します。

• 標準プレースホルダ、単純 SQL 名、修飾 SQL 名

• 埋込み SQL、ネイティブ動的 SQL、DBMS_Sql API

• PL/SQL 数値、PL/SQL 日時値、PL/SQL テキスト値

• SQL 数値リテラル、SQL 日時リテラル、SQL テキスト・リテラル

• PL/SQL の静的 varchar2 表現と PL/SQL の静的 varchar2 定数

これらの概念および用語により、このホワイト・ペーパーの主題の定義を、主張

対象の観点から単純かつ簡潔に表すことができます。SQL インジェクションは、

サブプログラムの作成者が意図したものとは異なるSQL構文テンプレートでSQL文が実行されるときに、PL/SQL サブプログラムの特定のコール・サイトで発生し

ます。また、SQL インジェクションは、実行時作成 SQL 文テキストが実行される

場合のみ発生する可能性があることも理解できます。

このことは、PL/SQL コードが SQL インジェクションの攻撃を避けるもっとも費

用対効果の高い方法を理解するための基礎となります。

• 埋込み SQL の execute immediate、または PL/SQL 静的 varchar2 表現の open forのいずれかを適切に使用するコンパイル時固定 SQL 文テキストのみを実行す

ることを目指す。

• 設計仕様書で、コンパイル時固定 SQL 文テキストが機能仕様書の要件を満た

すのに十分でないことを入念に説明した場合のみ、全体が静的テキストであ

る実行時作成 SQL 文テキストの実行に切り替える。

• コンパイル時固定 SQL 文テキストも、全体が静的テキストである実行時作成

SQL 文テキストも十分ではない理由を入念に説明した場合のみ、安全な SQL文テキストである実行時作成 SQL 文テキストを使用する。

• 安全な SQL 文テキストを作成する場合

92. 付録B:"このホワイト・ペーパーで紹介されている新しい専門用語の定義"(51 ページ)を参照し

てください。

93. この説明は、これまでおこなわれたことはないように思います。

SQL インジェクション・プルーフ PL/SQL の記述方法

48

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 53: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

− SQL 構 文 テ ン プ レ ー ト 内 の 値 プ レ ー ス ホ ル ダ を

DBMS_Assert.Enquote_Literal()または To_Char(x f, n)で置き換える動的

テキストの安全性を保証します。この設計の場合、標準プレースホル

ダへのバインディングが十分ではない理由をはっきりと説明します。

SQL テキスト・リテラルまたは SQL 日時リテラルには前者を使用し

ます。また、SQL 日時リテラルを作成する場合は、To_Char()とともに

必要とされる精度に適した書式モデルを使用し、同じモデルで

To_Date()を安全な SQL 文テキストにエンコードします。SQL 数値リテ

ラルには To_Char(x f, n)を使用します。

− SQL 構文テンプレート内の単純 SQL 名プレースホルダを

DBMS_Assert.Simple_Sql_Name()で置き換える動的テキストの安全性

を保証します。

その結果、明らかに、SQL インジェクションに対して安全な設計を実装する費用

は、より高度なアプローチが続くにつれて増加していきます。しかし、費用がか

かること理由に、実装を簡素化することを正当化してはなりません。このホワイ

ト・ペーパーで設定している原則を守ることでのみ、SQL インジェクションの脅

威を避けることができるのです。

Mark Fallon、[email protected]

Bryn Llewellyn、[email protected]

Howard Smith、[email protected]

Oracle Headquarters

2008 年 12 月 4 日

SQL インジェクション・プルーフ PL/SQL の記述方法

49

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 54: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

付録 A: 変更履歴

2008 年 9 月 19 日

• 初の公開バージョン。

2008 年 9 月 21 日

付録D:"動的SQLを実行する、そのほかのオラクル提供サブプログラム"の項

(58 ページ)

• の追加。同僚からのレビューに基づくいくつかの軽微な修正。

2008 年 10 月 26 日

インターネットにおける"SQL インジェクション"の検索時に、このホワイト・ペー

パーがヒットする確率を高めるためのタイトル変更。『インジェクション・プルー

フ PL/SQL の記述方法』から『SQL インジェクション・プルーフ PL/SQL の記述方

法』。

Google でのすべての一致文字列およびその変化形を、インターネット検索のため

のブランドフリーな参照に置換。

いくつかの軽微なタイプミスの修正。

2008 年 12 月 4 日

意味をより明確にするための"SQLインジェクションの定義"の項(11 ページ)の

表現の修正。

適のSQL実行計画を得るために、場合によってはリテラル条件を使用する必要

があることに関する文章の修正。脚注の 62を参照。

SQL インジェクション・プルーフ PL/SQL の記述方法

50

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 55: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

付録 B: このホワイト・ペーパーで紹介されている新しい専門用語の 定義

この付録には、このホワイト・ペーパーで紹介している新しい専門用語の定義を

収録しています。

一般 SQL 名

一般 SQL 名の先頭は A..Z の範囲の大文字のアルファベット文字であり、後続の文

字には A..Z の範囲の大文字の英数字、または下線、#、$のみ使用できます。一般

SQL 名は、SQL 文内では二重引用符で囲む必要はありません。また、囲まれてい

ない場合、名前が大文字であるか小文字であるかは関係ありません。ただし、二

重引用符で囲まれている場合もあります。囲まれている場合は、SQL パーサーに

よって文字の大小が保持されるので、すべて大文字で書く必要があります。

例 2:ユーザー指定の表名(14 ページ)を参照してください。

エキゾチック SQL 名

エキゾチック SQL 名とは、一般 SQL 名のルールに違反している名前のことです。

したがって、SQL 文内では二重引用符で囲む必要があります。

例 2:ユーザー指定の表名(14 ページ)を参照してください。

コンパイル時固定 SQL 文テキスト

コンパイル時固定 SQL 文テキストとは、実行時に変更できない SQL 文のテキスト

のことで、該当の SQL 文を実行する PL/SQL ユニットのソース・コードを読み取

ることで、確実に見分けられます。動的 SQL の場合は、PL/SQL の静的 varchar2表現である SQL 文のテキストです。PL/SQL の静的 varchar2 表現の値は、実行時

には変更できず、コンパイル時に事前に計算されます。PL/SQL コンパイラでは、

PL/SQL 埋込み SQL 文を実行する SQL 文のテキストが作成されるため、これもコ

ンパイル時固定 SQL 文テキストであると考えます。

"コンパイル時固定SQL文テキストと実行時作成SQL文テキストを区別する"(6ページ)を参照してください。

実行時作成 SQL 文テキスト

実行時作成 SQL 文テキストとは、コンパイル時固定 SQL 文テキストではない SQL文のテキストのことです。

"コンパイル時固定SQL文テキストと実行時作成SQL文テキストを区別する"(6ページ)を参照してください。

SQL 構文テンプレート

SQL 構文テンプレートは、通常の SQL 文のように見えますが、その概念は設計仕

様書で説明されています。次に例を示します。

SQL インジェクション・プルーフ PL/SQL の記述方法

51

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 56: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

select &&1 from &&2 where &&3 = &4

これは、テンプレートのインスタンスである一連の通常の SQL 文を規定するため

のドキュメンテーション手法です。PL/SQL プログラムによるインスタンス化は、

テンプレート内の&&または&で始まる要素の代わりに通常の SQL 要素を使用す

ることで実現できます。テンプレート内の空白ではないその他すべての要素は、

通常の SQL 文としてインスタンス化する際に忠実に再現する必要があります。こ

れには、SQL のヒントが含まれますが、空白として処理できる通常のコメントは

除外されます。

"新しい概念の紹介:SQL構文テンプレート"(4 ページ)を参照してください。

値プレースホルダ

値プレースホルダとは、SQL 構文テンプレート内の&で始まる要素のことです。

これは、適切な SQL リテラルまたは通常の SQL 文内の標準プレースホルダのい

ずれかを表します。

"新しい概念の紹介:SQL構文テンプレート"(4 ページ)を参照してください。

単純 SQL 名プレースホルダ

単純 SQL 名プレースホルダとは、SQL 構文テンプレート内の&&で始まる要素の

ことです。これは、通常の SQL 文内の単純 SQL 名を表します。

"新しい概念の紹介:SQL構文テンプレート"(4 ページ)を参照してください。

静的 SQL 構文テンプレート

静的 SQL 構文テンプレートとは、設計仕様書に明示的に記載できる SQL 構文テン

プレートのことです。静的 SQL 構文テンプレートをインスタンス化および実行す

る実装は、常に次のように記述でき、次のようにする必要があります。 <<Some_Inner_Block>>declare Stmt constant varchar2(32767) := 'select c1 from ' || Sys.DBMS_Assert.Simple_Sql_Name(Dynamic_Name) || ' where PK = :b for update wait ' || Sys.DBMS_Assert.Enquote_Literal(Dynamic_Value); begin execute immediate Stmt... ... end Some_Inner_Block;

代入するのは、次の 1 つまたは複数の呼出しを伴う、1 つまたは複数の PL/SQL の

静的 varchar2 表現を 1 つに連結したものです。

• DBMS_Assert.Enquote_Literal()、または

• To_Char(x f, 'NLS_Numeric_Characters = ''.,''')、または

• DBMS_Assert.Simple_Sql_Name()

静的 SQL 構文テンプレートは、from リスト項目の名前が実行時までわからない事

例を除き、埋込み SQL を使用可能な場合などに使用できます。

定義上は、コンパイル時固定 SQL 文テキストは、静的 SQL 構文テンプレートに

準拠します。

SQL インジェクション・プルーフ PL/SQL の記述方法

52

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 57: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

"静的SQL構文テンプレートの定義"(7 ページ)を参照してください。

動的 SQL 構文テンプレート

動的 SQL 構文テンプレートとは、特定のコール・サイトで実行する通常の SQL文を規定するために設計された大量のテンプレートのセットの 1 つです。こう

いったコール・サイトでは、すべてのテンプレートを記述するにはそのセットは

大きすぎますが、確実に記載されます。この説明は、正規表現構文などを使用し

て、設計仕様書に記載できます。

"サンプル・フォームによる問合せ"の項(40 ページ)に示されているようなイン

タフェース例での問合せを実行するためには、一連の動的SQL構文テンプレート

が必要です。動的SQL構文テンプレートでは、実用的にするために単純SQL名プ

レースホルダも値プレースホルダも使用する必要はありません。また、その安全

性については、安全でないコードをレビューすると容易に判定できます。このよ

うな使用においては、特定のコール・サイトに対する一連の動的SQL構文テンプ

レートに準拠する通常のSQL文はすべて、PL/SQLの静的varchar2 表現だけを使用

して作成されます。

実行時作成 SQL 文テキストは、静的 SQL 構文テンプレートまたは動的 SQL 構文

テンプレートに準拠する場合があります。

"動的SQL構文テンプレートの定義"(8 ページ)を参照してください。

静的テキスト

静的テキストは、次のいずれかです。

• 『PL/SQL Language Reference』ブックに定義されている PL/SQL の静的

varchar2 表現、または

• 静的テキスト項目の任意の連結によって形成される表現、または

• 静的テキストとともに明白に割り当てられているローカル変数の値(ローカ

ル変数は、現在のトップ・レベルの PL/SQL ブロック内で宣言されている変

数です)

この定義は、意図的に再帰的になっています。連結は、実行時まで結果がわから

ないテストによって制御される場合があります。しかし、ありうるすべての連結

結果は静的テキストのみであり、その 終的なソースは PL/SQL の静的 varchar2表現だけであることは、人間がおこなう普通の検査を通じて明らかにする必要が

あります。

"静的テキスト"(33 ページ)を参照してください。

動的テキスト

動的テキストとは、静的テキストではないすべてのテキストです。わかりやすい

例としては、仮パラメータ、パッケージのトップ・レベルで宣言されている constantキーワードのない変数、および SQL 文を実行することで、または

Utl_File.Get_Line()に対する Buffer 仮パラメータの実際の引数として割り当てられ

る通常の変数などがあります。

"動的テキスト"(34 ページ)を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

53

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 58: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

安全な動的テキスト

安全な動的テキストは、以下のいずれかの出力です。

• DBMS_Assert.Enquote_Literal()、または

• To_Char(x f, 'NLS_Numeric_Characters = ''.,''')、x は数値データ型の変数、f は明

示的な書式モデル'TM'、または

• DBMS_Assert.Simple_Sql_Name()

"安全な動的テキスト"(34 ページ)を参照してください。

安全な SQL 文テキスト

安全な SQL 文テキストとは、静的テキストと安全な動的テキストを任意に連結し

たものです。中間結果や 終結果にはローカル変数を使用できます。

"安全なSQL文テキスト"(35 ページ)を参照してください。

トップ・レベルの PL/SQL ブロック

トップ・レベルの PL/SQL ブロックは、次のいずれかです。スキーマレベルのファ

ンクション、スキーマレベルのプロシージャ、パッケージ本体またはタイプ本体

内のトップ・レベルで定義されているファンクションまたはプロシージャ、パッ

ケージの初期化ブロック、またはトリガーの実装。したがって、パッケージまた

はパッケージ本体のトップ・レベルで宣言された変数は、定義上はローカル変数

ではありません。

静的テキスト(33 ページ)を参照してください。

SQL インジェクション・プルーフ PL/SQL の記述方法

54

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 59: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

付録 C: SQL インジェクションの防止ルールの概要 この付録には、このホワイト・ペーパーで主張している SQL インジェクションを

防ぐためのルールが収録されています。

Rule_1:SQL文をプログラムで作成する必要がある場合、コードに

は通常、中間結果の変数を使用する必要があります(少なくとも、

使用することによるメリットがあります)。変数を定数として宣言

する目的で、宣言内で値を割り当てます。割り当てるには、ネスト

されたブロック文または前方宣言ファンクションを使用する必要

がある場合があります。この手法を使用することで、コード・レ

ビューが楽になります。これは、変数の値が初期割当て時と使用時

で変化しないとわかっているからです。(9 ページ)

SQL 文をプログラムで作成する場合、

中間結果に使用される変数を定数と

して宣言するとコードのレビューア

が理解しやすくなります。 SQL 構文テンプレートの用語が意味

する内容、および静的 SQL 構文テン

プレートと動的 SQL 構文テンプレー

ト間の違いを理解します。

Rule_2:SQL構文テンプレートが意味する内容を理解します。この

理解を、実行時作成SQL文テキストを構成するコードの設計に応用

します。静的SQL構文テンプレートと動的SQL構文テンプレートの

違いを理解します。(11 ページ)

Rule_3:SQLインジェクションという用語の定義方法について、意

図しないSQL構文テンプレートでのSQL文の実行という点から理解

します。その結果、実行時作成SQL文テキストを、動的SQLを使用

して実行する場合のみ、SQLインジェクションの攻撃を受けやすい

ことを理解します。(11 ページ)

SQL インジェクションは、意図しない

SQL 構文テンプレートでの SQL 文の

実行であり、そのリスクは、実行時作

成 SQL 文テキストが動的 SQL を使用

して実行されるときにのみ生じるこ

とを理解します。

PL/SQL API を介してのみクライアン

トにデータベースを公開します。 Rule_4:PL/SQL APIを介してのみクライアントにデータベースを公

開します。クライアントがアプリケーションのそのほかの種類のオ

ブジェクト(とくに表とビュー)に直接アクセスできないように、

権限を慎重に制御します。(28 ページ)

通常のアプリケーション・コードの設

計仕様書では、SQL を実行するため

に、埋込み SQL 以外の任意の方法を

使用するすべての提案を擁護してい

ます。

Rule_5:通常のアプリケーション・コードの設計仕様書に、埋込み

SQL以外のものを使用することが提案されている場合、仕様書を精

査し、論理的根拠について慎重に検討することをお勧めします。設

計次第で、優れたセキュリティを保持できます。しかし、セキュリ

ティのためのコードは明示的に記述する必要があります。(29 ペー

ジ)

SQL インジェクション・プルーフ PL/SQL の記述方法

55

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 60: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

可能な場合は、コンパイル時固定 SQL文テキストを使用します。PL/SQL の

静的 varchar2 表現とともに埋込み

SQLまたは execute immediateを使用

します。

Rule_6:使用できない場合を除いて可能な場合は、コンパイル時固

定SQL文テキストを使用します。埋込みSQLがサポートする種類の

SQL文の場合は、埋込みSQLを使用します。それ以外は、PL/SQLの静的varchar2 表現だけを使用して構成されている単一のPL/SQLのconstant引数とともにexecute immediateを使用します。使用できない

という結論に達した場合は、機能仕様書と設計仕様書を同僚ととも

に慎重に再検討し、コンパイル時固定SQL文テキストを使用できな

い理由を設計仕様書で具体的に説明します。(30 ページ)

SQL 構文テンプレート内の値プレー

スホルダの置換を提案する設計の正

当性を主張します。

Rule_7:(静的SQL構文テンプレートであろうと、動的SQL構文テ

ンプレートであろうと)SQL構文テンプレート内の値プレースホル

ダの置換を提案している設計仕様書は、疑って見る必要があります。

また、このアプローチについては、納得のいく議論をおこなってか

ら、承認する必要があります。(31 ページ)

Rule_8:(静的SQL構文テンプレートであろうと、動的SQL構文テ

ンプレートであろうと)SQL構文テンプレート内の単純SQL名プ

レースホルダの置換を提案している設計仕様書は、疑って見る必要

があります。また、このアプローチについては、納得のいく議論を

おこなってから、承認する必要があります。(32 ページ)

SQL 構文テンプレート内の単純 SQL名プレースホルダの置換を提案する

設計の正当性を主張します。

Rule_9:実行時に初めて構成が明らかになる一連のプレースホルダ

にバインドするための要件(これはDBMS_Sql APIで完全にサポー

トされています)と、 適な問合せ実行パフォーマンスを追及する

ために、直接エンコードされているリテラルを使用するための要件

を混同しないようにします。(33 ページ)

動的 SQL 構文テンプレートを使用

する必要性をテンプレート内の値

プレースホルダを SQL リテラルで

置換する必要性と混同しないよう

にします。

Rule_10:PL/SQLのテキスト表現で表されているSQL文が、動的SQLのためのPL/SQLのAPIのいずれかを使用して実行される場合、その

表現は安全なSQL文テキストである必要があります。安全なSQL文テキストは、静的テキストと安全な動的テキストを連結したもので

す。静的テキストは、PL/SQLの静的varchar2 表現でのみ構成されま

す。動的テキストは、静的テキストではないすべてのテキストです。

安 全 な 動 的 テ キ ス ト は 、 オ ラ ク ル が 提 供 す る 、

DBMS_Assert.Simple_Sql_Name()、DBMS_Assert.Enquote_Literal()、To_Char(x f, n)の 3 つのファンクションのうちの 1 つの出力です。 (35 ページ)

動的 SQL は、静的テキストと安全な

動的テキストを連結したもののみ実

行できます。安全な動的テキストは、

オラクルが提供する Simple_Sql_Name() Enquote_Literal() To_Char(x f, n) の 3 つのファンクションのうちの 1 つの出力です。

SQL インジェクション・プルーフ PL/SQL の記述方法

56

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 61: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

SQL 名の安全性を保証するには、

Simple_Sql_Name()を使用します。文

字列または日時リテラルの安全性を

保証するには、Enquote_Literal()を使

用します。数値リテラルの安全性を保

証するには、To_Char(x f, n)を使用し

ます。

Rule_11:DBMS_Assert.Enquote_Literal()またはTo_Char(x f, n)によっ

て作成される安全な動的テキストは、SQLのリテラルを対象とする

SQL 文 内 の ス ポ ッ ト で の み 使 用 す る 必 要 が あ り ま す 。

DBMS_Assert.Simple_Sql_Name()で作成される安全な動的テキスト

は、単純なSQL名を対象とするSQL文内のスポットでのみ使用する

必要があります。(35 ページ)

Rule_12:SQL文を実行するPL/SQL文の直前のコードで、実行時作

成SQL文テキストの安全性を確立することで、監査者の仕事を楽に

します。(37 ページ)

Simple_Sql_Name()、Enquote_Literal()、またはTo_Char(x f, n)をコールするコードは、これらの

コールを安全なものにする SQL 文を

実行するコードに近いものにします。

動的 SQL を実行するために送信する

オラクル提供の複数あるAPIのどれで

あろうと、同じ方法で実行時作成 SQL文テキストの安全性を保証します。

Rule_13:実行時作成SQL文テキストを実行するために設計されてい

る、オラクル提供の全APIを確認します。ネイティブ動的SQLおよ

びDBMS_Sql APIとともに使用するテキストの安全性を確保するた

めに使用するのと同じルールを使用して、そのようなテキストの安

全性を保証します。このアプローチは、テキストが完全なSQL文を

表している場合も、文のコンポーネントを表している場合も同じで

す。(58 ページ)

新しいコードでは

DBMS_Utility.Exec_DDL_Statement()を使用しないでください。

Rule_14:新しいコードでDBMS_Utility.Exec_DDL_Statement()を使用

しなければならない十分な理由はありません。使用しないでくださ

い。(59 ページ)

SQL インジェクション・プルーフ PL/SQL の記述方法

57

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 62: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

付録 D: 動的 SQL を実行する、そのほかのオラクル提供サブプログラム

かつて PL/SQL のプログラマーは、PL/SQL のテキスト値として提示されている

SQL 文を実行する方法は次の 2 つしかないと考えていました。ネイティブ動的

SQL および DBMS_Sql API です。これは事実とは異なります。ほかの方法も存在

します。この付録では、それらの方法について説明します。

一部の方法は、完全な SQL 文を受け入れて実行するために設計されていますが、

SQL 文は特定の種類(任意の種類の DDL 文、PL/SQL ユニットを作成するための

DDL 文など)であることが要求されます。そのほかの方法は、SQL 文のコンポー

ネント(where 句など)を受け入れてほかのコンポーネントと連結し、プログラム

で SQL 文を作成して実行するように設計されています。

これらの API はすべて、SQL インジェクションに対する安全性の確保はコール元

の責任であるという前提の上に設計されているプロパティを共有します。 動的 SQL を実行するために送信する

のが、複数あるオラクル提供の API のどれであろうと、同じ方法で実行時作

成 SQL 文テキストの安全性を保証し

ます。

Rule_13

実行時作成 SQL 文テキストを実行するために設計されている、オラクル提供の

全 API を確認します。ネイティブ動的 SQL および DBMS_Sql API で使用するテ

キストの安全性を確保するために使用するのと同じルールを使用して、そのよ

うなテキストの安全性を保証します。このアプローチは、テキストが完全な SQL文を表している場合も、文のコンポーネントを表している場合も同じです。

DBMS_Utility.Exec_DDL_Statement()

Code_41にDBMS_Utility.Exec_DDL_Statement()の実装の重要な部分を示します。 -- Code_41 Cur := DBMS_Sql.Open_Cursor(); DBMS_Sql.Parse(Cur, Stmt, DBMS_Sql.Native); DBMS_Sql.Close_Cursor(Cur);

StmtがDDL文94である場合、それを実行するにはこのコードで十分なことがわかり

ます95。しかし、埋込みSQLでサポートされていないその他の種類の文(alter session

など)の場合は、DBMS_Sql.Execute()をコールする必要があります。この事実から、

DBMS_Utility.Exec_DDL_Statement()ではDDL文以外のものは無視されることがわ

かります。

このことを意図した動作として規定する設計仕様書というのは、ありそうもあり

ません。したがって、新しいコードでは、execute immediate を使用して DDL 文を

実行する必要があります。また、埋込み SQL を使用できない場合は、ネイティブ

動的 SQL または DBMS_Sql API を使用して、その他の種類の文を実行する必要が

あります。コードをコールする目的はわかっており、SQL 文テキストは、この目

的のために特別に作成されています。

94. DDL 文として分類される文の種類の一覧については、『SQL Language Reference』ブックを参照し

てください。

95. この事実は、『PL/SQL Packages and Types Reference』ブックに記載されています。

SQL インジェクション・プルーフ PL/SQL の記述方法

58

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 63: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

Rule_14 新しいコードでは

DBMS_Utility.Exec_DDL_Statement()を使用しないでください。

新しいコードで DBMS_Utility.Exec_DDL_Statement()を使用しなければならない

十分な理由はありません。使用しないでください。

DBMS_DDL.Create_Wrapped()

このプロシージャの目的は、あいまいな表現の PL/SQL ユニットを作成して、カ

タログにコードを格納することにあります。入力は、適切な create or replace 文の

通常テキストです。ソースをあいまいにする必要がない場合は、execute immediateを使用します。

PL/SQLユニットをプログラムで生成するのがふさわしい場合にユースケースが

生じます。顧客サイトにISVコードをインストールする場合に、インストール後の

手順が実行されるコードは、1 つの例といえます。特定のサブプログラムのロジッ

クは、インストール環境の特定のプロパティを反映する必要があるからです。35ページのRule_10およびRule_11は、プログラムで作成できるPL/SQLに制限を加え

ています。そのような大量のソースは、静的テキストとして作成する必要があり

ます。ソース内で動的テキストから取得可能なのは識別子とテキスト・リテラル

だけです。十分な注意をテキスト・リテラル96に関しては払う必要があります。こ

のホワイト・ペーパーで求めている安全な動的テキストを使用する場合も同様で

す。たとえば、execute immediateの引数として安全な動的テキストを認めるのは明

らかに危険です。おそらく、静的テキスト以外で使用して問題ないのは、ユニッ

ト自体の名前またはユニットが公開するサブプログラムの名前など、外部から判

断できる名前だけです。

DBMS_HS_Passthrough

パススルー機能は、Oracle 以外のシステムで、Oracle Database で解析せずに SQL文を実行するためのメカニズムを開発者に提供します。

DBMS_HS_Passthrough.Execute_Immediate()

このファンクションは、SQL をただちに実行します。select 文を除く、すべての有

効な SQL 文がただちに実行されます。内部的には、SQL 文は、パススルーSQL プ

ロ ト コ ル の シ ー ケ ン ス ( Open_Cursor() 、 Parse() 、 Execute_Non_Query() 、Close_Cursor())を使用して実行されます。SQL 文にプレースホルダを含めること

はできません。

DBMS_HS_Passthrough.Parse()

このファンクションは、提供される SQL文をOracle以外のシステムで解析します。

文の実行は、後続の DBMS_HS_Passthrough.Execute_Non_Query()で、select 文の場

合は DBMS_HS_Passthrough.Fetch()でおこなわれます。SQL 文にプレースホルダを

含めることはできません。

OWA_Util

OWA_Util パッケージは、PL/SQL テキスト値として提示される select 文の結果を

使用して HTML ページまたはページ断片を生成する機能を提供します。

96. 安全な数値および日時リテラルを使用して PL/SQL ユニットのソース・コードを作成することは、

本質的にリスクを伴う作業ではありませんが、実際的な設計仕様書でこの点をどのように要求する

かを確認するのは難しい作業です。

SQL インジェクション・プルーフ PL/SQL の記述方法

59

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 64: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

OWA_Util.Bind_Variables()

このファンクションの目的は、その他の OWA_Util サブプログラムで、その後使

用 さ れ る select 文 を 作 成 す る こ と に あ り ま す 。 た だ し 、 こ れ は 、

DBMS_Sql.Open_Cursor() 、 DBMS_Sql.Parse() 、 お よ び 一 連 の

DBMS_Sql.Bind_Variable()をコールするためのラッパーにすぎません。このファン

クションは、通常の DBMS_Sql 数値カーソルを返します。

OWA_Util.Bind_Variables()には、bv17Name、bv17Value のような名前がついている

25 の仮パラメータのペアがあり、データ型はすべて varchar2 で、デフォルト値は

null となっています。そのため、これらのパラメータは用途に応じて省略できま

す。not null の各ペアによって、対応する DBMS_Sql.Bind_Variable()のコールがお

こなわれます。

OWA_Util.ListPrint()

このプロシージャは、select 文の出力から HTML フォーム・ユーザーインタフェー

ス要素(選択リスト)を生成します。これには 2 つのバージョンがあります。

初のバージョンは、完全な select 文を PL/SQL テキスト値として取得し、2 つめの

バージョンは、事前に OWA_Util.Bind_Variables()をコールするなどして準備されて

いる DBMS_Sql 数値カーソルを取得します。

OWA_Util.TablePrint()

このファンクションは、データベース表のコンテンツを表すの HTML 断片を生成

します。このファンクションは、完全な select 文を取得しないので、コール元は

表の名前、必要な列のカンマ区切りリストを提供し、where 句と order by 句を指定

する必要があります。where 句にプレースホルダを含めることはできません。

SQL インジェクション・プルーフ PL/SQL の記述方法

60

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 65: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

付録 E: 動的ポリモフィズムを使用するコールバックの実装を表すため

の自己完結型のコード このコードは、通常のユーザーとして実行できます。スペースを節約するために、

このコードが作成するオブジェクトはどれもまだ存在していないものとして記述

されています。

動的ポリモフィズムを使用する実装が、execute immediate での単純な実装と同じ結

果を生じることを証明するために、テスト・ハーネス( 後にあるプロシージャ

p())では、各メソッドを交互に使用して、異なる引数を指定した数多くの起動に

お け る チ ェ ッ ク サ ム を 計 算 し ま す 。 一 部 の タ イ ミ ン グ ・ コ ー ド

(DBMS_Utility.Get_CPU_Time()を使用します)は簡単に追加でき、チェックサムが

計算される入力値の範囲を大幅に増やすことができます。 create function Callback(Input in integer) return integer is begin return Input*Input; end Callback; / create type Hook authid Current_User is object( Dummy number, not instantiable member function Callback( self in Hook, Input in integer ) return integer ) not final not instantiable / create type My_Implementation under Hook( overriding member function Callback( self in My_Implementation, Input in integer) return integer ) / create type body My_Implementation is overriding member function Callback( self in My_Implementation, Input in integer) return integer is begin return Input*Input; end Callback; end; / create package Which_Callback is function The_Callback return Hook; end Which_Callback; / create package body Which_Callback is -- Implements some logic to chose an instance of

SQL インジェクション・プルーフ PL/SQL の記述方法

61

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 66: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

www.oracle.com/technology/tech/pl_sql/pdf/how_to_write_injection_proof_plsql.pdf

2008 年 12 月 4 日

-- the appropriate subtype of Hook function The_Callback return Hook is Which constant Hook := My_Implementation(0); begin return Which; end;end Which_Callback; / create procedure p(Name in varchar2, Obj in Hook) is No_Of_Repeats constant pls_integer := 100; Checksum integer; n integer; x integer; Safe_Name constant varchar2(32767) := Sys.DBMS_Assert.Simple_Sql_Name(Name); Stmt constant varchar2(32767) := 'begin :x := '||Safe_Name||'(:n); end;'; procedure Check_It is begin if(Checksum is null or Checksum <> 338350) then Raise_Application_Error(-20000, 'Callback: unexpected Checksum: ' || Checksum);end if; end Check_It; begin Checksum := 0; n := 0; for r in 1..No_Of_Repeats loop n := n + 1; execute immediate Stmt using out x, in n; Checksum := Checksum + x; end loop; Check_It(); Checksum := 0; n := 0; for r in 1..No_Of_Repeats loop n := n + 1; Checksum := Checksum + Obj.Callback(n); end loop; Check_It(); end p; / begin p('Callback', Which_Callback.The_Callback()); end; /

SQL インジェクション・プルーフ PL/SQL の記述方法

62

Oracle Corporation 発行「How to write SQL injection proof PL/SQL」の翻訳版です。

Page 67: SQL インジェクション・プ ルーフ PL/SQL の記述方法 · トピックの詳細は、ホワイト・ペーパー『Doing SQL from PL/SQL:Best and Worst Practices 』

SQL インジェクション・プルーフ PL/SQL の記述方法 2008 年 12 月 Mark Fallon、Software Development Director、Oracle Headquarters Bryn Llewellyn、PL/SQL Product Manager、Oracle Headquarters Howard Smith、Director、Global Product Security、Oracle UK Copyright © 2008, Oracle.All rights reserved. 本文書は情報提供のみを目的として提供されており、ここに記載され

る内容は予告なく変更されることがあります。 本文書は、その内容に誤りがないことを保証するものではなく、また、

口頭による明示的保証や法律による黙示的保証を含め、商品性ないし

特定目的適合性に関する黙示的保証および条件などのいかなる保証お

よび条件も提供するものではありません。オラクルは本文書に関する

いかなる法的責任も明確に否認し、本文書によって直接的または間接

的に確立される契約義務はないものとします。本文書はオラクル社の

書面による許可を前もって得ることなく、いかなる目的のためにも、

電子または印刷を含むいかなる形式や手段によっても再作成または送

信することはできません。 Oracle、JD Edwards、PeopleSoft、および Retek は、登録商標です。