ITエンジニア特化型Q&Aフォーラムteratailが主催となり、2018年3月24、25日の2日間に渡り開催された「MANABIYA -teratail Developer Days-」ではITエンジニアの問題解決カンファレンスをテーマに「疑問」を主題としたセッションが多く実施されました。その中から、徳丸 浩さんによる基調講演「teratailのQ&Aから学ぶWebセキュリティの現状」のレポートをお送りします。
セキュリティのプロがteratailで回答し続ける理由
講演は徳丸さんの自己紹介から始まりました。セキュリティを仕事としてセキュリティの書籍『体系的に学ぶ安全なWebアプリケーションの作り方』(SBリエイティブ)や『徳丸浩のWebセキュリティ教室』(日経BP社)を執筆し、脆弱性診断やコンサルティング業務を行う徳丸さんが、無償でYahoo!知恵袋やteratailなどで回答し続ける理由は、「正しい情報を知ってほしいという衝動や、間違った情報が書かれていると正したくなる、また課題が解決されていない状態で放置されているのがムズムズするから」ということです。
次に、生活のためだけに開発している人やあまり勉強をしない人が、どうやって開発をしているかという点を問題提起されました。
本を読み勉強する人、インターネット検索をする人、Q&Aサイトで質問する人、職場の先輩に聞く人も多くいると思います。不勉強な人は多くの場合、インターネット検索やQ&Aサイトのコードをコピペして少し変更して使うでしょう。teratailでも、学校の宿題や、仕事を丸投げで質問する方がいらっしゃいます。検索して、Q&Aサイトで聞いてつくっていく人が多いという現状があります。これ自体は変えられないと考えています。であれば、変えられない中で何が出来るのかということを考えていたりします。
昔はベストアンサーがついていても脆弱性がある例が多かったのですが、今のQ&Aはだいぶ改善されいるようです。
teratailのQ&Aを覗きながらWeb開発の現状をみる
「どのような方法で、どこまでチェックをしているのかを知りたい」という質問に対してですが、徳丸氏は、セキュリティチェックをする前提でなく、まずは脆弱性が入らない方法で開発をしたほうが良いとアドバイスされていました。処理や局面、それぞれどういった個所でどういった脆弱性が入るかを知り、その点を考慮した方法で開発をしたほうが良いと答えています。
続いては、データベースの暗号化方式について、よくセキュリティのリリースなどを読んでいると、暗号化されているのから大丈夫ですというのを見るのですがそういったところについての質問です。
「データベースの機能で暗号化するか、アプリケーション側で暗号化するのかどっちが良いのでしょうか」という質問。これに対して徳丸さんは以下のように解説を加えました。
どちらが良いということはなく、目的に応じて選択する必要があります。言語で暗号化して保存すると、SELECT文でデータを取得しても、データは暗号化されています。なので、復号されない限りSQLインジェクション対策として効果があります。しかし、データベースエンジン側で暗号化するとSELECT文でデータを取得すると、データは平文で返ってきます。この場合はSQLインジェクション対策として効果がありません。
では、データベースエンジン側での暗号化は意味が無いのかというと、HDDが盗まれた等の場合、復号鍵と共に盗まれない限り復号が難しいので、そういった場合は意味があります。ただ単純に暗号化するのではなくて、暗号化が求められているかを判断し、正しく暗号化しましょうということですね。
「addslashes関数を使うと良いと本に書いてありましたが、なぜこの関数を使うと良いのかわかりません。」という質問です。回答は「addslashes関数を使うのをやめましょう」なのですが、本当の問題はそういった古い教科書を使って学んでいることです。
まずadslashesがなぜだめなのかということから解説してくださいました。adslashesといえば、昔はMySQLにおいてインジェクション対策の標準でしたが、今は否定されています。MySQLはバックスラッシュを入れてエスケープするという仕様です。ただ、MySQLサーバの設定を変えると、バックスラッシュをエスケープしなくても良いシングルクオートは重ねるという仕様になります。たまたま、MySQLのバックスラッシュでのエスケープとadslashesと合致していたので、MySQLのSQLインジェクション対策の標準となっていたそうです。
また、もう1つ大きな問題があります。文字コードがShift-JISの場合、addslashsesは文字コードを考慮しないため、実際はエスケープ処理が必要ない「ソ」の文字コード(835c)の前半83を「NBH」、後半の5cを「¥」と解釈し、¥をエスケープして「¥¥」とします。こういった動作があることからSQLインジェクションが可能になるそうです。
正しいSQLインジェクション対策とそのロジック
ここから話を発展させて、SQLインジェクション対策についての解説となりました。
SQL文の動的な組み立て方には「文字列連携」と「プレースホルダ」の2種類があり、プレースホルダのほうが安全です。ではなぜ安全なのか? 静的プレースホルダ(プリペアードステートメント)は、値が入らない状態でデータベースに送り、コンパイルされた後から値を入れて実行されます。2回目も同じSQL文で値だけ違う場合は、値だけをセットし直して実行されます。なので、値が入らない状態でコンパイルされているため、値の操作でSQLの意味を変えることは原理的にできなくなります。
動的プレースホルダは、プログラムソースコード上ではほとんど同じなのですが、内部の動作が異なります、プログラムソース上で呼び出し側で完全な値が入ったSQLを作り、データベースに送りコンパイルして実行します。これはSQLインジェクションにならないのかというと、基本的にはならないです。なぜかというと、内部でSQLインジェクションにならないようライブラリを利用してエスケープ処理を行ってくれます。だから、ライブラリにバグがない限りは安全です。
静的プレースホルダは動的プレースホルダよりやや安全です。ですが、ここはメリット・デメリットで選ぶ必要があります。簡単にまとめると、以下のようになります。
- 文字列連結による組み立ては、アプリケーションの開発者の無知や不注意によりSQLインジェクションの可能性がある
- 動的プレースホルダはライブラリのバグによりSQLインジェクションの可能性がある
- 静的プレースホルダは原理的にSQLインジェクションの可能性がない
ということなので、パフォーマンスか、完全なSQLインジェクション対策をか天秤にかけて、正しく選択する必要がありそうですね。
「htmlspecialchars()やintval()を使っておけば対策されると思ってい良いのでしょうか?」という質問ですが、徳丸さんの答えは「htmlspecialcharsはコマンドインジェクションとは関係ない」です。
コマンドインジェクション対策には、以下のようなものがあります。
- そもそも外部呼び出しを避ける
- 外部由来のデータをsystem関数に指定しない
- 可能であれば、シェルを介在しないコマンド実行を用いる
- system関数等の引数を安全な関数を用いてエスケープ処理をする
たとえば、PHPの外コマンド呼び出しには以下の2種類があります。
- escapeshellarg:使用可能
- escapeshellcmd:使用禁止
このescapeshellcmdが使用禁止になっている理由の解説もありました。シングルクオート(')がペアでなければエスケープするというものもあるため、ペアができているとエスケープされずにパラメータの追加が可能になることもあるらしいです。
ユーザがフォームに入力した文字列を受け取り、
のようにmailコマンドで送信したい、という質問です。escapeshellargを利用することによって、上記のコマンドは安全性を高めることができるようですが、teratailでは別の回答者の方が「popenを使いましょう」と答えています。では、なぜpopenが安全なのでしょうか。
質問にある脆弱なサンプルはsystem関数の引数の中に外部変数があるため、外部変数をエスケープしなければいけません。安全なサンプルは、メール本文の流し込みはfwirteでpopenプロセスを開いて値を入れています。system関数の引数ではエスケープが必要ですが、friwteのときはエスケープ不要な個所にパラメータを代入するため、エスケープを書く必要はないというのが理由です。ともあれ、メールの送信はmail関数やmailライブラリを利用して行うほうが安全ですので、利用できる場合はそちらを利用するようにしましょうということでした。
Fizzbuzzの問題とコピペプログラマ
また、現状コピペで開発するユーザーが多くいる状態を、FizzBuzzの問題とともに解説されました。
多くのエンジニアがFizzbuzzをかけないと話題になりましたが、どうしてそんなことが起きるのでしょう?
信じがたいですが、最近ではコピペでアプリケーションを完成させるプログラマもいるようです。そういったコピペプログラマはパターンマッチで書いているので、パターンから外れると急にFizzbuzzが書けない人が多く居たりします。FizzBuzzは普段のプログラムとは少し外れているので、そういったプログラマが生まれるのでしょう。
ただ、コピペでやることが問題なのではなくて、そこから勉強をしていかないのが問題ですね。そういったプログラマに対しても、Q&A等でしっかりと救いの手を差し伸べてあげることによって、安全なインターネット社会は出来上がるのでしょう。
teratail Q&Aではどうなのか?
セキュリティ系の回答者のレベルは高いように感じます。一方、質問者はセキュリティについての不安はあるがどうするのかわからない方や、残念ながらエスケープの種類が間違っている場合が多いように感じます。少しでも正しい情報を上げることによって、より安全に貢献していくと考えています。
終わりに
徳丸さんは、最後にこうまとめられていました。
- セキュリティで「こうすれば安全」という方法はないので、個別に覚えていくのが結局は早道になる
― ただし「これだけは危険」という方法を覚えておき、避ける必要がある
- 『体系的に学ぶ安全なWebアプリケーションの作り方』を読もう
『体系的に学ぶ安全なWebアプリケーションの作り方』は、現在第二版を執筆中とのこと。待てる方は少し待って第二版を買ってみましょう。第一版でも読むとよりセキュリティ対策について学べるでしょう。