SyntaxHighlighter

2011年9月30日金曜日

局所的な視点でコードの正当性を確認できるように書く

古い記事ですが、いろいろとたどっていて見つけて、目からうろこでした。

「サニタイズ言うなキャンペーン」とは何か(高木浩光@自宅の日記)

PHP のスクリプトで、出力の際に htmlspecialchars 関数を通していないサンプルスクリプトを例に出し、以下のように仰ってます。

たしかにこれは脆弱性ではない。プログラムの全体を精査すれば、「$row['id']」の値が数値しかとらないことを確認できるだろうし、「$row['postdate']」の値が日付を表す文字列しかとらず、HTTPリクエストの値(CGI入力)に依存していない式だとということも確認できるのだろう。
だが、そのような確認作業をしないと正しいコードかどうかわからないような、プログラムの書き方をすべきでない。セキュリティ云々以前に、プログラムの開発方法論として、できるだけ局所的な視点でコードの正当性を確認できるように書くのが、近代プログラミングの基本だ。つまり、このコード断片だけ見て、問題がないとわかるように書くべきである。

ごもっとも。耳が痛いです。私はこれまで、入力の段階でチェック済みの変数に対して、出力時には特になにもしない、というようなコーディングを結構してしまってました。改めることにします。

2011年9月29日木曜日

perl スクリプトは EUC-JP で書くべきか? UTF-8 で書くべきか?

結論から言えば、専用のアプリケーションを新規で書くなら UTF-8、既存環境や不特定多数の環境を対象とした汎用的なスクリプトを書くなら EUC-JP ですね。

手元の環境(perl 5.8.4)の $ perldoc utf8 を見てみると、以下の文があります。

in future we would like to standardize on the UTF-8 encoding for source text

今後は UTF-8 が標準になっていくのは間違いないでしょう。
どうしても他の文字コードを使わなければならない理由が無ければ、 特定の環境でのみ動作すればよい新規アプリケーションの作成は UTF-8 一択です。

UTF-8 が perl で完全にサポートされたのはバージョン 5.8 からで、それは 2002年7月にリリースされています。 それ以前の perl では Encode.pm も同梱されておらず、UTF-8 を満足に扱うことができません。 ですが、その世代の古い perl しか入っていないサーバもまだ現役でたくさん存在しているので、利用者の環境を特定しない汎用的なスクリプトにはまだまだ UTF-8 は使えないのです。 (例えば Solaris 9 の perl は 5.6 ですので、Solaris 9 と 10 が混在しているような環境では UTF-8 で運用スクリプトを書くわけにはいきません)
UTF-8 以外で、perl で日本語を使うなら、EUC-JP 一択です。 その理由などは大崎さんの Perlメモ が詳しいのでそちらをどうぞ。

2011年9月28日水曜日

Digest 認証のかけ方 (Apache)

/usr/local/apache が Apache のインストールディレクトリという前提。 また、mod_auth_digest モジュールが組み込まれていることが前提。 設定方法は一部を除いて 2.0.X, 2.2.X 共通(後述)。設定方法は Basic 認証と共通の部分も多いので差異だけを説明する。

まずは認証を通過させるユーザとパスワードのリストを作成する。htpasswd コマンドではなく htdigest コマンドを使用する。

# /usr/local/apach/bin/htdigest -c /usr/local/apach/conf/.htdigest "members only" ユーザ名
Adding password for ユーザ名 in realm members only.
New password:パスワードを入力
Re-type new password:パスワードを入力

-c は初回限定で付ける。追加登録時には -c は付けない。
/usr/local/apach/conf/.htdigest は作成するファイルのフルパスで、この通りでなくとも良いが、DocumentRoot 以下には置かないこと。また、ファイル名の .ht の部分は変えないことを推奨。先頭が . のファイルは Unix 系 OS では隠しファイルとして扱われ、-a なしの ls コマンドでは表示されないというメリットがある。また、.ht で始まるファイルは Apache の設定ファイルでデフォルトでアクセスを拒否する設定になっている。
"members only" の部分は任意に変えて良いが、後述の AuthName ディレクティブと合わせる必要があるので注意。
ユーザ名 の部分は実際のユーザ名に置き換えること。OS に存在するアカウントである必要はない。

Apache の設定は以下の通り。Basic 認証との差は AuthType を Digest にするだけ。

AuthType Digest
AuthName "members only"
AuthUserFile /usr/local/apach/conf/.htdigest
Require valid-user

