日記
tDiaryでもできますよ (16:19)Edit

ウェブロについて考える。6の599ime.nu経由)」より、

>MTに限らずCookieやBASIC認証を使った全てのCGIで似たようなことは出来る。

はFUD

ちなみにtDiary(BASIC認証)でもできますよ。別タブ/ウィンドウとかで管理画面をBASIC認証済みにしておいて(ブラウザを閉じなければ、そのページ自体は閉じてもOK)、別に登録フォーム相当のHTMLを持つページを用意し、実行ボタンをhiddenパラメータ化しておいて、JavaScriptでsubmitさせる。IE系でもMozilla(Firefox)でもできた。

タブブラウザユーザーの場合は、そういう運用(別窓で認証した状態をキープ)って珍しくないよね。BASIC認証を切るためには、いったんタブブラウザごと閉じなきゃいけないんで面倒だし。まあMTみたいにブラウザセッションを越えて認証状態を継続できない(でもMTのその機能もオプションだけど)んで、ちょっとだけましだけど。

あと、個人的にREFERERで縛るのって、ユーザー環境によっては誤動作する可能性があるし、その場合の対応が面倒なんで(セキュリティソフトONで動かない場合、セキュリティソフトをOFFにしろとは言いにくい)あんまりやらないんだけど、一般的には標準だったりするのかな?

まあやっぱり抜本的な対策として、セッションIDの一部をhiddenパラメータで埋め込んで認証するほうを(自分の)標準にしようかな。BASIC認証の場合は、REMOTE_PASSWORDの一部のmd5ハッシュとかかな。REMOTE_PASSWORDって使ったことないんだけど、BASIC認証を通った場合は必ず得られるんだっけ?

そういえば、「ウェブロについて考える。6の596ime.nu経由)」にある、

Yahooとかみたいにワンタイムのクッキーでうにゃうにゃすればいいような…

に関する具体的な情報希望。ちなみにMy Yahoo!で試してみたけど、まあふつうに同様の操作(他のフォームからJavaScriptを使って勝手に更新)ができたよ。ワンタイムってのがどの程度の寿命なのかよくわからないけど、ブラウザセッションCookie(ブラウザを閉じるまで有効)だったら大して差がない気がする。というか、どちらにしろフォーム(に含まれる情報)とCookie(に含まれる情報)を照合する仕組みがないと意味がない気が。

まめにログアウト(セッションCookie削除)できるようにするのは簡単だけど、ユーザーがそんなのいちいち使ってくれることを期待しても無駄っぽい気がする。MTだってログアウト機能はついているけど、更新が終わるとログアウトする人なんてあんまりいないと思うし。

REMOTE_PASSWORDはだめっぽい

PHPだったら、$_SERVER['PHP_AUTH_PW']でいけるんだけどなー。しょうがないから、一般的なBASIC認証の場合は、REMOTE_USERと適当なシードを組み合わせて生成する方向かなー。

tDiaryだと

update.rhtmlとpreview.rhtmlに、

<input type="hidden" name="authkey" value="<%= Digest::MD5.hexdigest(ENV['REMOTE_USER']+'some unique seed') %>">

なんて感じで埋め込んでおいて、クリティカルな処理時は、

if @cgi.params['authkey'][0].to_s != Digest::MD5.hexdigest(ENV['REMOTE_USER']+'some unique seed')
die
end

って感じかな。

実際には'some unique seed'は@confにセットするか、あるいはサーバー上で非公開の使えるキーを自動生成。tdiary.confのmd5値とかどうだろう? クリティカルな処理は、TDiaryAppendとTDiaryUpdateあたりでいいのかな?

PHP+BASIC認証の場合

<input type="hidden" name="authkey" value="<?php echo md5(substr($_SERVER['PHP_AUTH_PW'], strlen($_SERVER['PHP_AUTH_PW']) / 2)); ?>">

なんて埋め込んで、

if ($_POST['authkey'] != md5(substr($_SERVER['PHP_AUTH_PW'], strlen($_SERVER['PHP_AUTH_PW']) / 2))) {die('不正な投稿処理');}

とか。フルにパスワードを使うのが気持ち悪いんで、なんとなく後半半分だけ使ってみた。

PHP+セッションCookieの場合

<input type="hidden" name="authkey" value="<?php echo md5(session_id()); ?>">

なんて埋め込んで、

if ($_POST['authkey'] != md5(session_id())) {die('不正な投稿処理');}

とか。セッションIDの一部にしなくても、セッションID全体のmd5ハッシュで十分か。

そういえば

DI:DOみたいに、クリティカルな処理をするときには必ずパスワードを入力させる、という方法もあるんだよな。んでもって、クリティカルじゃない処理はCookieによる認証で済ませる、と。

ただそうやってクリティカルな処理のたびにパスワードを入力させると、パスワードの強度が下がるリスク(頻繁に入力するなら簡単にしたくなる&パスワード文字列が頻繁にネットワーク上を流れるんで漏洩機会が多くなる)もあるんだけど。

HNS

webifの記事投稿時はパスワード入力必須だったんだっけ。すっかり忘れていたな。

結構シンプルなツール側の対策を考えてみた

簡易認証用のPATH_INFOを必須にするってのはどうだろう? たとえば、tDiaryだったら管理CGIファイル名はupdate.rbなわけだけど、適当なPATH_INFO設定(たとえばabc)を@confで定義できるようにして、update.rb/abc?[QUERY_STRING]じゃないと管理CGIが動作しないようにする。

もしもPATH_INFOなしでアクセスされた場合は、ユーザー認証済み(BASIC認証の場合は認証後じゃないとcgiにはアクセスできないわけだが)だったら、自動的にPATH_INFO付のURLにリダイレクトする。そうすると、そのフォームからREQUEST_URIに対してPOSTするぶんにはPATH_INFOが維持される。PATH_INFOなしでアクセス(=攻撃?)があった場合、自動的にPATH_INFO付きURLにリダイレクトする際に、QUERY_STRINGやPOSTパラメータが除去できるんで、攻撃が無効になる。

mt.cgiをリネームしよう案と似ているけど、いちいち実ファイルをリネームするのではないぶん手軽で、同様の効果(投稿先URLが違うので処理が働かない)がありそう。表側に管理ツールへのリンクを張る際も、update.rbのままでかまわないし。管理CGI側でPATH_INFOを使っていなければ、内部的には処理を書き換える必要がない。単に処理の最初にPATH_INFOの正当性チェックコードを入れるだけ。

この仕組みに穴はあるかな?

ああ、いかん

REQUEST_URIじゃQUERY_STRINGも維持されちゃうじゃないか。PHP_SELFの方だな。って、これはPHPだけの環境変数か。えーっと、一般的なCGI環境変数にPHP_SELF相当(SCRIPT_NAME+PATH_INFO)ってないのかな? いちいちSCRIPT_NAME+PATH_INFOって書かなきゃだめなのか?

Published At2004-09-15 00:00Updated At2004-09-15 00:00