OpenSSL #heartbleed 脆弱性の影響とその後を考えるときのメモ

影響を受けたウェブサイトやソフトウェアの対応については、piyologさんの以下の記事に詳しいです。

この記事では、それ以外の動きについてまとめていきます。先週の2つの記事と違い、あくまで個人的興味によるものです。

日本政府およびセキュリティ関連組織による情報公開

ベンダー等による情報公開

今回の脆弱性に関する考察と議論

代替策に向けた議論

より安全なcipher suiteの検討

これまでのHeartbleed以外のSSL実装のバグ

OpenSSLの脆弱性で想定されるリスク

JVNJPCERT/CCの記事があまりにもさらっと書かれていて、具体的なリスクが想像しづらいと思うので説明します。

今北産業 (今ニュース見て来たから三行で教えて欲しいという人向けのまとめ)

  1. インターネット上の「暗号化」に使われているOpenSSLというソフトウェアが2年間壊れていました。
  2. このソフトウェアは便利なので、FacebookだとかYouTubeだとか、あちこちのウェブサイトで使っていました。
  3. 他の人の入力したIDとかパスワードとかクレカ番号とかを、悪い人が見ることができてしまいます。(実際に漏れてる例)

他にも色々漏れてますが、とりあえずエンジニア以外の人が覚えておくべきはここまででOKです。もう少し分かりやすい情報が以下にあります。

まだ直っていないウェブサイトもあれば、元々壊れていないウェブサイトもあるので、「直したからパスワード変更してね」って言ってきたら変更してください。あと、悪い人が偽メール送ってきているので、メールで連絡してきたとしても、ブラウザのブックマークから開きましょう。

ここからはエンジニア向けの詳細です。

今回の脆弱性の内容

いわゆる「SSL通信」と呼ばれる暗号化通信は、実際にはSSL 3.0とより新しいTLS 1.0〜1.2(互換性のためプロトコル上の番号は3.1〜3.3)というプロトコルで定義されています。これらを使うために、様々なソフトウェアが利用している共通ライブラリが OpenSSL です。今回の脆弱性は、このTLS 1.2に追加されたハートビート拡張*1の実装の不具合によるものです。

SSL通信の最初「ハンドシェーク」段階でSSL証明書の検証などを行いますが、今回の脆弱性はそのハンドシェーク段階の中でも発生する*2ため、正しいSSL証明書を持っていなくても攻撃が成立します。ほとんどの場合にログが残らないのもこのためです。

攻撃者は、通信の相手のメモリ内容を取得することができます。一回で最大64キロバイト、同じプロセス内の情報に限られますが、ログに残らず、めぼしい情報が取得できるまで何度でも繰り返すことができるため(これがメモリガチャと呼ばれる理由)、プロセス内の全ての情報が漏洩したと考えるべきです。

「同じプロセス」というところが問題で、どのプロセスがSSL通信をしたかによってリスクが大きく変わります。

ロードバランサーSSLを提供し、脆弱性があった場合

ロードバランサーSSLのサーバ側として、SSL証明書秘密鍵をメモリ内に保持します。この秘密鍵が攻撃者に漏洩します。

SSL処理をしているところでデータが漏洩した場合のリスクは、大きく三つあります。
(これは、Apache等でSSL処理をしている場合も同様です!)

すぐ直前の通信内容を盗聴されてしまう

空きメモリ内には、直前の通信内容がクリアされず残っている場合があります。SSL処理が終わり復号された平文の通信内容もふくまれるため、以下のような攻撃が成立します。

過去の通信内容を盗聴されてしまう

特に意識せず設定されたSSLサーバの場合、SSL証明書秘密鍵を知っている攻撃者は、暗号化されているはずの通信内容を復号して、内容を見ることができます。

特に、毎年SSL証明書を更新する毎に秘密鍵を作り直していなかった場合、過去の通信内容を保存している組織*3があった場合、過去に遡って全ての通信内容を復号して、そのなかのあらゆる情報を知ることができます。たとえ毎年秘密鍵を生成し直していた場合でも、直近の最大1年間の通信内容は全て筒抜けです。

これに対しては、Forward Secrecyという対策が存在します。

ただし、これまで必要性が重要視されなかった(まさかこれほど大規模に秘密鍵が漏洩する脆弱性が見つかるなんて思われていなかった)、性能が従来のRSAより若干遅い、などの理由があってあまり普及していません。今回を良い機会として、利用している暗号化アルゴリズムを見直し、Forward Secrecyを有効にするのが良いでしょう。Verisignから証明書ビジネスを引き継いだSymantecは以下のように主張しています。

結論として、ウェブサイトが RSA から離れて Forward Secrecy を提供するとともに、極めて低速なDHEよりもECDHE 鍵交換を配備することを、我々は強く推奨します。