AuthUserFile は 2.2.X の場合。 2.0.X の場合は代わりに AuthDigestFile と記載すること。

2011年9月27日火曜日

Basic 認証のかけ方 (Apache)

/usr/local/apache が Apache のインストールディレクトリという前提。設定方法は 2.0.X, 2.2.X 共通。

まずは認証を通過させるユーザとパスワードのリストを作成する。

# /usr/local/apach/bin/htpasswd -c /usr/local/apach/conf/.htbasic ユーザ名
New password:パスワードを入力
Re-type new password:パスワードを入力
Adding password for user ユーザ名

-c は初回限定で付ける。追加登録時には -c は付けない。
/usr/local/apach/conf/.htbasic は作成するファイルのフルパスで、この通りでなくとも良いが、DocumentRoot 以下には置かないこと。また、ファイル名の .ht の部分は変えないことを推奨。先頭が . のファイルは Unix 系 OS では隠しファイルとして扱われ、-a なしの ls コマンドでは表示されないというメリットがある。また、.ht で始まるファイルは Apache の設定ファイルでデフォルトでアクセスを拒否する設定になっている。
ユーザ名 の部分は実際のユーザ名に置き換えること。OS に存在するアカウントである必要はない。

次に Apache の設定。メインの設定ファイルに直接記述する方法と、アクセスファイルを使う方法がある。
メインの設定ファイルに記述する場合のデメリットは1つだけで、編集後に Apache の再起動が必要になること。 アクセス制限をかけたいディレクトリがころころ変わったり増えたりする場合や、レンタルサーバ等でメインの設定ファイルを書き換える権限がない場合にはアクセスファイルを使用することになる。ただ、メインの設定ファイルを編集する方法に比べ、セキュリティ的にも劣るし、パフォーマンスも悪くなる

メインの設定ファイルを使用する場合は <Directory 制限をかけたいパス></Directory> の間に以下の記述を追加する。

AuthType Basic
AuthName "members only"
AuthUserFile /usr/local/apach/conf/.htbasic
Require valid-user

"members only" の部分は任意に変えて良い。認証用のダイアログボックスに表示される。
/usr/local/apach/conf/.htbasic は htpasswd で作成したファイルのフルパス。
Apache を再起動すれば設定が有効になり、認証を求められるようになる。

アクセスファイルを使用する場合は、まず最初にメインの設定ファイルの <Directory 制限をかけたいディレクトリを含むパス></Directory> の間で AllowOverride AuthConfig を記載する。(既に AllowOverride ディレクティブがある場合は、値が None であれば AuthConfig に変更。 それ以外の値の場合はスペースで区切って AuthConfig を追加)
Apache を再起動すれば、アクセスファイル(デフォルトでは .htaccess。AccessFileName ディレクティブで変更可能)が使用可能になる。
制限をかけたいディレクトリに .htaccess というファイルを作り、先程の内容(AuthType Basic~Require valid-user)を記載する。 ファイルを作成すれば即有効になる。

2011年9月26日月曜日

Basic 認証と Digest 認証の比較 (Apache)

Basic 認証
  • HTTP 1.0 から利用可能
  • 対応ブラウザの制限は実質的にない
  • アカウントとパスワードは base64 エンコードで送信される(暗号化ではない)ので万が一通信経路で傍受されると容易に漏洩してしまう
  • Apache の場合デフォルトで利用可能
Digest 認証
  • HTTP 1.1 から利用可能
  • 対応ブラウザが限られる(最新のブラウザであればほぼ問題ないが、古いブラウザでは未対応であったりバグがあったりする)
  • チャレンジ/レスポンス方式なので万が一通信経路で傍受されても漏洩する可能性は低い
  • Apache の場合、利用するには mod_auth_digest モジュールを有効にしてコンパイルする必要がある(ソースからコンパイルする場合。OS同梱のパッケージなどでは予め有効になっていることもある。)

推奨:対象ブラウザを制限できるなら Digest 認証、できないなら Basic 認証+SSL。

2011年9月25日日曜日

ブラウザから送られてくるフォームデータの文字コードを検証する

ブラウザから送られてくるフォームデータの文字コードは何になるのか。

軽くググってみた限りでは、そのフォームがあるHTMLファイルの文字コードになることが暗黙の了解的に書かれていて、(常識だからなのか?)そういう仕様である、と明記してあるものは見つけられませんでした。これ以上ググるのもめんどくさいので、自分で検証してみました。

