レガシーwebアプリケーションを、ファイアウォールの内側で利用する
2011年3月9日
古くなったwebアプリケーションは、既知の脆弱性がそのまま放置されているようなケースが良くある。こうなると危なくてもう使えない(イントラネットで使うようなケースは別)。
そういったアプリケーションのすべての機能ではなく、一部の機能だけ使いたいというようなことは良くあるだろう。そういう場合、その一部の機能を担うための情報だけサニタイズして与えれば、問題なく使えるはずである。
使用中のwebページでそういったことに相当するケースがあったので、対処してみた。
PHPで書かれた古いwebアプリケーションが、/legacy/というURIにインストールしてあるとする。
1)まず、このレガシーアプリケーションへの直接のアクセスを遮断しなければならない。ここでは、BASIC認証を用いて、遮断する。.htaccessと.htpasswdを適当に設定して、アクセスするにはユーザー名とパスワードを入力しなければならないようにしておく。
2)ディレクトリ名を、第三者に類推できないようなものに変更する。ここでは、/xxxx_legacy/に変更したとする。
3)セキュリティーに関係の無い静的ファイルを、元のディレクトリ、/legacy/にコピーするか移動するかする。
4)/legacy/index.phpなど、動的ページ作成に必要なPHPファイルを、この記事の最後に書いたコードを参考にして作成する。ファイアーウォール機能はこのPHPファイルが担うことになる。
5)/xxxx_legacy/に移動したwebアプリケーションの設定を、これが適切に動くように修正する。場合によっては、スクリプトの先頭で$_SERVER['REQUEST_URI']を書き換えるなどの荒行事が必要になる場合もある。
これで終了である。元のレガシーアプリケーションへは直接アクセスせず、別個に作成したPHPを介してのアクセスになるため、既知の脆弱性があっても影響を受けることは無い。ただし、ファイアウォール部分のコードは当然ながら慎重に書かなければならない。
「www.example.com」「username」「password」の部分は、個々の設定に応じて書き換えること。
そういったアプリケーションのすべての機能ではなく、一部の機能だけ使いたいというようなことは良くあるだろう。そういう場合、その一部の機能を担うための情報だけサニタイズして与えれば、問題なく使えるはずである。
使用中のwebページでそういったことに相当するケースがあったので、対処してみた。
PHPで書かれた古いwebアプリケーションが、/legacy/というURIにインストールしてあるとする。
1)まず、このレガシーアプリケーションへの直接のアクセスを遮断しなければならない。ここでは、BASIC認証を用いて、遮断する。.htaccessと.htpasswdを適当に設定して、アクセスするにはユーザー名とパスワードを入力しなければならないようにしておく。
2)ディレクトリ名を、第三者に類推できないようなものに変更する。ここでは、/xxxx_legacy/に変更したとする。
3)セキュリティーに関係の無い静的ファイルを、元のディレクトリ、/legacy/にコピーするか移動するかする。
4)/legacy/index.phpなど、動的ページ作成に必要なPHPファイルを、この記事の最後に書いたコードを参考にして作成する。ファイアーウォール機能はこのPHPファイルが担うことになる。
5)/xxxx_legacy/に移動したwebアプリケーションの設定を、これが適切に動くように修正する。場合によっては、スクリプトの先頭で$_SERVER['REQUEST_URI']を書き換えるなどの荒行事が必要になる場合もある。
これで終了である。元のレガシーアプリケーションへは直接アクセスせず、別個に作成したPHPを介してのアクセスになるため、既知の脆弱性があっても影響を受けることは無い。ただし、ファイアウォール部分のコードは当然ながら慎重に書かなければならない。
<?php
// Check the URI
$uri=$_SERVER['REQUEST_URI'];
ここにファイアウォール部分のコードを書き、安全な$uriを作成する。
/legacy/を/xxxx_legacy/に書き換えるなどの措置を取る。
http_request('www.example.com',$uri,'username','password');
function http_request($server,$uri,$user=false,$passwd=false){
// Sanitize the inputs
$server=preg_replace('/[^a-zA-Z0-9\-\.]+/','',$server);
$uri=preg_replace('/[\r\n\x00]+/','',$uri);
// Get the socket
$errno=$errstr=false;
$socket=@fsockopen($server,80,$errno,$errstr,30);
stream_set_blocking($socket,1);
if (!$socket) {
echo '<html><body>'.htmlspecialchars("$errstr ($errno)").'</body></html>';
exit;
}
// Prepare the request
$request = "GET $uri HTTP/1.0\r\n";
$request .= "Host: $server\r\n";
if ($user!==false) $request .= "Authorization: Basic ".base64_encode("$user:$passwd")."\r\n";
$request .= "Connection: Close\r\n\r\n";
// Connect to server
fwrite($socket, $request);
$ret='';
while (!feof($socket)) {
$ret.=fread($socket,1024);
}
fclose($socket);
$i=strpos($ret,"\r\n\r\n");
if ($i!==false) {
$header=substr($ret,0,$i);
foreach(explode("\r\n",$header) as $line){
header($line);
}
$ret=substr($ret,$i+4);
}
echo $ret;
}「www.example.com」「username」「password」の部分は、個々の設定に応じて書き換えること。