注意: 本日のお話はフィクションです。
うっかりしていると陥りがちなミスなので、野暮を承知で注意喚起。
前置き
株式会社コラショは、青森県八戸市に本社を構える菓子メーカーである。国内7拠点に工場を持ち、独自の生産管理システム「BNS」を運用している。
このシステムは、もともと5年前に準大手ITベンダのトリッピーソリューションズ(株)がスクラッチ開発したものである。しかし、運用中に品質的課題が多発したことから、このたびベンダを弊社に切り替えて品質向上と機能強化を図ることとなった。
僕は、その案件にプログラマとして参戦している。
ある夜。
しんと静まったオフィスの中で、僕は顧客から受領した現行ソースを解読していた。が、さっそく怪しげな実装を目の当たりにすることとなる。
ほんとうにあったヤバい実装
本日の題材は、なんか企業向けシステムの「商品検索画面」です。
商品コードを入力し検索ボタンを押すと、検索結果が表示される、ただそれだけの画面です。簡単ですね。
検索ボタンの押下イベントには、以下のようなコードが書かれていました:
var sql = "SELECT 商品名 FROM 商品テーブル WHERE 商品コード = '" + txtProductCode.Text + // ←画面から取得した得体の知れない値 "';"; var result = db.Exec(sql); // SQLを実行
どうやら、たとえば商品コード欄に12345
と入力した場合には、以下のようなSQLが内部で生成・実行されることを意図しているようです。
SELECT 商品名 FROM 商品テーブル WHERE 商品コード = '12345';
ここまで読んだところで、さきほどの実装の問題点に気づけましたか?
一見正しく動作しそうですが、実は致命的な脆弱性を孕んでいます。
即死パターン
商品コード欄にBAKA'; DELETE FROM 商品テーブル; --
と入力して検索ボタンを押したら、果たしてどうなるでしょう。
以下のようなSQLが生成・実行され、(最悪の場合)商品テーブルのレコードが全削除されてしまいます*1。
SELECT 商品名 FROM 商品テーブル WHERE 商品コード = 'BAKA'; DELETE FROM 商品テーブル; --';
このような攻撃手法を、我々の業界ではSQL注入と呼んでいます。
基本情報技術者試験*2に出題されるくらい超メジャーな手口にも関わらず、我流でコーディングしているプログラマにとっては、存外に防御意識が稀薄化しやすいようです。
とりあえず、今回の件で僕が言いたいのはたったこれだけ。
プログラムの中でSQLを組み立てるときに、無防備に文字列を結合するな
経験上、こういうことをやらかす人はユニットテストもザル*3という偏見を持っているので、どのみち保険としてコードレビューは必要だと思っています。メンバのスキルが未知数なときは、特に。
結局どうすればよいか
IPAが「安全なウェブサイトの作り方」の中で、「別冊: 安全なSQLの呼び出し方」という資料を公開しているので、それに従うのが最も確実でしょう。とりあえずおとなもこどもも、おねーさんも読んでおいて損はないと思います。