誰かに偽のSSLサーバを立てられてしまう

たとえば、sns.example.jp というSNSSSL証明書秘密鍵が漏洩したとします。攻撃者はすかさず、その秘密鍵を使って、sns.example.jp の正当なSSL証明書を持った偽サーバを公開できます。公衆Wifiを使っていたり、DNSへの攻撃を併用すれば、その偽サーバにアクセスさせることができます。

アドレスバーを見ろと言ったところで正しいドメインで鍵が表示されているわけですし、たとえEV-SSL証明書を使ってアドレスバーを緑に染め上げていたとしても、全く同じ証明書でサーバを用意されてしまっては無力です。

これが、脆弱性のあるバージョンを使っていた場合に証明書の再発行だけでなく、「失効」が必要となる理由です。

ApacheやnginxでSSLを提供し、脆弱性があった場合 (APサーバ併用)

アプリケーションサーバの前段にApacheやnginxを設置し、そこでSSL処理を行っていた場合です。

ロードバランサーの場合に加えて、以下の情報が漏洩します。

  • httpd.confや.htaccessで設定されている内容の全て
  • Apacheが読み取っていたhtpasswdファイルの内容

ApacheSSLを提供し、脆弱性があった場合 (mod_php5,mod_perl等)

mod_php5やmod_perlなどを利用し、Apacheモジュールとしてウェブアプリケーションを動かしていた場合です。

これまで登場した全ての内容に加えて、以下の内容が漏洩します。

  • 全てのアプリケーションコード
  • 全てのアプリケーション設定
    • データベース等の接続先、ユーザ名、パスワード
    • TwitterFacebook等のconsumer_secret
    • Rails等のCookieベースセッションの暗号化キー(secret_token, secret_key_base)
  • DB等から取得したメモリ上のデータ全般(別リクエストの内容も、場合によって空きメモリに残っている可能性がある)

ここまでくると、想定されるリスクはかなり大きいです。

  • 別ユーザからのリクエストに含まれた、パスワードや個人情報、クレジットカード番号、セキュリティコードなどが空きメモリに残っていた場合、それが漏洩します。
  • Cookieベースセッションは、自由にセッションデータの捏造ができます。
  • インターネットからDBサーバ等に直接接続ができなくとも、別の攻撃を行うための予備情報を攻撃者に与えることになります。

PHPであっても、PHP-FPMを使うなどApacheとプロセスを分けることで、権限ごと分離できるので、今回を期に挑戦すると良いのでは無いでしょうか*4

外部のサーバ等にSSL通信を行っている場合

SSH系を除くほぼ全てのクライアントアプリケーションの暗号化通信が当てはまりますし、サーバ側であっても、外部のAPIサーバへの通信などにはあてはまります。

あくまでこの場合、攻撃者が用意したサーバに接続しない限り安全であると考えられますが、その一方で、DNSキャッシュポイズニングBGPのハイジャックなど、攻撃する側がトラフィックを呼び込むための手法が存在しています。

これをされた場合、そのクライアントが持っている接続先リストやそこに含まれるユーザー・パスワードが漏洩します。

外部に接続するサーバ側アプリであれば、前述の「ApacheSSLを提供し、脆弱性があった場合 (mod_php5,mod_perl等)」と同等です。例えばSSLサーバとしては社内からの接続しか受けていなかったとしても、抜け道があるので、やはり上げておきましょう、ということです。

まとめ

  • 脆弱性の影響を受けていたら、証明書の「失効」までが必要です。
  • Forward Secrecy が無ければ、秘密鍵の漏洩が通信内容の漏洩に直結します。
  • SSL処理とアプリケーションコードはプロセスを分けよう。

補足情報

あまりにも再現が容易であることから、既に脆弱なサーバの探索が増えているようです。

警察庁の定点観測システムにおいても、9日以降、当該攻撃コードに実装されている
Client Hello パケットと、完全に一致するパケットを多数観測しています。このことから、同攻撃コードを使用して、脆弱性が存在するサーバ等の探索が実施されているものと考えられます。

*1:2012年2月にRFC6520として公開され、PMTU discoveryなどを想定しているようです。

*2:なお、RFC6520としては、ハンドシェーク中のHeartbeatRequestは捨てるべき(SHOULD)となっています。

*3:某国のNなんちゃらとか

*4:実際、OpenSSHにはそうやって権限分離が実装されたという歴史があります。

CVE-2014-0160 OpenSSL Heartbleed 脆弱性まとめ

必要な情報は http://heartbleed.com/ にまとまっているのですが、英語だし長いしって人のために手短にまとめておきます。