検証用サイト(別窓で開きます)

検証用サイトは PHP で作りました。
フォームの HTML は ASCII のみで記載し、メタタグの charset 指定をリンクで切り替える仕様になっています。 textarea に日本語を記載して submit すると、mb_detect_encoding 関数で判定した結果を画面表示します。

試した環境は、Windows XP の IE 8 と Firefox 6.0.2 。
結果は以下の通り。まぁ予想通りですね。

  • メタタグで charset 指定している場合は、その文字コードで送信される
  • メタタグに charset 指定がない場合は、Shift_JIS で送信される(OSのデフォルト文字コード?Mac や Linux ではどうなるのか?誰か試して結果を教えてください・・・)
  • メタタグの charset 指定を無視して手動でブラウザの表示文字コードを変えると、変更後の文字コードで送信される

ちなみに上記の検証用サイトは、今回の検証の趣旨とは関係ないですが、mb_detect_encoding の誤検知っぷりテストにも使えます。フォームの charset を EUC-JP にして、textarea に 製造 と入れて submit すると、UTF-8 と表示されます。 やはりセキュアなアプリを目指すには、自動検出はしてはいけないですね。 きちんと charset 指定していれば、その文字コードで送られてくると確認できたので、正規のフォームから送られてくるデータを想定した場合、自動検出を使う必要はない。百害あって一利なしです。

※異なる環境で試していただけた方いましたら、是非コメントでお知らせください。

2011年9月24日土曜日

Apache の既存環境調査

まずプロセスを見る。(プロセス表示のコマンドはOSによってオプションとかいろいろ異なるので勝手に読み替えて)

# /usr/ucb/ps auxww | grep httpd | grep -v grep
root       803  0.1  0.4 5308 2056 ?        S 15:14:35  0:00 /usr/local/apache2064/bin/httpd -k start
nobody     805  0.0  0.3 5332 1308 ?        S 15:14:35  0:00 /usr/local/apache2064/bin/httpd -k start
nobody     807  0.0  0.3 5332 1308 ?        S 15:14:35  0:00 /usr/local/apache2064/bin/httpd -k start
nobody     808  0.0  0.3 5332 1308 ?        S 15:14:35  0:00 /usr/local/apache2064/bin/httpd -k start
nobody     804  0.0  0.3 5332 1308 ?        S 15:14:35  0:00 /usr/local/apache2064/bin/httpd -k start
nobody     806  0.0  0.3 5332 1308 ?        S 15:14:35  0:00 /usr/local/apache2064/bin/httpd -k start

上記の例の場合、/usr/local/apache2064 が Apache のインストールディレクトリ。-f オプションがないので、設定ファイルはデフォルトの、インストールパス/conf/httpd.conf (例の場合 /usr/local/apache2064/conf/httpd.conf)が読み込まれている。-f オプションがある場合は、その直後にあるパスの設定ファイルを読み込んでいる。
設定ファイルのパスがわかればあとはそれを見ればほとんどの環境調査は可能なはず。 Listen ポート、DocumentRoot、ログの場所などは全て設定ファイルを見ればわかる。

設定ファイルではわからないが重要な情報としてはバージョンと静的に組み込まれたモジュールの情報ぐらいか。

バージョンの確認方法は httpd に -v または -V オプションを付けて実行する(ラージVのほうがちょっとだけ詳しい)。 httpd が起動していても停止していても関係なく実行でき、起動中の httpd に影響が出ることもない。

# /usr/local/apache2064/bin/httpd -v
Server version: Apache/2.0.64
Server built:   Aug 31 2011 20:09:15
# /usr/local/apache2064/bin/httpd -V
Server version: Apache/2.0.64
Server built:   Aug 31 2011 20:09:15
Server's Module Magic Number: 20020903:14
Server loaded:  APR 0.9.19, APR-UTIL 0.9.19
Compiled using: APR 0.9.19, APR-UTIL 0.9.19
Architecture:   32-bit
Server compiled with....
-D APACHE_MPM_DIR="server/mpm/prefork"
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_FCNTL_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D HTTPD_ROOT="/usr/local/apache2064"
-D SUEXEC_BIN="/usr/local/apache2064/bin/suexec"
-D DEFAULT_PIDLOG="logs/httpd.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_LOCKFILE="logs/accept.lock"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="conf/mime.types"
-D SERVER_CONFIG_FILE="conf/httpd.conf"

