PEAK XOOPS - News in englishin japanese

Archive | RSS |
  
Poster : GIJOE on 2007-03-07 05:28:47 (9644 reads)


# php -r '$a=array("1"=>1);var_dump($a[1]);'
int(1)

まあこれはPHP配列の基本ですね。普通ならアレっと思うところですが。

# php -r '$a=array("1a"=>1);var_dump($a);'
array(1) {
  ["1a"]=>
  int(1)
}

数字から始まるインデックスでも、勝手に整数型へキャストされるわけではない。

# php -r '$a=array("0x1a"=>1);var_dump($a);'
array(1) {
  ["0x1a"]=>
  int(1)
}

数値リテラルでも文字列になってたら文字列。(当たり前)

# php -r '$a=array(0x1a=>1);var_dump($a);'
array(1) {
  [26]=>
  int(1)
}

ちゃんとした数値リテラルなら数値インデックス。(これも当たり前)

# php -r '$a=array(""=>1);var_dump($a);'
array(1) {
  [""]=>
  int(1)
}

空文字列のインデックスもアリ。0ではないので注意。

# php -r '$a=array(false=>1);var_dump($a);'
array(1) {
  [0]=>
  int(1)
}

ブール型は整数型にキャストされる(知らなかった!)

# php -r '$a=array(true=>1);var_dump($a);'
array(1) {
  [1]=>
  int(1)
}

同上

# php -r '$a=array(1.1=>1);var_dump($a);'
array(1) {
  [1]=>
  int(1)
}

おっと、浮動小数点型は整数型にキャストされるのか!

# php -r '$a=array("1.1"=>1);var_dump($a);'
array(1) {
  ["1.1"]=>
  int(1)
}

おっと、こっちは文字列インデックスのまま。整数との違いでハマルかも

# php -r '$a=array("01"=>1);var_dump($a);'
array(1) {
  ["01"]=>
  int(1)
}

ありゃりゃ。これは危険だぞ。DBからZEROFILLしたフィールドの結果を、intval()かけずにインデックスに格納したらハマリそう

# php -r '$a=array(-1=>1);var_dump($a);'
array(1) {
  [-1]=>
  int(1)
}

うん、まあ。これは当然。

# php -r '$a=array("-1"=>1);var_dump($a);'
array(1) {
  [-1]=>
  int(1)
}

ああ、こっちは整数として認識するんだあ。0は駄目。小数点も駄目。マイナスだけOKか。

# php -r '$a=array("+1"=>1);var_dump($a);'
array(1) {
  ["+1"]=>
  int(1)
}

-はOKだけど、+は駄目でした。

いやあ、知らないことが結構いっぱいありましたね。
配列インデックスはなるべく型を揃えて指定しましょう。(当たり前)
実際の所、配列インデックスが整数型か文字列型かで、+演算子とかarray_merge()の挙動が全然違うので、それを意識しておかないと確実にハマリますね。

というわけで、昨日のパッチも本当は良くない。intval()かけておかないと、結構怖いかも。
midフィールドにzerofill属性ついてたらアウトじゃないかなあ。

$_cachedModule_mid[intval($module->getVar('mid'))] =& $module;

0 comments

Poster : GIJOE on 2006-10-29 06:54:04 (11735 reads)

in englishin japanese
PEAK XOOPSのようなサイトまるごと引越でpagerankを引き継げるか。
まず、基本的なリダイレクトテクニックは、上鍵さんがどこかに書かれた記事を参考にしました。ForceType と Actionの組み合わせは、とっても便利です!

OSCでjidaikoboさんに、302ではなく301で返せば引き継がれるらしい、と聞いて調べてみたら、こんなのが見つかりました。

http://www.web-20.net/2006/05/seo301.html

なるほど。
と、telnetで確認してみたら、302を返してました。

PHPマニュアルを読み返してみると、
「header()でLocationヘッダを送ると、自動的に302を付与する」
そうだったのか!

というわけで、リダイレクトスクリプトを修正します。Pagerank7に戻るのは無理でも、6くらいには回復したいですね。


DocumentRoot
-- xoops_redirect.php
-- xoops/
---- .htaccess
---- index.html (dummy)


/xoops_redirect.php

<?php
$uri = strtr( substr( $_SERVER['REQUEST_URI'] , 6 ) , array( "\n" => '' , "\r" => '' ) ) ;

header( "HTTP/1.0 301 Moved Permanently" ) ;
header( "Location: http://xoops.peak.ne.jp".$uri ) ;
?>


/xoops/.htaccess

ForceType xoops_redirect
Action xoops_redirect /xoops_redirect.php

0 comments

Poster : GIJOE on 2006-06-25 18:20:31 (11401 reads)

in englishin japanese
「大きな傘」(BigUmbrella)XSS対策システムの最終回。考察編です。
(3)に示したコードには、議論の余地が2つ残っています。