どうすればいいのか

  1. OpenSSL 1.0.1〜1.0.1fを使っていなければセーフ
  2. あてはまる場合には、一刻も早くバージョンアップして、サーバごと再起動(わかるひとはサービス単位でもOK、ただしreloadではだめなことも)
  3. SSL証明書でサーバを公開しているなら、秘密鍵から作り直して証明書を再発行し、過去の証明書を失効させる(末尾に関連リンクあり)。
  4. サーバを公開していない場合も、外部へのSSL通信があれば影響を受けるので、詳しく精査する。
  5. PFS(perfect forward secrecy)を利用していない場合、過去の通信内容も復号される可能性があるため、詳しく精査する。

漏洩する情報の具体例は、OpenSSLの脆弱性で想定されるリスクとして別の記事に書きましたのでそちらをご覧くださいまし。

影響を受けるディストリビューション

基本的には、パッケージ更新などでOpenSSLをバージョンアップして、そのあとOpenSSLを読んでいるサービスを再起動します。外部サーバへの接続なども含めるとOpenSSLを利用するサービスが多いので、よく分からない人はサーバごと再起動がおすすめ。

1.0.1からの新機能による脆弱性なので、1.0.0とそれ未満であれば問題ありません。逆に、1.0.1eやそれ以前のバージョン番号のまま脆弱性のみ修正される場合もあるので、OpenSSLのバージョンだけでなく、パッケージのバージョンを確認してください。

ディストリ どうすればいいか
Debian Wheezy (stable)以降 DSA-2896-1
stable: libssl1.0.0 1.0.1e-2+deb7u5 で修正
testing/unstable: libssl1.0.0 1.0.1g-1 で修正
Ubuntu 12.04.4 LTS以降 [USN-2165-1]
12.04: libssl1.0.0 1.0.1-4ubuntu5.12 で修正
12.10 libssl1.0.0 1.0.1c-3ubuntu2.7 で修正
13.10 libssl1.0.0 1.0.1e-3ubuntu1.2 で修正
RHEL 6.5 [RHSA-2014:0376-1]
openssl-1.0.1e-16.el6_5.7で修正
CentOS 6.5 [CESA-2014:0376]
openssl-1.0.1e-16.el6_5.7 で修正
Scientific Linux 6.5 openssl-1.0.1e-16.el6_5.7 で修正
Fedora 18以降 [Bug 1085065]
Fedora 19: openssl-1.0.1e-37.fc19.1 で修正
Fedora 20: openssl-1.0.1e-37.fc20.1 で修正
(Fedora 18はサポート切れのため公式パッケージ無し)
OpenBSD 5.3以降 [Patches for OpenSSL bounds checking bug]
パッチあり: 5.3 / 5.4 / 5.5
FreeBSD ports [[ports] Revision 350548]
security/openssl 1.0.1_10 で修正
FreeBSD 10.0 [FreeBSD-SA-14:06.openssl]
freebsd-updateでバイナリ更新するかpatchあててbuildworld/installworld/reboot
NetBSD pkgsrc [NetBSD Security Advisory 2014-004]
openssl-1.0.1gで修正
NetBSD 6.0以降 [NetBSD Security Advisory 2014-004]
各arch向けにビルド済みバイナリが配布されている。
OpenSUSE 12.2 [openSUSE:Maintenance:2718]
Amazon Linux 2013.03以降 [Thread: openssl heartbleed]
ALAS-2014-320: openssl Security Update - Information Disclosure Vulnerability]
2013.09: openssl-1.0.1e-4.58.amzn1 で対応
2014.03: openssl-1.0.1e-37.66.amzn1.x86_64 で対応
Amazon ELB [HeartBleed Bug Concern]
[AWS Services Updated to Address OpenSSL Vulnerability / 参考和訳]
全てのRegionで対応済み、ただし証明書の更新を推奨
(リスクの度合いは不明)
Amazon CloudFront [AWS Services Updated to Address OpenSSL Vulnerability / 参考和訳]
対応済み、ただし証明書の更新を推奨
(リスクの度合いは不明)
Homebrew [openssl 1.0.1g]
brew update
brew upgrade openssl
brew link openssl --force
(コメ欄にて詳しい手順有り)
Gentoo Linux [GLSA 201404-07 / openssl]
dev-libs/openssl-1.0.1g で修正
BIG-IP [SOL15159: OpenSSL vulnerability CVE-2014-0160]
11.5.0以降が影響を受ける模様
「Use only Native SSL stack ciphers」に設定していれば回避可能
Google Compute Engine [Security Bulletins]
各ディストリの v20140408 以降のイメージで作り直すか、yum/aptでパッケージ更新
Android 4.1.1 [Google Services Updated to Address OpenSSL CVE-2014-0160 (the Heartbleed bug)]
Android 4.1.1のみ影響
ChromeはOpenSSLを使わない(NSS)のため影響外