モジュールは httpd -l で確認できる。これも httpd の起動状態に関係なく、また影響を及ぼすこともなく実行できる。

# /usr/local/apache2064/bin/httpd -l
Compiled in modules:
core.c
mod_access.c
mod_auth.c
mod_include.c
mod_log_config.c
mod_env.c
mod_setenvif.c
prefork.c
http_core.c
mod_mime.c
mod_status.c
mod_autoindex.c
mod_asis.c
mod_cgi.c
mod_negotiation.c
mod_dir.c
mod_imap.c
mod_actions.c
mod_userdir.c
mod_alias.c
mod_rewrite.c
mod_so.c

ここで表示されるのは、Apache に静的に組み込まれているモジュール、つまりコンパイル時に --enable-module されたモジュール。モジュールを追加したい場合には、Apache の再コンパイルが必要になる。ただし、mod_so が組み込まれていれば、Apache を再コンパイルすることなく動的にモジュールを追加することができる。
mod_so の機能を使って動的に追加されたモジュールはここには表示されず、何が組み込まれているのかは設定ファイルの LoadModule ディレクティブを見ればわかる。

2011年9月22日木曜日

[書籍紹介] 体系的に学ぶ 安全なWebアプリケーションの作り方

素晴らしい の一言。

Webアプリケーションの実装に伴って発生する多数の脆弱性、その原因、影響、対策を1つ1つ丁寧に、具体的に、PHP のサンプルコード付きで解説しており(perl や Java のサンプルもちょこちょこあります)、これ一冊で『安全なWebアプリケーションの作り方』をまさに『体系的に学ぶ』ことができます。看板に偽りなし。難解な解説で初心者を煙に巻くこともなく、嘘や誤解を招くような描写もありません。付属CDには検証用に環境構築済みのVMまで用意されているという至れり尽くせりっぷり。
Webアプリケーションの開発に関わる方は絶対に絶対に買って損はありません。仮に書かれている内容を全て知っていたとしても、これほど丁寧に、具体的に、その知っている内容を誰かに教えることができるでしょうか?これほどまでに体系的にまとまった文献が他にあったでしょうか?PGを養成する学校や、開発会社の研修では必須講読書にしてくれればもっと世界が平和になると思います。;-)

9/28 からは電子書籍(PDF)での販売も始まるそうです。
「安全なWebアプリケーションの作り方」電子書籍版9月28日(水)販売開始します

CLI で便利な Ctrl キー

bash前提。

Ctrl+aコマンドラインの行頭に移動。
history から長い引数のコマンドを再利用するときに行頭のコマンドだけ書き換えたい時とか便利。
Ctrl+eコマンドラインの行末に移動。
Ctrl+a で行頭をいじった後に行末にも何か追加修正したくなった時とか便利。
Ctrl+uカーソル位置から左側にある文字列を全て削除。
パスワード入力を間違った(かもしれない)時によく使う。
Ctrl+rhistory から検索できる。
Ctrl+l画面クリア。Windows の CLS コマンドと同じ。
Ctrl+dコマンドラインで、何も入力していない状態で押下するとログアウト。コマンド入力途中で押下するとカーソル位置の文字を1文字 delete。標準入力中に押下すると標準入力の終了を意味する。
Ctrl+c実行中コマンドの強制終了。
Ctrl+z実行中コマンドを中断してバックグラウンドにまわす。
jobs コマンドで確認できる。fg コマンドでフォアグラウンドに戻して再開できる。
Ctrl+s画面出力の停止。
意図して押下することはほとんどない。誤って押下した場合、一切のコマンド入力を受け付けないように見える(実際は見えないだけで受け付けている)ので、このキーの存在を知らないとハングと間違って焦る。
Ctrl+q画面出力の再開。
!$Ctrl キーじゃないけどおまけ。直前に実行したコマンドの最後の引数。
例えば 『 ls -l なが?いパス 』 の後に 『 vi !$ 』 のように使うと便利。

2011年9月21日水曜日

[書籍紹介] Effective Perl

一通りなんでも perl で書けるようになったら是非手にとってください。とってもお勧め。1ランク上の perl 使いになれます。

2011年9月20日火曜日

運用エンジニアにお勧めの資格

国家試験

