| SQLインジェクション対策はおすみですか? 開発開始時点からのコンサルティングから、公開済みWebサイトの脆弱性検査、 脆弱性発見後の適切な対策まで |
2008-06-01 そろそろSQLエスケープに関して一言いっとくか
●SQLのエスケープ再考
本稿ではSQLインジェクション対策として、SQLのエスケープ処理の方法について検討する。
最近SQLインジェクション攻撃が猛威を振るっていることもあり、SQLインジェクションに対する解説記事が増えてきたようだが、対策方法については十分に書かれていないように感じる。非常に稀なケースの対応が不十分だと言っているのではない。ごく基本的なことが十分書かれていないと思うのだ。
SQLインジェクション対策には二通りある。バインド機構を使うものと、SQLのエスケープによるものだ。このうち、SQLのエスケープについて、十分に書かれているテキストが見当たらないのだ。このため、自分で書いてみようと思う。
IPAの「安全なウェブサイトの作り方改訂第三版」ではSQLのエスケープについて以下のように説明されている。
1)-2 バインド機構を利用できない場合は、SQL 文を構成する全ての変数に対しエスケープ処理を行う
解説 これは、根本的解決 1) のバインド機構を利用した実装ができない場合に実施すべき実装です。
利用者から入力されるパラメータや、データベースに格納された情報などに限らず、SQL 文を構成する全ての変数や演算結果に対し、エスケープ処理を行ってください。エスケープ処理の対象は、SQL文にとって特別な意味を持つ記号文字(たとえば、「'」→「''」、「\」→「\\」 など)です。
なお、SQL 文にとって特別な意味を持つ記号文字は、データベースエンジンによって異なるため、利用しているデータベースエンジンに応じて対策をしてください。データベースエンジンによっては、専用のエスケープ処理を行うAPIを提供しているものがあります(たとえば、Perl ならDBIモジュールのquote()など)ので、それを利用することをお勧めします。
引用した部分は、一般的な内容を網羅しているものの不満もある。短い文章の中で「など」が3回も現れることに象徴されるように、あいまい性の残る文章となっている。これはデータベースの製品依存のところでやむを得ないというのはよく理解できるのだが、結果として、読者に「データベースエンジンのマニュアルを読め」と言っているの等しい。それでいて、読者には、マニュアルのどこをどう調べたらよいかまでは示していない。「エスケープ処理の対象は、SQL文にとって特別な意味を持つ記号文字」と書かれているが、この説明だと、「;」や「=」、空白までエスケープしなければならないと誤解する読者が出てくるかもしれない。実際には、これらの文字をエスケープする必要はないし、SQLの標準規格には、そもそもこれらの文字をエスケープする手段が用意されていない。エスケープの必要がないので手段も用意されていないのだ。
そこで、「安全なウェプサイトの作り方」を補完するような形で、もう少しSQLエスケープについて書き足してみたい。
SQLインジェクションのおさらい
SQLインジェクション攻撃では、SQLに渡すパラメータ部分にSQL断片を挿入し、SQLの意味を書き換えることによって行われる。 バインド機構を使わずに自前でSQLを組み立てる場合、SQLに対するパラメータはSQLのリテラル(定数)の形で渡される。
リテラルには複数の型があるが、通常問題になるのは数値と文字列である。
数値リテラルの場合:
SELECT * FROM XXXXX WHERE NNUM=●●● -- ●●●は数値、例えば 123
文字列リテラルの場合:
SELECT * FROM XXXXX WHERE EID='■■■' -- ■■■は文字列、例えば S853
なぜリテラルを構成する文字列(●●●や■■■)を操作することでSQLの構文まで変わるのか。それは特殊記号などでリテラルを終端させ、その後にSQL断片を埋め込むからだ。
数値リテラルを終端させるには、数値以外の文字を加えればよい。
例: 123OR TRUE -- 数値リテラル123の後に、OR TRUE が続く。123 OR TRUEと同じ
文字列リテラルを終端させるには、単一引用符「'」を加えればよい
例: A'OR'A'='A
この例を先のSQLに適用すると、
SELECT * FROM XXXXX WHERE EID='A'OR'A'='A'
となる。つまり、WHERE句は常に真となる。
SQLインジェクション対策の基本的な考え方
すなわち、SQLインジェクション対策の方針としては、リテラルを勝手に終端させないようにすることが必要なのだ。そして、この処理は数値リテラルと文字列リテラルとでは、方法が異なる。
数値リテラルの場合は、数値以外の文字が出てきた時点でリテラルの終端となるので、数値としての妥当性検証を行うことになる(数値項目に対するSQLインジェクション対策のまとめ参照) 。
一方、文字列リテラルの場合は、文字列リテラルのエスケープを行えばよい。SQL92などSQLの標準規格で規定しているのは、単一引用符「'」のエスケープであるが、データベースの種類によっては、円記号(バックスラッシュ)「\」のエスケープも必要となる。
商用データベースの場合
商用データベースの場合は、SQL標準に従い、単一引用符「'」を「''」と重ねる処理を行う。Oracle、SQL Server、IBM DB2についてはリファレンスと動作の両方で確認した。
オープンソース・データベースの場合
オープンソースのデータベースとして広く普及しているMySQLの場合、文字列リテラル中にC言語風の「\」を使ったエスケープシーケンスが記述できる。従って、文字列リテラル中に「\」自体を記述する際には、「\\」とする必要がある。一方、単一引用符「'」は「''」としてもよいし、「\'」としてもよい。その他、二重引用符「"」が利用できるなど自由度が高い。
オープンソース・データベースのもう一方の雄PostgreSQLの場合は事情が少し複雑となるが、デフォルトではMySQLと同じで、単一引用符と円記号(バックスラッシュ)の両方をエスケープする必要がある。しかし、設定パラメータstandard_conforming_stringsがonの場合(デフォルトはoff)は、Oracleなどと同じ挙動となる(現実にはもう少し複雑だが稿をあらためて説明したい)。
まとめると以下のようになる。
| データベース | 元の文字 | エスケープ後 |
| Oracle MS SQL IBM DB2 |
' | '' |
| MySQL PostgreSQL |
' | '' または \' |
| \ | \\ |
なお、念のため補足すると、エスケープ処理はセキュリティ対策のために行うものでは元々なく、与えられたパラメータに対して正しく処理を行うために必要な処置である。入力に「'」や「\」が使えないと不便で仕方がないし、現実的に不具合が生じるだろう。
その他のデータベース製品の場合はどうしたらよいか
今回説明した内容にもっとも近い記述があるドキュメントとしては、佐名木智貴氏の近著「セキュアWebプログラミングTips集(ソフト・リサーチ・センター)」がある。同書では、SQLエスケープの基本として
「'」は、「''」(シングル・クォート2個)にエスケープ処理することで、SQLインジェクションから防御することができる(同書P210)
とした上で、
mySQLとPostgreSQLの場合のSQLインジェクション対策として、入力データをSQL文の文字列リテラルとして使う場合、「'(シングル・クォート)」と「\」をSQLエスケープすること(同書P213)
と指摘している。
非常に丁寧な仕事ぶりで好感を持った。ただ、同じページの以下はいただけない
筆者の知らないデータベース・ソフトウェアでは、SQLが拡張され、それ以外のメタキャラクタもあるかも知れない。ぜひ読者諸氏には今一度、自分の使っているデータベース・ソフトウェアのSQLリファレンスを通読することを推奨する(同書P213)。
「SQLリファレンスを通読」とは・・・無茶言うなよと思う。
実際には通読する必要はなく、「リテラル」、「定数」、「文字列」などのキーワードを手掛かりに、文字列リテラルの項を探すとよい。本稿で引用したリファレンスもこのようにして探したものである。
Oracleの場合を例にマニュアルの見方を説明しよう。題材として、Oracle10gのオンラインマニュアルを利用する。
まず、目次から「リテラル」を探すと、「2 Oracle SQLの基本要素」に「リテラル」や「テキスト・リテラル」という項が見つかる。「テキスト・リテラル」の項を読むと、
- cは、データベース・キャラクタ・セットの任意の要素です。リテラル内の一重引用符(')の前には、エスケープ文字を付ける必要があります。リテラル内で一重引用符を表すには、一重引用符を2つ使用します
- ''は、テキスト・リテラルの始まりと終わりを示す2つの一重引用符です。
このように、エスケープの必要な文字は一重引用符「'」であること、エスケープの仕方は一重引用符を2つ使用することであることがわかる。他のデータベース・ソフトウェアでも、同様に探すことができるだろう。
本稿を参考に正しいSQLインジェクション対策を実施していただきたい。
徳丸さんの記事。 これは良いまとめ。 「'」→「''」、「\」→「\\」という単純な置き換えは、文字コードが Shift_JIS だったりする場合に問題が生じることがあると思うので、そのあたり(文字コードの選択の話や、半端なマルチバイト文字の取扱いの話?)に言及があると..
昨日のエントリ(徳丸浩の日記 - そろそろSQLエスケープに関して一言いっとくか - SQLのエスケープ再考)は思いがけず多くの方に読んでいただいた。ありがとうございます。その中で高木浩光氏からブクマコメントを頂戴した。 \がescape用文字のDBで\のescapeが必須になる理..
貴重な休みである土曜日に、朝から「WAS Forum Conference 20
- http://takagi-hiromitsu.jp/diary/ ×277
- http://takagi-hiromitsu.jp/diary/20080620.html ×204
- http://www.tokumaru.org/ ×71
- http://b.hatena.ne.jp/HiromitsuTakagi/ ×70
- http://yuki-lab.jp/diary/diar0806.html ×66
- http://www.mixclips.org/ ×63
- http://b.hatena.ne.jp/hotentry? ×61
- http://b.hatena.ne.jp/entry/http://www.tokumaru.or... ×40
- http://b.hatena.ne.jp/HiromitsuTakagi/20080602 ×39
- http://d.hatena.ne.jp/ockeghem/ ×39
- http://b.hatena.ne.jp/hotentry?cname=elec ×37
- http://takagi-hiromitsu.jp/diary/200806.html ×34
- http://yamagata.int21h.jp/d/ ×29
- http://yamagata.int21h.jp/d/?date=20080601 ×23
- http://www.hash-c.co.jp/ ×23
- http://ja.reddit.com/r/ja ×20
- http://codezine.jp/bookmark/hatena.aspx?dt=2008060... ×16
- http://www.tokumaru.org/d/20080716.html ×13
- http://b.hatena.ne.jp/entrylist?sort=hot&of=100&th... ×13
- http://ja.reddit.com/r/ja/ ×12
- http://d.hatena.ne.jp/ockeghem/20080622/p2 ×12
- http://d.hatena.ne.jp/Luffy/ ×12
- http://news.qooqle.jp/ ×11
- http://b.hatena.ne.jp/entrylist?sort=hot&of=50&thr... ×11
- http://reddit.com/r/ja ×10
- http://b.hatena.ne.jp/hotentry?mode=daily&date=200... ×8
- http://b.hatena.ne.jp/entry/8803069 ×7
- http://d.hatena.ne.jp/ikepyon/ ×7
- http://clip.nifty.com/entry/531e0f11b3b826fcfe8655... ×7
- http://b.hatena.ne.jp/t/エスケープ ×6
- http://b.hatena.ne.jp/t/sqlinjection ×6
- http://d.hatena.ne.jp/ikepyon/20080602 ×6
- http://www.tokumaru.org/d/20080602.html ×5
- http://www.tokumaru.org/was/ ×5
- http://www.tokumaru.org/JavaScript/ ×5
- http://javascriptist.net/docs/news.html ×5
- http://d.hatena.ne.jp/Luffy/20080602 ×5
- http://d.hatena.ne.jp/hsada/20080603/1212421919 ×5
- http://labs.ceek.jp/hbnews/list.cgi ×5
- http://b.hatena.ne.jp/HiromitsuTakagi/SQLインジェクション/... ×5
- http://img.simpleapi.net/ ×4
- http://www.tokumaru.org/d/20070924.html ×4
- http://d.hatena.ne.jp/ockeghem/searchdiary?word=SQ... ×4
- http://tokumaru.org/ ×4
- http://d.hatena.ne.jp/f-star/ ×4
- http://labs.ceek.jp/hbnews/ ×4
- http://b.hatena.ne.jp/hotentry?mode=rss ×4
- http://d.hatena.ne.jp/f-star/20080602/p21 ×4
- http://www.tokumaru.org ×4
- http://del.icio.us/popular/sql ×4
- http://search.live.com/results.aspx?q=SQL エスケープ&sr... ×3
- http://codezine.jp/bookmark/hatena.aspx?dt=2008060... ×3
- http://d.hatena.ne.jp/ikepyon/200806 ×3
- http://b.hatena.ne.jp/t/escape?sort=eid ×3
- http://d.hatena.ne.jp/f-star/?of=1 ×3
- http://b.hatena.ne.jp/t/escape ×3
- http://news.qooqle.jp ×3
- http://b.hatena.ne.jp/t/SQLインジェクション ×3
- http://del.icio.us/popular/security ×3
- http://www.hash-c.co.jp/index.html ×3
- http://feed.distantplace.org/はてなブックマーク_最近の人気エントリー/... ×3
- http://sideblue.net/ ×3
- http://tokumaru.org/d/20080602.html ×3
- http://b.hatena.ne.jp/entrylist?sort=hot&of=100&th... ×3
- http://b.hatena.ne.jp/ockeghem/SQL Server/ ×3
- http://b.hatena.ne.jp/entrylist?sort=hot&of=50&thr... ×3
- http://b.hatena.ne.jp/entry/http://www.tokumaru.or... ×3
- http://reddit.com/r/ja/ ×3
- http://www.tokumaru.org/d/20080822.html ×3
- http://b.hatena.ne.jp/entrylist?sort=hot&of=150&th... ×3
- http://mgw.hatena.ne.jp/?url=http://www.tokumaru.o... ×3
- http://del.icio.us/rss/popular/security ×3
- http://d.hatena.ne.jp/minechi_n/20080603/121251916... ×3
- http://b.hatena.ne.jp/tyamamoto/sql/?mode=detail ×2
- http://codezine.jp/bookmark/hatena.aspx?dt=2008060... ×2
- http://b.hatena.ne.jp/lizy/sql/?mode=detail ×2
- http://b.hatena.ne.jp/t/postgresql ×2
- http://clipp.in/ ×2
- http://labs.ceek.jp/hbnews/list.cgi?k=5 ×2
- http://reader.livedoor.com/subscribe/http://www.to... ×2
- http://yamagata.int21h.jp/d/?date=20080603 ×2
- http://b.hatena.ne.jp/ockeghem/ ×2
- http://bookmark.fc2.com/search/tag/database ×2
- http://zapanet.info/new/hatebu/ ×2
- http://d.hatena.ne.jp/f-star/20080602 ×2
- http://s.luna.tv/search.aspx?client=lunascape&s=0&... ×2
- http://d.hatena.ne.jp/ockeghem/20080708/p1 ×2
- http://b.hatena.ne.jp/nilnil/favorite ×2
- http://b.hatena.ne.jp/t/db2 ×2
- http://d.hatena.ne.jp/hsada/ ×2
- http://b.hatena.ne.jp/keyword/C言語 ×2
- http://logpi.jp/everybody ×2
- http://clip.nifty.com/popular ×2
- http://s.luna.tv/search.aspx?q=oracle escape&st=20... ×2
- http://b.hatena.ne.jp/t/セキュリティ?sort=hot ×2
- http://b.hatena.ne.jp/mitch0702/ ×2
- http://b.hatena.ne.jp/entrylist?url=http://&sort=h... ×2
- http://b.hatena.ne.jp/t/エスケープ?sort=count ×2
- http://b.hatena.ne.jp/hotentry/ ×2
- http://d.hatena.ne.jp/ockeghem/searchdiary?word=SQ... ×2
- SQL エスケープ ×34 / キーワード不明 ×16 / sql escape ×15 / SQL エスケープ文字 ×12 / SQL ESCAPE ×9 / SQL Server エスケープ ×7 / escape sql ×6 / SQL 必須 エスケープ ×5 / 徳丸 SQL ×5 / oracle sql エスケープ ×5 / エスケープ文字 ×4 / sql エスケープ ×4 / sqlserver 引用符 エスケープ ×4 / Oracle SQLインジェクション エスケープ ×4 / mssql エスケープ ×4 / sql server エスケープ文字 ×4 / sql エスケープ 必要 文字 ×4 / 徳丸浩の日記 ×4 / DB2 SQL エスケープ文字 特殊文字 ×3 / sql エスケープ文字 ×3 / postgresql 正規表現 エスケープ ×3 / SQL エスケープ 空白 ×3 / sql 文字列 単一引用符 ×3 / oracle シングルクォート エスケープ ×3 / SQL server エスケープ文字 バインド ×3 / データベース エスケープ処理 ×3 / sql シングルクォート エスケープ ×3 / oracle エスケープシーケンス ×2 / エスケープ記号 oracle ×2 / oracle エスケープ 記号 ×2 / mysql エスケープ バックスラッシュ ×2 / html エスケープ文字 ×2 / SQLServer エスケープ文字 ×2 / エスケープ ×2 / sql エスケープ処理 ×2 / sql 数値 クオート ×2 / sql エスケープ文字 oracle ×2 / エスケープ単一引用符 ×2 / db2 escape ×2 / MS SQL Server エスケープ ×2 / エスケープ インジェクション オラクル ×2 / sql A=a ×2 / mysql sqlインジェクション エスケープ ×2 / DB エスケープ ; ×2 / postgresql エスケープ シングル ×2 / sql select エスケープ ×2 / sql ×2 / sql escape dbi ×2 / sql インジェクション postgres escape ×2 / SQLServer 特殊文字 エスケープ ×2 / エスケープ SQL ×2 / エスケープ SQL ×2 / sql 文字 エスケープ ×2 / SQL 記号文字 ×2 / Oracle SQL エスケープ ×2 / 佐名木 MySQL バックスラッシュ ×2 / sql ' エスケープ ×2 / Oracle エスケープ文字 ×2 / mod_imagefight ×2 / sql _ エスケープ ×2 / SQLエスケープ ×2 / データベース エスケープ文字 ×2 / sql エスケープ 正規表現 ×2 / エスケープ sql ×2 / SQLインジェクション 対策 エスケープ文字 ×2 / "SQL Server" エスケープ文字 ×2 / エスケープ sqlserver ×2 / sql エスケープ oracle ×2 / C言語 SQLインジェクション MySQL ×2 / SQL エスケープ文字 ' ×2 / SQL エスケープ 文字 ×2 / sql ESCAPE ×2 / エスケープ SQL Server 文字列 ×2 / Oracle 'のエスケープ ×2 / oracle エスケープ シングル ×2 / oracle バックスラッシュ エスケープ ×2 / エスケープ 文字コード sqlserver ×2 / "sql server" エスケープ ×2 / バインド機構 ×2 / SQL Server エスケープ文字 ×2 / SQL バインド機構 ×2 / SQL エスケープ文字 MSSQL ×2 / http://www.tokumaru.org/d/20080601.html ×2 / sql エスケープ escape ×2 / SQL ×2 / mysql エスケープ文字 ×2 / postgresql SQL エスケープ文字 ×2 / SQLエスケープ再考 ×2 / DB2 エスケープ ×2 / oracle 記号 シングルクオート ×2 / oracle like インジェクション バインド 変数 ×2 / MySQL エスケープ処理 ×2 / SQLserver エスケープ ×2 / mysql エスケープ文字列 ×2 / sqlインジェクション エスケープ処理 ×2 / メタキャラクタインジェクション ×2 / sql server 記号 エスケープ ×2 / sql server バインド機構 ×2 / postgres エスケープ文字 ×2 / SQLインジェクション バインドの仕方 ×2
| SQLインジェクション対策はおすみですか? 開発開始時点からのコンサルティングから、公開済みWebサイトの脆弱性検査、 脆弱性発見後の適切な対策まで |
mySQL での「\」のエスケープについて知ったのは、(私にとっては無知の知の領域にある)mySQL のリファレンスを通読している時でした。私には衝撃的でした。無意味だと思うからです。「'」→「''」だけで十分なのに、「\」を導入する必要性を感じないからです。
なので、読者の人が使っているかも知れない私の知らない DB については、DB 開発者が自由に拡張しているかも知れないから注意してね。
というつもりなのです。
出版までにちゃんとまとめなくて中途半端だったので、次回は正規表現(Like演算子)の時のエスケープについてお願いします。
といっても正規表現の使用場面を考えると、そもそもインデックスを張っていないカラム対象が多いのでそれほど問題にはならないとは思いますが、プログラミング書法という観点でもお願いしたいですね。