自前で入れたり、バージョンアップがすぐにできない場合は、「-DOPENSSL_NO_HEARTBEATS」を設定し、OpenSSLをコンパイルしてもよい。

  • 影響を受けない主要ディストリ:
  • 個別のベンダー製品については、各社のURLが以下の記事に集約されているのでこちらを探してください。

その他影響を受けるソフトウェア群

ソフトウェア 対応
Cygwin [Updated: openssl-1.0.1g-1]
openssl-1.0.1g-1 で対応
TeraTerm(TTProxy) 影響無し
当初「SSH部分に影響無し、TTProxyでSSL利用時のみ影響を受ける可能性」と書いていましたが、今のところSSL通信をしないとのこと[出典]
Apache mod_spdy 独自OpenSSLを内部で持つのでv0.9.4.2に上げる(出典)
Phusion Passenger [Phusion Passenger 4.0.41 released, OpenSSL Heartbleed security update]
OpenSSLを静的リンクしており4.0.41未満で影響あり
Ruby RVM [rvm/config/db]
rvm管理下で別にopensslを入れた場合は対応が必要
Ruby rbenv/ruby-build [OpenSSL library updated to 1.0.1g #547]
ruby-buildプラグインで別にopensslを入れた場合は対応が必要
plugins/ruby-build下でgit pullしてrbenv installしなおす
OpenVPN [OpenVPN 2.3.2 Windows build I004 リリース]
[OpenSSLの脆弱性 – Heartbleed について]
Windows版バイナリにOpenSSLが含まれるため、OpenVPN側のバージョンアップも必要。
Android版クライアントもAndroid 4.1(.0)と4.1.1は影響を受けるのでAndroid対応待ち
TLS-Authを使っていなければ影響は無い。TLS-Authを使っていれば影響は無い。
修正(2014/04/10 18:24)
TLS-Authの記述が逆になっていました。TLSハンドシェークの前にHMACによるチェックを挟む機能で、有効にしていれば影響を受けません。こちらの記事に詳しく書かれています。
FFFTP [臨時版 1.98g1 - FFFTP]
FTPS(FTP over TLS/SSL)のみ影響、1.98g1 で対応
WinSCP [OpenSSL vulnerability CVE-2014-0160]
5.5.3で対応予定
通常のSCP/SFTPには無関係、FTP over TLS/SSLを使っている場合のみ影響

関係しそうなサービス

  • HTTPSサーバ (上記mod_spdyにも注意)
  • メールサーバ SMTP(STARTTLS, SMTP over SSL), POP3/IMAP (STARTSSL, POP3/IMAP over SSL)
  • そのほかのSSL/TLSを提供するサーバ
  • 外部HTTPSサイト(外部APIサーバ等)への接続するクライアント、バッチ処理等のプログラム全般
  • FTPSサーバ、クライアント
  • LDAPSで認証もらってる
  • MySQLPostgreSQLへの接続を暗号化

何が起きているの

OpenSSLに脆弱性があり、SSLで通信している相手のメモリを閲覧できます*1
具体的には、以下のようなシナリオが考えられます。

  • 攻撃者がクライアント側の場合、 HTTPSサーバのSSL証明書秘密鍵が漏洩する。
  • 攻撃者がクライアント側の場合、 HTTPSサーバのプロセス内にあるユーザー情報が漏洩する。特にApache+mod_php5のコンボでは、ウェブアプリ内の全ての情報が見られる可能性がある。
  • 攻撃者がサーバ側の場合、HTTPSで外部のAPIサーバ等に接続したアプリケーションが、メモリ内にあるユーザー情報等が漏洩する。
    • これが危険になるケース: HTTPSのCommonNameの検証をきちんと行っていない場合や、審査が緩いCAが同じ名前でSSL証明書を発行した場合、外部APIサーバのSSL証明書秘密鍵が奪取された場合など
    • 04/08 22:34追記: そもそもハンドシェークフェーズなので証明書の検証は無関係でした。DNS毒入れ等の併用で攻撃者のSSLサーバに接続させられてしまえば無条件でやられます。
  • 攻撃された記録は一切残らない。安全側に倒すならば、脆弱性のあるバージョンをインターネットに公開していたら攻略されたとみなす方が良い。

SSL証明書等の扱い

脆弱性のあるバージョンを使っていた場合、最悪でSSL証明書秘密鍵が奪取されている可能性があります。この場合、現在使っているSSL証明書を失効(revoke)させ、再発行する必要があります。

証明書の失効方法については、SSL証明書を発行したCAに問い合わせてください。CA側で管理する証明書失効リストに掲載され、漏洩した秘密鍵によるSSL証明書が使えないようになります。例えばSymantec(旧Verisign)であればストアフロントから失効申請ができるようです。

直接の関係が無いもの

  • あくまでSSL通信(TLSプロトコル)上の脆弱性のため、OpenSSLライブラリを使っていても、OpenSSHやGnuPG(PGP)などには直接は関係しません。ただし、OpenSSHがLDAP等で外部へのSSL通信を行う場合、そこから攻撃される可能性が残ってはいます。
  • 読み取られるのは、SSL通信を行っているプロセスのメモリ内容のため、ロードバランサや前段のApache等でSSL処理を行っている場合は、別プロセスのアプリケーションサーバ内のメモリ内容は漏洩しません。ただし、前述の通り外部へのSSL通信を行っている場合は別です。
    • curlコマンドを別プロセスで起動している場合*2や、OpenSSL以外のライブラリを使っている場合も無関係です。
  • そのプロセスが読んでいない無関係なファイル等は、たとえそのプロセスの実行ユーザにパーミッションが開かれていても漏洩しません。あくまでプロセスのメモリ内容のみです。

関連URL

*1:64KB単位で何度でも繰り返せる。要するにSR双葉杏が引けるまで何度でも引けるガチャ。

*2:RHEL6/CentOS6系のcurlはOpenSSLのかわりにNSSを使っているため影響を受けないようです。

常設GrowthForecastでお手軽メトリクス監視

この記事はおひとりさま Monitoring Advent Calendar 2013の1日目です!

「あの値とその値の変化を一週間だけ取ってグラフで見たい!」
そんなことってありませんか?自分はまれによくあります。

そんな貴方におすすめなのが、この記事で紹介する常設GrwothForecastとgrouthforecast-workerの組み合わせです。慣れると、必要になってから5分もあれば値の取得が始められるようになります。

GrowthForecastのインストール

GrowthForecastは単体でWebサーバとして起動して、放り込んだ値をうまいことグラフ化してくれるソフトウェアです。インストールの方法は、公式サイトCentOSUbuntuでそのまま使えるコマンドで書いてあります。

デフォルトではTCP5125番ポートで起動するので、ファイアウォールで開けるなりリバースプロキシするなりしておきます。

API投稿をlocalhostなどに制限するallow-fromオプション等もあるので、慣れてきたらそのあたりも調整していくと良いと思います。

grouthforecast-workerのインストール

次に、grouthforecast-workerをインストールします。

githubにありますが、必要となるのはPerlスクリプト1ファイルです。wgetなどで取得して実行権限を立ててください。また、後ほど使うmetrics_scriptという空ディレクトリもここで用意しておきます。

% cpanm -n Path::Class
% wget https://raw.github.com/yappo/grouthforecast-worker/master/grouthforecast.pl
% chmod +x grouthforecast.pl
% mkdir metrics_script

スクリプト内の二箇所を変更します。

# GrowthForecast の /api の URL
my $growthforecast_endpoint = 'http://localhost:5125/api';

# グラフをどのサービスに属するかを example の変わりに入れてね
my $service_name = 'metrics';

もうひとつ、起動スクリプトを用意します。とりあえずfetch.shとでもしておきます。

#!/bin/bash
$(dirname $0)/grouthforecast.pl $(dirname $0)/metrics_script

この起動スクリプトをcrontabに登録します。

 */5 * * * * /(ファイルを置いたパス)/fetch.sh

ここまでを「常設GrowthForecast環境」として、暇なときにでも、お手元にあるroot権限があるVPSなどにいれて立ち上げっぱなしにしておきましょう。

グラフを書きたい衝動が抑えきれなくなったとき

グラフにしたい数字をただ表示するだけのプログラムを作ります。シェルスクリプトでもRubyでもPerlでもPHPでもJavaでもなんでもかまいません。とにかく数字を表示してください。

たとえば、DBにクエリを投げてユーザ数を取りたければこんな感じです*1

#!/bin/bash

echo 'SELECT COUNT(*) FROM users;' | sqlite3 /foo/bar/foobar.db

もうちょっと複雑な例として、先日行われたANIMAX MUSIX三森すずこチャリティオークションの落札金額であれば、スクレイピングしてくるスクリプトをこんな感じで用意すれば良いです。ほとんど、どっかに転がっているサンプルコードそのままです*2

#!/usr/bin/env perl

use strict;
use warnings;

use Web::Scraper;
use Data::Dumper;
use URI;

my $uri = new URI('http://www.pashaoku.jp/auction/detail/34875092');

my $scraper = scraper {
    process '.itemAuctionInfoData', 'yen' => 'TEXT';
};

my $res = $scraper->scrape($uri);
if ($res->{yen} =~ m/([0-9,]+)$/) {
    my $yen = $1;
    $yen =~ tr/,//d;
    print $yen;
}

print "\n";

この例だと変数 $uri を変更したファイルをいくつか用意しておけば*3、複数のオークションの価格を同時に比較することができます。

実行したら数字を表示するプログラムが用意できたら、先ほどこっそりつくっておいた metrics_script ディレクトリに、さらにもう一階層切ってから放り込みます。実行権限も付けておいてください。

grouthforecast.pl
+ metrics_script
  + db
    + users (DBからのユーザ数取得プログラム)
  + auction
    + mimorin (オークション価格取得プログラム)

はい、これでおしまいです。

10分ぐらい待てば値を2回取ってきて最低限のデータが揃うので、早速見てみましょう。

http://(サーバーのホスト名やIPアドレス):5125/

一覧ですね。それぞれクリックすればグラフが見られます。

ね、簡単でしょ?

grouthforecast.plで設定した$service_nameと、スクリプトディレクトリ、ファイル名がそのままグラフのパスになります。とにかく値さえ取っておきさえすれば、あとからAPIドキュメントにあるとおりグラフのURLをいじってあげると、複合グラフや期間指定も自由自在です。


あとかたづけ

必要が無くなったら、実行権限を落としておきましょう。

% chmod -x metrics/auction/mimorin

今年のANIMAX MUSIXは、まめぐがとても良かったです。

*1:参考までに、MySQLなら--defaults-fileでオプションファイルを利用するとコマンドラインにパスワード書かずにすみます。http://dev.mysql.com/doc/refman/5.1/ja/option-files.html

*2:Web::Scraperモジュールが必要なので、cpanm -n Web::Scraper などしてください。

*3:もちろんDRYではありませんが……

SQLでエスケープなんてしたら負けかなと思ってる。

SQLへの安全な値の埋め込み方について、ここ数日で色々議論というか意見の投げ合いがありましたが、自分としての考えをまとめておきます。

1. SQLに値を埋め込む場合は、プリペアドステートメントプレースホルダを必ず使う

新規に開発する場合、99%以上はこれで十分なはずです。

値を埋め込まない静的SQLのみで、RDBMSAPIとして提供しているプレースホルダだけを使っている限り、絶対に安全です。もしそうでないとしたらそれはRDBMS脆弱性です。

ぱっと見プレースホルダっぽく見えて、ライブラリの中で値を埋め込んでRDBMSに投げている実装がたまにあるようなので、そういう場合はそこに脆弱性が存在する可能性が高まります。そのあたりは自分の使うライブラリやフレームワークの情報を収集してください。有名なところだと、PHPのPDOが提供する「動的プレースホルダ」がこのような実装になっていて、実際にShift_JISを利用した場合に脆弱性がありました。

なお、全てのRDBMSで使えるかどうかは分かりませんが、ストアドプロシージャを利用することで、テーブル名等についても展開が可能です。

ただし実装が不必要に複雑になったり、ストアドプロシージャが様々な理由で使えない場合もあるかと思います。そこで。

2. 文字種が判明している文字列のみ、テーブル名など限られた場所でSQL文字列に直接埋め込んで良いものとする

プレースホルダが利用できない(しづらい)状況として、日付や何らかの識別子をテーブル名(やパーティション名)に使わなくてはいけない場合があります。また、ソート順のORDERにカラム名を入れる場合などもあります。

その場合は、自分で生成した日付や、マスタデータとして持つ文字列など、文字種が完全に固定されたもののみを組み合わせるのであれば、その生成処理が正しく動いている限りリスクは。エスケープの必要の無い文字種のみを使うような設計をすれば良いです。

WordPressMovableType等でつかわれている「テーブル名プレフィックスという外部入力値」については、設計として「英数字とアンダースコアだけを使う」と決め、その通りに入力バリデーションを実施していれば、やはりエスケープの必要は無いはずです。

安全な設計をしましょう。

3. RDBMSの管理ツールなどの開発をしている場合は頑張ってエスケープする

RDBMSの管理ツールなどをあなたが実装しているのであれば、ユーザがうっかり作ってしまった記号まみれのテーブル名に対してもアクセスできないといけないかもしれません。

おめでとうございます!

ここまできて初めてエスケープが必要となります。

個人的には、RDBMSエスケープAPIや既存のライブラリを用いて「プレースホルダに値をエスケープして埋め込む」という関数をきっちり作り込み、そこに集約させるのが良いのでは無いかな、と思います。

決して、エスケープ処理自身を一から自分で書こうだなんて恐ろしいことを考えないでください。

その必要があるのは、あなたがRDBMS自身やそのライブラリの開発者の場合だけです。

4. レガシーコードのメンテをしている人も頑張ってなんとかする

SQL文字列に直接値をエスケープして埋め込んだコードが既にあり、修正する手間を掛けられない人。

正しいエスケープでもプレースホルダの利用に切り替えるでもリスクの許容でも好きな手法をお選びください。

エスケープする場合は上に書いたとおりです。

まとめ: 静的SQLプレースホルダを原則とし、エスケープが必要な場面そのものを可能な限り減らせ

アプリケーション開発者にとって、SQLインジェクション対策において必要な知識は、これに尽きると思います。

こちらの記事で指摘されるようなプリペアドステートメントのデメリットはありますが、「静的SQLプレースホルダ」の安全性と比較してもなお動的SQLが必要となる状況であれば、設計を見直すべきだと考えます*1

補足

SQLに限らず「エスケープは難しい」という認識は、全てのエンジニアが持っているべきです。

その認識が無いからこそ、プレースホルダが使える状況にもかかわらず動的にSQLを組み立ててしまうエンジニアが後を絶ちません。

補足2

大事なことを書き忘れましたが、SQL文法上の「エスケープ」の話です。

例えばLIKE句に食わせる文字列値の中の % や正規表現のメタ文字など、「文字列の中身」で必要なエスケープはもちろん必要ですし、そこから先こそこそがアプリケーションの責務だと考えます。もちろん「エスケープが必要な場面そのものを可能なかぎり減らせ」という主張は変わりません。

理想を言えば、LIKE句や正規表現などエスケープが必要な処理全てに対応する組み込み関数や、先頭一致を示すオペレータがRDBMS側で用意されていると、アプリケーション側でのエスケープが必要なくなり、より安全になるのではないかなと考えています。

*1:このあたりはさすがに宗教かなとは思います。

「メールアドレスのルール」なんて使ってはいけない3つの理由

定期的に繰り返される話題ですがまた盛り上がっているのできちんと書いておきます。

「通るべきメールアドレスが弾かれると激おこ」という前提で話を進めます。

問題点1. メールアドレスに関して、RFCなんてそもそも守られていない

2009年以前に登録されたDoCoMo携帯のメールアドレスなど、quoted-stringじゃないのにピリオド連続するものが実在している以上、彼らを許容するべきです。

今そこにある実装 >>(越えられない壁)>> RFC です。

問題点2. メールアドレスの国際化

@の左側(addr-spec)でUTF-8を利用できるようにするRFC5335が発行されています。これにより、通すべき文字が一気に増えます。

通常、電子メールアドレスは「ユーザー名@ドメイン名」という形式で表されます。そのうち、@の右側の「ドメイン名」の国際化については、国際化ドメイン名(IDN)として、既に標準規格が定められています。今回の国際化電子メールアドレスは、@の左側の「ローカルパート」と呼ばれる部分も含んだメールアドレス全体の国際化を行い、電子メールアドレスの任意の部分に、日本語を含むさまざまな言語の文字を使用可能にするための技術です。

また、そもそもドメイン部はとっくに国際化されています。こちらはPunycodeでデコードされた「xn--***.jp」というドメインを使えばいいわけですが、真にユーザーの利便性を考えるなら国際化ドメインを直接入力できるべきでしょうね。ははは。

結局のところ「メールアドレスのルール」というのが現実としてどんどん変わっていく以上、これを元にバリデーションを掛けるのは人類にはまだ早すぎます。

問題点3. そもそも役に立たない

で、そんな厳しくバリデーションしたところで、「メールアドレスのルール」を逸脱しない範囲での打ち間違えの方が多いです(例: foobar@example.jpo)。

TLDホワイトリストなんて新gTLDがぽこぽこ量産される時代に持っちゃいけません。

解決策: じゃあどうすればいいの

推奨: 実際にメールを送る

これが一番早いです。余程偏った利用者層でない限り、メール内のURLクリックというインターフェースにユーザーも慣れています。

ちなみに本件とは別ですが、メールアドレスの打ち間違えで別の人に届くリスクがあるので、メール内のURLクリック後に無条件にログイン状態にしてしまうと危険だったりします。

消極策: いわゆる「普通に使えるべき記号」までを許容する

「"」でくるむquoted-stringなんて使っている人ほとんど居ないのでその部分は忘れ、普通に使う事ができる記号「!#$%&'*+-/=?^_`{|}~.」は受け取るようにします。

これでほとんどの人が救済される上、バリデーション部分の正規表現の変更のみと変更コストが最も低いです。ただ、当然ながらRFCに準拠して作られたマニアックなメールアドレスは受け取れません。

頑張って理想のバリデーションを実装する

上にも書いたとおり仕様や現実が変わっていく以上大変ですが、それに見あう効果があるかというと自分は見あわないと思います。

とはいえ、技術的トライアルとしては楽しそうなので、だれかやってください(他力本願)。

DNSを引く

メールは送りたくないが、せめてドメイン部は、という事であればDNSのMXレコードとAレコードを引きましょう。

当然ながら、@の左側の打ち間違えには無力ですし、フィッシング用の偽ドメインが存在してしまう場合もあります。

非推奨: SMTPで接続してRCPTまでやってみる

実際にDNSを引いて調べた相手先メールサーバにSMTPで接続し、RCPTコマンドを送るところまでやってみることで、存在しないユーザーであればエラーを返してくれる場合が多いです。

ですが、DNSを引くのに比べてずっと時間が掛かるのでウェブアプリの1リクエスト中では厳しくなります。

昨今では、エラーを返さず受け取るだけ受け取って迷惑メール事業者フラグを立てるという実装もありそうな気がしますし、そもそも相手のメールサーバにメールを送らないのに無駄に接続する以上、負荷を掛ける行為にもなりかねません。

というわけで、理論上はできそうと言うだけで、やらない方が良いでしょう。非推奨ということで敢えて入れました。

まとめ

メールアドレスの確認はそこにメールを送る事で確認を行いましょう。

入力フォーム段階でのバリデーションは、フィールドの入れ間違い等への対策として「@」や「.」を含むどうか程度に留めべきです。


補足: 重要なのは「その仕様で誰が得をするか」をよく考えましょうってことです。どうしてもバリデーションだけで頑張りたいのであれば、""なしで使える記号を許容すると、対応が楽な割に救済される人が一気に増えます。逆に、絶対に厳密な入力制限が必要な場合は、死ぬ気で頑張るしかないでしょう。

http://anond.hatelabo.jp/20131110022429 無能なプログラマに32の質問

プログラマじゃ無くてエンジニアだけど。

  • ◎ 技術書を買っただけで満足するw
  • ○ ブクマするだけで理解した気、分かった気になっているw
  • △ 勉強会(笑)には参加するが復習も実践もしないw
  • ○ 一つの言語を使い込めてないのに複数言語に手を出すw
  • ○ 流行りの技術に飛びつくけど直に飽きるw
  • ○ 専門と断言できる技術領域がないw
  • × VisualStudioを貶す割には、パフォーマンス分析とかテストなどの便利機能は使えないw
  • × WPFが分からないだけなのに、自前で作る方が偉いと思っているw
    • そもそも分かる分からない以前にWPF使ったこと無い
  • × オーバーヘッドやフットプリントなどデメリットを考えず、すぐにtemplateとか純粋仮想関数を使って可読性を落とすw
  • × オブジェクト指向/デザインパタンを何か特別の技術だと思っているw
  • × 無駄なところにラムダ式を使うw
  • △ メモリ使用量や計算量の予測ができないw
    • できてるかっつーと自信ない
  • × 最大負荷を予測した上で始めから対策を取った実装が出来ないw
    • 最大負荷を予測できない市場に居りますので。。。
  • × HHKでないと仕事できないw
  • ○ とりあえずVim(笑)を使うw
  • × 用途もないのにマックブックプロを買ってみるw
  • ○ 最新のCPU買わせといて並列処理が出来ない部類w
  • △ 技術書を英語で読めないw
  • ○ 英語で会話できない、最低ラインといっていいTOEICで800すら取れないw
  • × ただの行列の掛け算程度で数学使ってる気になってるw
  • ○ 使用してる数式の意味まで理解しないw
  • ○ なんで動いてるのか分からない/動けばいい、と言ったことがあるw
  • △ 使用してるライブラリのコードが読める状態でも見ようとしないw
    • 困るまでは読まないね。
  • × 公式ドキュメントを読まずにググって素人のいい加減なブログを信用するw
  • × 技術書/参考書/論文を読まずに、素人のいい加減なブログからコピペしたりするw
  • △ 技術情報の検索を日本語でやるw
    • 最近はとりえあず最初日本語で突っ込んでも、勝手に同義語検索かかって英語記事出てきたりするんやで。
  • × 仕事以外の時間に勉強しないw
  • ◎ 疲れてるからと休日は昼すぎまで寝ているw
  • × 根拠も外資の勤務経験もなく、海外のほうがエンジニアのキャリアパスがある、と信じて日本Dis w
  • ダイクストラ/フロイドウォーシャル/ベルマンフォード の区別を咄嗟に説明できないなど、基本的データ構造とアルゴリズムをしっかり勉強してないw
    • グラフ理論の教科書とか買って持ってるけど、困った時に読めば良いと思ってる。
  • ○ プロコンなどで基本的なデータ構造とアルゴリズムを実践的に使ってないw
    • ライブラリ任せですしおすし
  • × 海外支社やアウトソーシングもなく、100人も居ない会社で、コード規約(笑)とか言い始めるw
  • 合計:
    • ◎2
    • ○11
    • △5
    • ×14

なんか質問一個目で出落ちてる気もする。