年に1,2回しかないものの、5,250円で受験できて、ベンダ資格に比べて市場価値も高い。(その分ベンダ試験より難しいですが)
実用的ではないといってよく批判されていますが、この試験はそういうものを期待して受けるものではないと個人的には思っています。汎用的な知識を体系的に身に付けることを主眼とすれば、これ以上の試験はないのではないでしょうか。

ITパスポート お勧め度
新卒1年目限定で。1年過ぎてしまったら基本情報に切り替えましょう。
基本情報処理技術者試験 お勧め度 ★★★
運用エンジニアに限らず、IT関連の全ての技術者にとってほしい。非常に幅広いジャンルの基本的な知識が必要とされます。午後にプログラミングの問題があるので、PG以外のエンジニアには難易度高めですが、PGでなくともこの程度のプログラムを読むことぐらいできなければエンジニアとは言えない。できれば20代前半のうちに。
応用情報処理技術者試験 お勧め度
基本情報より簡単だと思います。これに合格しておくと、高度試験の午前Iが免除されるので余裕があれば受けておく、という程度で。
ネットワークスペシャリスト お勧め度 ★★★
これも運用エンジニアに限らず、IT関連の全ての技術者にとってほしい。今時ネットワークの知識が不要なIT職種などないと言い切っていいでしょう。
情報セキュリティスペシャリスト お勧め度 ★★
これも、今時避けては通れないジャンル。開発者向けの内容が多いですが、開発以外の人間も知っておくべき。
ITサービスマネージャ お勧め度 ★★★
運用チームのリーダー・マネージャーをやっている人、目指す人にお勧め。実際にそういう立場にいる人ならば難易度は低いと思います。

ベンダ試験

何の運用かに関わらず共通でお勧めなのはITIL。職場がITILを導入しているなら勿論のこと、未導入であっても資格取得の勉強を通じてITILを学び、職場に改善提案してみましょう。
それ以外は、実際に業務で使用している製品の資格だけを取ることをお勧めします。経験のない製品の資格を机上の勉強だけで取るのは何の意味もないのでやめたほうがいいと思います。ベンダ試験は高いので、会社からの補助がないなら無理に取る必要もないです。

2011年9月19日月曜日

telnet で http(Basic認証対応)

まずは認証に使用するユーザ名とパスワードを base64 エンコードします。

$ perl -e 'use MIME::Base64; print encode_base64("username:password");'
dXNlcm5hbWU6cGFzc3dvcmQ=
$ telnet xx.xx.xx.xx 80
GET / HTTP/1.1
Host: xx.xx.xx.xx
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
Connection: close

通常の http の時との差は Authorization ヘッダです。 ブラウザでアクセスした場合は、一度認証に成功すると以降のアクセスはブラウザが勝手にその Authorization ヘッダを付け続けてくれるので、ユーザーは認証を意識せずに移動できますが、独自クライアントでは毎回 Authorization ヘッダを付ける必要があります。

2011年9月18日日曜日

脆弱性を検証してみよう - Apache Killer(CVE-2011-3192)その2

前回のエントリで脆弱性を検証しました。今回は対策が有効であることを検証します。

2.2系に関しては対策版が正式にリリースされているので、まずはそちらから試します。 CentOS5.4 + Apache2.2.3 の環境を Apache 2.2.21 にアップグレードして検証しました。

# ./bin/httpd -v
Server version: Apache/2.2.21 (Unix)
Server built:   Sep 18 2011 10:12:52

検証方法は前回のエントリを参照してください。
vmstat 1 の結果です。

idle が 97 になっているところが攻撃された瞬間です。 前回と全く同じ攻撃条件でしたが一瞬で処理が終わりました。 サーバ側には何の影響もありません。対策がきちんと行われていることが確認できました。

具体的にどのようなレスポンスが返されているのか見てみましょう。

206 ではなく 200 が返ってきています。ちなみに正常なレンジ(例えば Range: bytes=0-100)のリクエストであれば 206 を返してきます。

次は、Solaris10 + Apache 2.0.64 の環境を使って、回避策を試してみます。

http://httpd.apache.org/security/CVE-2011-3192.txt

私の環境では mod_rewrite は組み込まれていますが mod_header は無いので(勿論リコンパイルすればいいのですが面倒くさいので・・・)、Mitigation の 1) の Option 2 をベースに、Request-Range ヘッダも Range ヘッダと同様に処理することにします。
以下の記述を httpd.conf に追記して Apache を restart 。