・疑うべきリクエストのパターンがそれで良いのか

(3)で示したチェックパターンはこうです。

preg_match( '/[<\'"].{15}/s' , $val , $regs )


タグの開始文字<および、HTMLのクオーティングを破るための'と"から始まる全16文字について、そのまま出力されていたらアウト、というチェックですが、それで本当に抜けがないでしょうか?

とりあえず怖いのは、リクエスト文字列をそのまま、<a>タグのhrefや<img>タグのsrcに渡しているケースで、この場合、java-script: から始まる文字列も(IE対策のためにはその間に制御文字が入っているものも)潰す必要があります。つまり、BigUmbrellaを本格運用するなら、"java-script:"対策も追加しておくべきでしょう。

リテラルクオーティングキャラクターである"および'を潰しているからといって、JavaScriptでリテラルを表現できないなどと考えるのは軽率で、文字コードを羅列し結合することで、十分に攻撃力を持つリテラルの表現は可能なはずです。

・チェック対象リクエストは、POSTとGETだけで良いのか

(3)では、GETとPOSTのみについてチェックしていますが、これも本来なら不十分です。ただし、それはCOOKIEについてではありません。もちろん、COOKIE経由のXSSがないという保証はありませんから、チェックを入れても構いません。ただ、冷静に考えれば、COOKIEに意図しないそんなリクエストが埋め込まれている時点でXSSなどの攻撃を受けているのは明らかで、手遅れと言わざるを得ないでしょう。すでに体がびしょ濡れなのに、「傘」をさしても意味がないのです。

むしろ問題となりがちなのは、SERVER(ENV)変数です。PHP側でデコードされるSERVER変数が3つほどあります。その中で一番有名なのはPHP_SELFですが、PATH_INFOなどを直接表示してしまっているスクリプトも少なからず存在してそうです。

とりあえず最低限足しておくべきなのは、3つのSERVER変数(PHP_SELF,PATH_INFO,PATH_TRANSLATED)についてのチェックです。実際には他のSERVER変数についても、通常のリクエストで<'"が登場することは滅多にないようなので、SERVER変数全てについて無条件でチェックする、なんていうのでも、サーバ負荷への影響は大きくないかもしれません。


なお、この「大きな傘」は、XSS対策にしかなりません。同じJavaScript系攻撃でも、ScriptInsertion(別名 HtmlInjection)にはほぼ無力です。

「対策方法」という観点からも、XSSとScriptInsertionは別物であることがお判りいただけるでしょう。

ScriptInsertion攻撃からサイト全体を守るための「大きな傘」となると難しいのですが、怪しいリクエストが見つかった時点で、サーバ管理者にメールをする、なんて地道な方法しかないかもしれません。

メールを送るとなると、可能な限りノイズを減らしたいところです。リクエストが怪しいかどうかの判断には、<'"なんて汎用的なキャラクターではなく、JavaScript特有の関数名であればかなりノイズは減らせますが、それで十分かどうかはなんとも言えないところです。

とにかく、JavaScriptで最も怖いのは、"XMLHttpRequest"なので、この文字列と、動的にXMLHttpRequestという関数名を生成・実行する"eval"の2つを検出することは最低限必要でしょう。あとは定番ですが、cookieも押さえておきます。

実際の所、こんな方法でサイト全体のScriptInsertion対策になるのか、と問われたら、自分自身でも「疑わしい」としか答えようがないのですが…

以上、宿題を残しつつも、anti-XSSシステムについてはこれにていったん終了とします。


Poster : GIJOE on 2006-06-22 12:46:44 (10267 reads)

in englishin japanese
「大きな傘」(BigUmbrella)XSS対策システムの第3回。最終回のつもりでしたが、まだひっぱります。

前回では、コンセプトだけの簡単なコードを示しましたが、それだと以下のような問題が考えられます。

(A) text/html 以外のContent-Typeに対しても、XSSチェック処理が入ってしまう。

例えば、POSTでリクエストされた通りに、text/plain で出力するアプリケーション。この場合、htmlspecialchars()をかける方がおかしいわけですが、正しいはずの処理が、"XSS Found" で潰されてしまいます。

(B) 巨大なファイル転送処理についても、出力フィルターがかかる

一見(A)に似ていますが、こちらはパフォーマンス上の問題点です。数百Mbyteのファイルを、phpで転送する、という場合、出力フィルターがかかると、その巨大なファイルをいったんすべてメモリに取り込んでからの処理となってしまうため、サーバ負荷が極端に上昇します。つまり、DoS攻撃に対して弱くなる、と言えます。

(C) text/html 出力で、GET/POSTされた文字列をそのまま表示したいケースはある

いわゆる「HTML許可」投稿におけるプレビューがそれに該当します。


