9.4 SQLインジェクションの回避

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

SQLインジェクション攻撃(SQL Injection)、省略して注入攻撃はWeb開発において最もよく見かけるセキュリティホールの一種です。データベースから慎重に扱うべき情報を取得することができます。またはデータベースの特徴を利用してユーザの追加したりファイルをエクスポートしたりといった一連の悪意ある操作を行うことができます。データベースないしシステムユーザの最高権限を取得することすらありえます。

SQLインジェクションが発生する原因はプログラムがユーザの入力に有効フィルタリングを施しておらず、攻撃者がサーバに対し悪意あるSQL検索クエリを送信させてしまうからです。プログラムが攻撃者の入力を誤って検索クエリの一部として実行し、オリジナルの検索ロジックが改竄され、想定外に攻撃者が手塩にかけて作った悪意あるコードを実行してしまいます。

SQLインジェクション実例

多くのWeb開発者はSQLクエリが改ざんされるとは意識していません。そのためSQLクエリを信用できるコマンドとしてしまいます。意外にも、SQLクエリはアクセスコントロールを迂回することができます。そのため、身分の検証と権限の検査を迂回します。SQLクエリからホストシステムレベルのコマンドが実行されてしまうこともあります。

以下では実際の例によってSQLインジェクションの方法について詳しくご説明します。

以下のような簡単なログインフォームについて考えます:

<form action="/login" method="POST">
<p>Username: <input type="text" name="username" /></p>
<p>Password: <input type="password" name="password" /></p>
<p><input type="submit" value="ログイン" /></p>
</form>

我々の処理ではSQLはおそらくこのようになります:

username:=r.Form.Get("username")
password:=r.Form.Get("password")
sql:="SELECT * FROM user WHERE username='"+username+"' AND password='"+password+"'"

もしユーザが以下のようなユーザ名を入力して、パスワードが任意だった場合

myuser' or 'foo' = 'foo' --

我々のSQLは以下のようになります:

SELECT * FROM user WHERE username='myuser' or 'foo' = 'foo' --'' AND password='xxx'

SQLでは--はコメントを表します。そのため、検索クエリは途中で中断されます。攻撃者は合法的なユーザ名とパスワードを知らなくてもログインに成功します。

MSSQLではシステムをコントロールするさらに危険なSQLインジェクションがあります。下の恐ろしい例ではあるバージョンのMSSQLデータベース上でどのようにシステムコマンドを実行するか示しています。

sql:="SELECT * FROM products WHERE name LIKE '%"+prod+"%'"
Db.Exec(sql)

もし攻撃でa%' exec master..xp_cmdshell 'net user test testpass /ADD' --がprod変数として送信されると、sqlは以下のようになります

sql:="SELECT * FROM products WHERE name LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--%'"

MSSQLサーバは後ろのシステムに新しいユーザを追加するコマンドを含んだSQLクエリを実行します。もしこのプログラムがsaで実行され、かつMSSQLSERVERサービスに十分な権限があれば、攻撃者はシステムアカウントを取得しホストにアクセスすることができます。

上の例はある特定のデータベースシステムに対してですが、他のデータベースシステムで似たような攻撃が実施できないことを表すものではありません。このようなセキュリティホールは異なる方法を使用するだけで、あらゆるデータベースにおいて発生する可能性があります。

どのようにしてSQLインジェクションを予防するか

攻撃者はデータベースの構造の情報を知っていなければSQLインジェクション攻撃は実施できないと思われるかもしれません。確かにその通りです、しかし誰も攻撃者がこのような情報を取得できないとは保証できません。一旦彼らの手にわたってしまうと、データベースは危険に曝されます。もしBBSプログラムなどでオープンソースのソフトウェアパッケージを使ってデータベースにアクセスしているのであれば、攻撃者は容易に関連するコードを取得できます。もしこれらのコードの設計に不備があれば、リスクは更に大きくなります。現在Discuz、phpwind、phpcms等流行のオープンソースプログラムはいずれもSQLインジェクションによる攻撃の例があります。

これらの攻撃は常にセキュリティの高くないコードで発生します。そのため、外界で入力されたデータは永遠に信用してはいけません。特にセレクトボックスやフォームのhidden項目、cookieといったユーザからのデータがそうです。上のはじめの例のように、正常な検索であっても災難に見舞われる可能性があります。

SQLインジェクション攻撃の被害はこれだけ大きく、どのように予防すればよいのでしょうか?以下のこれらの提案はひょっとしたらSQLインジェクションの予防に一定の助けとなるかもしれません。

  1. Webアプリケーションのデータベースの操作権限を厳格に制限する。このユーザにはその作業に必要となる最低限の権限だけを与え、できる限りSQLインジェクション攻撃がデータベースに与える被害を減少させる。
  2. 入力されたデータが期待するデータ形式であるか検査し、変数の型を厳格に制限する。例えばregexpパッケージを使ってマッチング処理を行ったり、strconvパッケージを使って文字列を他の基本型のデータに変換することで判断する。
  3. データベースに入ってくる特殊文字('"\角括弧&*;等)に対してエスケープ処理を行う。またはエンコードする。Goのtext/templateパッケージにはHTMLEscapeString関数があり、文字列に対してエスケープ処理を行うことができます。
  4. すべての検索クエリにはなるべくデータベースが提供するパラメータ化検索インターフェースを使用する。パラメータ化されたクエリはパラメータを使用し、ユーザが入力した変数をSQLクエリに埋め込みません。すなわち、直接SQLクエリを組み立てないということです。例えばdatabase/sqlの検索関数PrepareQueryを使ったり、Exec(query string, args ...interface{})を使います。
  5. アプリケーションをデプロイする前になるべく専門のSQLインジェクション検査ツールを使って検査を行い、発見されたSQLインジェクションセキュリティホールにはすぐにパッチをあてる。ネット上ではこの方面のオープンソースツールがたくさんあります。例えばsqlmap、SQLninja等です。
  6. ページがSQLのエラー情報を出力するのを避ける。例えば型のエラー、フィールドのミスマッチ等です。コードのSQLクエリが暴露されることで攻撃者がこれらのエラー情報を利用してSQLインジェクションを行うのを防ぎます。

まとめ

上の例によってSQLインジェクションは被害が相当大きいセキュリティホールであるとわかりました。そのため我々が通常書くWebアプリケーションに対してはどのような小さな事でも非常に重視する必要があります。小さな事が命運を分けます。生活も同じ、Webアプリケーションを書くことも同じです。

results matching ""

    No results matching ""