RewriteEngine on
RewriteCond %{HTTP:range} !(^bytes=[^,]+(,[^,]+){0,4}$|^$) [NC]
RewriteRule .* - [F]
RewriteCond %{HTTP:request-range} !(^bytes=[^,]+(,[^,]+){0,4}$|^$) [NC]
RewriteRule .* - [F]

攻撃してみます。
以下は vmstat 1 の結果です。

idle が 90 になっているところが攻撃した瞬間です。 こちらもすぐに処理が完了してサーバに影響はありません。 回避策も有効であることが確認できました。ちなみにこちらのレスポンスは mod_rewrite のルール通り 403 になります。

2011年9月17日土曜日

脆弱性を検証してみよう - Apache Killer(CVE-2011-3192)その1

★注意★ 間違っても本番サーバで試さないでください。人生終わります。

■対象
脆弱性があるのは、2.0.64以下の全ての2.0系と2.2.19以下の全ての2.2系。1.3系にはありません。

■脆弱性の概要
Range ヘッダは本来、取得するコンテンツのバイト数を指定してリクエストを送るために使います。例えば、Range: bytes=500-600 とすれば、GET 対象のコンテンツの 500byte 目から 600byte 目までのデータだけを取得することができます。これによって、FTP のレジューム機能のようなものが HTTP でも実現できるわけです。このレンジ指定は、カンマで区切って複数記載できます。その場合、それぞれの指定幅の複数のコンテンツが返されます。
Accept-Encoding ヘッダは、クライアントが解釈可能なコンテンツのコーディング方式をサーバ側に知らせるためのヘッダです。これを知らされたサーバはそのエンコード方法が利用可能ならエンコードしてデータを返します。例えば Accept-Encoding: gzip がリクエストヘッダにあった場合、mod_deflate や mod_gzip などのモジュールが組み込まれて設定されていれば(デフォルトでは組み込まれません)、コンテンツを動的に gzip 圧縮して返します。mod_rewrite 等で予め圧縮しておいたファイルに誘導することもできますし、モジュールに頼らなくてもCGIなどのプログラムで独自に実装することも勿論できます。帯域を節約したい場合に有効です。
※HTTPヘッダについて詳しくは RFC 2616 を参照してください。
で、Apache Killer はというと、大量のレンジ指定をリクエストすることによって、サーバのリソースを食いつくします。どれだけ大量かというと、こんな感じです。



上記のレンジ指定を含んだリクエストを複数投げつけることで攻撃します。どれぐらいのセッション数で DoS を引き起こせるかはサーバ側のスペック次第ですが、クライアント側にはほとんど負荷にならない量でサーバ側に大ダメージを与えられます。サーバが gzip 圧縮可能な設定になっていると更にダメージUP。そういう意味で mod_deflate などが使われていると効果的なわけですが、必須ではありません。
どのリソースがボトルネックになってくるかは、動的な gzip 圧縮が有効になっているかどうかや、サーバのスペック次第なところがあると思いますが、いずれにせよ DoS は成立してしまいます。

では実際にやってみましょう。

■検証環境
サーバ:Solaris10 + Apache 2.0.64 / CentOS5.4 + Apache 2.2.3
 ※OSは何でも良いです。Apache も対象バージョンであれば何でも OK。モジュールもデフォルトで構いません。
クライアント:Solaris10
 ※Perl5以上が使える環境であれば何でもOKです。攻撃対象のサーバとは分けましょう。

■検証
出回っている攻撃ツールには killapache.pl と apachepartial.pl がありますが、killapache.pl のほうはモジュールのインストールが必要だし、use strict; してないし、バグもあるし、使い勝手悪くて編集しないと使い物にならないので、apachepartial.pl を使います。
http://pastebin.com/NCDv9eTh
ダウンロードするなりコピペするなりして、クライアントに設置します。

サーバは Apache が起動していなければ起動します。vmstat 1 で観察しましょう。

いよいよ攻撃です。apachepartial.pl を使って攻撃対象の URL を探します。攻撃が成立するのは、レスポンスとして 206 Partial Content を返してくる URL のみです。DocumentRoot に入っているコンテンツ次第では、単純に / では 206 が返ってこない場合がありますので、その場合は適当に検証用のファイルを置くなりしてアクセスしてみてください。ファイルサイズが小さいとうまくいかないので、1300byte 以上あるファイルを用意して試してください。
引数は、ホスト名、パス、子プロセス数、ループ数、ポート番号です。ホスト名以外は省略可能で、デフォルトでは前から順番に / 、10、5、80 が使われます。