このうち、(A)は比較的まっとうに対応が可能です。出力フィルター側で、headers_list()関数を使えば、Content-Typeが明示的に指定されているかどうかを確認できます。Content-Typeとしてtext/html が指定されているか、Content-Typeが指定されていない時に限れば、問題解決でしょう。ただし、headers_list()関数は、PHP5以降でしか利用できないため、PHP4でこれを実現することはほぼ不可能であり、(B)と同じ解決法を選択せざるを得ないでしょう。

一方、(B)の問題を、BigUmbrella側だけで対応するのは不可能です。というのも、出力フィルター関数に処理が移ってきた時点で、負荷が問題となる出力のメモリー取り込み処理は終了しているからです。
だから、ユーザスクリプト側で、巨大なファイルを転送する時などに、明示的に出力フィルターを切るしかないでしょう。


	// remove output bufferings
	while( ob_get_level() ) {
		ob_end_clean() ;
	}


最後に残った(C)を解決するには、BigUmbrellaとユーザスクリプトの協調が不可欠です。
外部からのInjectionを避けるためには、定数を利用するのがベストでしょう。

ユーザスクリプト側で、あえてBigUmbrellaを切りたいときには、以下のように指定します。

	define( 'BIGUMBRELLA_DISABLED' , true ) ;

あくまで、BigUmbrellaを通過しないのが、「特例」である、ということです。

そしてその特例を通すためには、そのプレビューページに、リファラーやチケットといった、CSRFと同じ対策が必要です。もしそれがないと、外部サイトからXSSで飛んできて(この時点では、XMLHttpRequestは利用不可)、プレビューページへPOSTした表示内容がXSS。後者(プレビューページ)のXSSはサイト内ですから、あとはどこへでもXMLHttpRequestを打ち放題、となるわけです。

このあたり、頭がこんがらがってしまう人も多そうですが、XMLHttpRequestは同一ホストにしか飛ばせないので、サイト全体がXSS対策されている以上、リファラー偽装・チケットの別途取得といったXMLHttpRequest特有のテクニックが使えず、結果的に、CSRF対策がXSS対策にもなる、というわけです。

とりあえず、下に改良版コードを載せます。(A)と(C)に対応するコードを追加し、さらに、REQUESTをGETとPOSTに分離しています。COOKIEやSERVER変数については、(4)で検討します。

Read more... | 987 bytes more |0 comments

Poster : GIJOE on 2006-06-21 15:27:40 (10958 reads)

in englishin japanese
昨日書いた「大きな傘」たりえるXSS対策のコード編です。
理屈はすでに述べた通りですが、これをどうコードに落とすかについては、実はまだ迷いがあります。
とりあえず、一番簡単なパターンだとこんな感じでしょうか。

(1) ファイルを作成
/usr/local/lib/php/bigumbrella.php


<?php
function bigumbrella_init() {
        foreach( $_REQUEST as $key => $val ) {
                if( preg_match( '/[<\'"].{15}/s' , $val , $regs ) ) {
                        $GLOBALS['bigumbrella_doubtfuls'][] = $regs[0] ;
                }
        }
        if( ! empty( $GLOBALS['bigumbrella_doubtfuls'] ) ) {
                ob_start( 'bigumbrella_outputcheck' ) ;
        }
}

function bigumbrella_outputcheck( $s ) {
        if( ! is_array( @$GLOBALS['bigumbrella_doubtfuls'] ) ) {
                return "bigumbrella injection found." ;
        }

        foreach( $GLOBALS['bigumbrella_doubtfuls'] as $doubtful ) {
                if( strstr( $s , $doubtful ) ) {
                        return "XSS found." ;
                }
        }
        return $s ;
}

$GLOBALS['bigumbrella_doubtfuls'] = array() ;
bigumbrella_init() ;
?>


(2) httpd.conf の各VirtualHost内に1行追加。

php_value auto_prepend_file /usr/local/lib/php/bigumbrella.php

php.ini に記述することも可能です。

auto_prepend_file = /usr/local/lib/php/bigumbrella.php


大事なのは、このXSS対策は、ホスト単位で守らないと意味がないことです。それ故の、httpd.conf指定、または、php.ini指定です。auto_prepend_file は、PHP_INI_PERDIRなので、.htaccessで指定してもOKですが、結果的にこの「傘」に守られない同一ホストのファイルができてしまうようでは、意味がない可能性があります。

実は、まだ上記のコードは不完全です。例えば、GETのXSS攻撃を同名のPOSTでカモフラージュすることが可能ですし、「HTML許可したプレビュー」なんて条件での回避策がありません。というわけで、もう少しまともなコードは(3)へと続きます。(時間がなくて細切れにてすみません…)

とりあえず、このanti-XSSを命名だけしちゃいます。

「ビッグ・アンブレラ・anti-XSS・システム」

なんとも、洋館に巨大なゾンビがうようよしてそうな名前ですが

0 comments

« 1 2 (3) 4 5 6 »
Login
Username or e-mail:

Password:

Remember Me

Lost Password?

Register now!