※NGパターンの例


※OKパターンの例


サーバの vmstat 1 の様子です。(Solaris10 + Apache 2.0.64)


サーバの vmstat 1 の様子です。(CentOS5.4 + Apache 2.2.3)

Solaris10 + Apache 2.0.64 の結果も CentOS5.4 + Apache2.2.3 の結果もほぼ同じです。最初の idle がほぼ 100 の間は何もしていない時です。攻撃を開始するとほぼ同時に、メモリが食い尽くされ、ひどいスラッシング状態に陥り、ディスクI/O待ちのプロセス数が頭打ち状態になりました。攻撃を停止してもこの状態は続き、Web の応答は勿論、新たにOSにログインすることもできません。vmstat も 1 秒ごとには流れなくなっています。DoS 成功です。
放っておいても回復しないのでOSごと再起動してしまいましょう。

次回は、アナウンスされている暫定回避策の有効性確認と対策済みバージョンの検証を行いたいと思います。

2011年9月16日金曜日

コマンドラインで https

残念ながら telnet は使えません。暗号化されているのでね。openssl を使います。

$ openssl s_client -connect 127.0.0.1:443
(証明書などの情報が大量に出てきます。略)
---
GET / HTTP/1.0

(普通のレスポンスが表示されます。略)

2011年9月15日木曜日

telnet で http

単に生死確認等が目的でレスポンスを見たいだけなら HTTP/1.0 でリクエスト。 リクエストの最後には空行を送ってやらないとレスポンス返ってきませんので注意。

$ telnet 127.0.0.1 80
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Date: Sat, 17 Sep 2011 06:11:48 GMT
Server: Apache/2.0.64 (Unix) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 536
Connection: close
Content-Type: text/html
(省略)

特定のヘッダの挙動を見たい等で HTTP/1.1 でリクエストする場合には、Host ヘッダが必須になるので注意。 Host を忘れると、少なくとも Apache は 400 Bad Request ではじいてきます。 また、Connection: close を指定しておかないとサーバの設定によっては keep-alive されるので書きましょう。

$ telnet 127.0.0.1 80
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
GET / HTTP/1.1
Host: 127.0.0.1
Connection: close

HTTP/1.1 200 OK
Date: Sat, 17 Sep 2011 06:19:01 GMT
Server: Apache/2.0.64 (Unix) PHP/5.3.5
X-Powered-By: PHP/5.3.5
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 536
Connection: close
Content-Type: text/html
(省略)

2011年9月14日水曜日

[書籍紹介] CGI&Perlポケットリファレンス

サイズが手ごろで持ち運びしやすい。 関数などはインターネットでも調べられるし、もちろんperldocで調べればいいんですが、手元にこういうリファレンス本があればそれで引くほうが早くてわかりやすいです。 他のリファレンス本を使ったことがないので比較はできないんですが、私はこれで必要充分です。 説明もわかりやすし、サンプルもそれなりにあって◎。 なんといっても中古が安いです。ブックオフでよく105円で見かけますし、Amazonでも中古は1円(+送料250円)で手に入ります。持っていて損はありません。

2011年9月13日火曜日

[書籍紹介] 初めてのPerl

オライリー本独特のとっつきにくさがありますので、プログラミングのプの字もわからない初心者には正直オススメできません。 シェルスクリプトなら書けるようになって、次のステップとしてperlをやってみようと思っている人、基本情報処理技術者試験の午後対策としてCやJavaを軽く触ったけど自分でプログラムを作ったことはない人、ぐらいが最適なレベルで、良いテキストになると思います。 Cのバイブルとしてよくあげられるプログラミング言語Cという本がありますがそれに似た感じです。

2011年9月12日月曜日

IO::Socket::INET でメール送信(smtp-auth対応)

#!/usr/bin/perl

use strict;
use IO::Socket::INET;
use MIME::Base64;

my $server = 'mail.hoge.com';
my $port = 587;
my $fqdn = 'my.fqdn';
my $user = 'username';
my $passwd = 'password';
my $from = 'hoge@hoge.com';
my $to = 'foo@bar.com';

$user = encode_base64($user,'');
$passwd = encode_base64($passwd,'');

my $sock = IO::Socket::INET->new(PeerAddr => "$server:$port", Proto => 'tcp') or die;
print $sock <<"END_OF_DATA";
EHLO $fqdn
AUTH LOGIN
$user
$passwd
MAIL FROM: $from
RCPT TO: $to
DATA
From: $from
To: $to
Subject: sock test

test mail with sock
.
QUIT
END_OF_DATA
$sock->close();

exit;

2011年9月11日日曜日

Net::SMTP でメール送信

#!/usr/bin/perl

use strict;
use Net::SMTP;

my $smtpserver = 'mail.hoge.com';
my $port = 25;
my $from = 'hoge@hogehoge.com';
my $to = 'foo@foobar.com';

my $smtp = Net::SMTP->new($smtpserver, Port => $port);
$smtp->mail($from);
$smtp->to($to);
$smtp->data();
$smtp->datasend("From: $from\n");
$smtp->datasend("To: $to\n");
$smtp->datasend("Subject: mod test\n");
$smtp->datasend("\n");
$smtp->datasend("A simple test message\n");
$smtp->dataend();
$smtp->quit;

exit;

telnet で smtp-auth

まずは認証に使用するユーザ名とパスワードを base64 エンコードします。

$ perl -e 'use MIME::Base64; print encode_base64("username"), encode_base64("password");'
dXNlcm5hbWU=
cGFzc3dvcmQ=
$ telnet xx.xx.xx.xx 587
EHLO myfqdn
AUTH LOGIN
dXNlcm5hbWU=
cGFzc3dvcmQ=
MAIL FROM: hoge@hoge.com
RCPT TO: foo@bar.com
RCPT TO: some@exsample.com
RCPT TO: someone@exsample.com
DATA
From: hoge@hoge.com
To: foo@bar.com, some@exsample.com
Cc: someone@exsample.com
Subject: test

test mail
.
QUIT

通常の smtp の時との差は、base64 エンコードされたユーザ名とパスワードを AUTH LOGIN 行の下に書くだけです。

2011年9月10日土曜日

telnet で smtp

$ telnet xx.xx.xx.xx 25

HELO myfqdn
MAIL FROM: hoge@hoge.com
RCPT TO: foo@bar.com
RCPT TO: some@exsample.com
RCPT TO: someone@exsample.com
DATA
From: hoge@hoge.com
To: foo@bar.com, some@exsample.com
Cc: someone@exsample.com
Subject: test

test mail
.
QUIT

telnet の引数に IP (名前解決できるなら勿論ホスト名でも可) とポート番号。
HELO の引数は自身の FQDN。
送信先が複数ある場合、RCPT TO は複数行書ける。
DATA から . までがメール。
メールヘッダと本文の区切りは空行1つ。
US-ASCII 限定。日本語を使用したい場合はちゃんとエンコードして MIME の仕様に沿ったフォーマットにする必要あり。

2011年9月9日金曜日

use と require の違い

use は perl5 以上でモジュールを読み込むのに使われ、スクリプト中のどこに記載してもコンパイル時に一度だけ必ずロードされる。

一般的には先頭に書かれることが多いが、最後に書かれていても、使われないサブルーチンの中でも、偽になって実行されない if 文の中でも、ループの中でも関係ない。子プロセスを fork しまくるようなプログラムではこちらを使ったほうがよい。

require は一般的にはモジュールではなくファイルを読み込むときに使われるが、モジュールにも使える。 コンパイル時ではなく実行時にロードされるので、偽になる if 文の中にあればロードはされない。 条件によってモジュールを読み込ませるかどうか切り替えたい場合にはこちらを使う。

以下のサイトの説明が詳しく、わかりやすい。
http://www.ndis.co.jp/blog/tech/2008/10/use-require.html

2011年9月8日木曜日

perl の正規表現の謎

たとえばこんなコード。

$p = "/";
$a = "/hoge/hoge";
if ($a =~ /$p/) { print "Match\n" } else { print "Not match\n" }

$p が展開されて、/// になってしまいエラーになると思っていたのだが、ならない。
$p が ")" だと括弧の不整合エラーがでるのだが・・・。
なんでなんでしょう?誰かわかる方教えてください。m(_ _)m

2011年9月6日火曜日

きっと何者にもなれないお前たちに告げる

このブログは私の個人的なメモ置き場です。

できるだけ嘘は書かないように気をつけてはいますが、情報の正確性は一切保証しません。 ブログ中のソースコードの流用、改変はご自由に。 ただし、ここにある情報を利用して発生した如何なる損害に対しても私は責任を負いません。