Posts Tagged ‘php’

How to Disable PHP mail() Per-Domain/Vhost without Suhosin or FPM/suEXEC

So I’m tired of my goddamned WordPress blog (yep, this one) getting compromised and being used as a platform for spammers or phishers and so on.

Getting pwn’t every so often is pretty much just a fact of using popular shrinkwrapware and being a lazy updater. But that’s no reason to change our ways when we can just mitigate the damage – namely getting your web server’s IP on to all those friendly RBLs and interrupting legit e-mail notification delivery.

The good news is you can use disable_functions in your php.ini to disable functions globally.

The bad news is you can’t set disable_functions on a per-domain or vhost basis unless you’re using FPM/suEXEC or the like.

The worse news is suhosin, a really sweet PHP security patch that I’ve written about before and which would give us the ability to do this has been abandoned for about two years now and there is no official support for php 5.4 and later.

So I’d like to give Dave Lachapelle a big round of applause for pointing out that even if you can’t kill mail() you can at least cripple it. From http://www.davelachapelle.ca/2009/08/05/php-mail-abuse/:

…PHP doesn’t support setting disable_functions in the php_admin_value flag.

So, after a bit of searching, I decided to just add the following to each site’s .htaccess files:

php_admin_value sendmail_path “/dev/null”

Essentially sending all e-mail to /dev/null for that particular site. Perhaps not the most elegant solution, but it was effective…

Maybe not elegant, but surely classy.

Stay classy, Dave.

Crossfire Ping Monitoring Part 3: The Presentation

In part one of this series we set up a ping monitoring script on each of our servers. In part two we covered importing the data to a centralized database.
The number of potential different uses for this information is making my head spin, but in this installment we will focus on spitting it out out in a way which we can gain some basic insights.

<?php

$sql_host = '';
$sql_user = '';
$sql_pass = '';
$sql_base = '';

$sql_h = mysql_pconnect($sql_host, $sql_user, $sql_pass);
$sql_d = mysql_select_db($sql_base, $sql_h);

$now = empty($_GET['time']) ? time() : mysql_real_escape_string($_GET['time']);
$intervals = 100;
$interval = 300; // 5 minutes
$before = $now - $interval;

print("<html>
<head>
<meta http-equiv=\"refresh\" content=\"$interval\">
<title>Crossfire PoC</title>
<style type=\"text/css\">
body{background: white; color: black; margin: 10px; padding: 0px; font-family: Arial, sans; font-size: 9pt;}
td, th{font-family: Arial, sans; font-size: 8pt; white-space: nowrap;}
td{vertical-align: top;}
tr.darkrow{background-color: #dddddd;}
tr.lightrow{background-color: #eeeeee;}
a{text-decoration: none;}
h1{margin: 0px 0px 10px 0px; padding: 0px; border-bottom: 1px solid black;}
h2{margin: 10px 0px 5px 0px; padding: 0px; border-top: 1px solid black;}
</style>
</head>
<body>
<h1>Crossfire Ping Monitoring PoC on <a href=\"http://foxpa.ws/\">foxpa.ws</a></h1>
This is a proof-of-concept showing how it is possible to use a group of hosts which constantly ping-monitor each other to visualize connectivity problems and general downtime.<br>
Each column is a different host. Each row is a slice of 5 minutes. Each cell contains how that host was seen by every other host in the pool during that time.<br>
The monitoring script pings each host 4 times every 5 minutes. If a host does not respond at all it will show up in <span style=\"color: red; font-weight: bolder;\">red</span>. If a host
responds to one or more pings it will show up in black. An empty cell means no host reported any problems.
<br><br>
<a href=\"?time=".($now - ($interval * $intervals))."\">< back</a>
   •   
<a href=\"?time=".($now + ($interval * $intervals))."\"> forward ></a>
<table>
<tr class=\"darkrow\">
<th>Time</th>");

$hosts = array();
$h_result = mysql_query("select * from `hosts` order by `id` desc");
while($h_array = mysql_fetch_array($h_result))
{
	$hosts["{$h_array['id']}"] = $h_array;

	print("<th>{$h_array['name']}</th>");
}

print("</tr>");

$counter = 0;
while($counter < $intervals)
{
	$after = $before + $interval;

	if($counter % 2)
		$class = 'dark';
	else
		$class = 'light';

	print("<tr class=\"{$class}row\"><th>".date("Md/y H:i", $before).' - '.date("H:i", $after).'</th>');

	foreach($hosts as $host)
	{
		print("<td>");
		$log_result = mysql_query("select * from `logs` where `date` > '$before' and `date` < '$after' and `ip` = '{$host['ip']}'");
		while($log_object = mysql_fetch_object($log_result))
		{
			if($log_object->packets == 0){print("<span style=\"color: red; font-weight: bolder;\">");}
			print('• '.$hosts["{$log_object->host}"]['name']." reports {$log_object->packets} replies.");
			if($log_object->packets == 0){print("</span>");}
			print("<br>");
		}
		print("</td>");
	}

	print('</tr>');
	$before = $before - $interval;
	$counter++;
}

print("
</table>

<a href=\"?time=".($now - ($interval * $intervals))."\">< back</a>
   •   
<a href=\"?time=".($now + ($interval * $intervals))."\"> forward ></a>

<h2>So Can You!</h2>
<a href=\"http://foxpa.ws/2013/05/28/crossfire-ping-monitoring-part-1-the-setup/\">Crossfire Ping Monitoring Part 1: The Setup</a><br>
<a href=\"http://foxpa.ws/2013/05/28/crossfire-ping-monitoring-part-2-collecting-the-data/\">Crossfire Ping Monitoring Part 2: Collecting the Data</a><br>
<a href=\"http://foxpa.ws/2013/05/28/crossfire-ping-monitoring-part-3-the-presentation/\">Crossfire Ping Monitoring Part 3: The Presentation</a>

</body>
</html>");

?>

Again, we have a very inefficient (in terms of SQL queries) script and I encourage anyone who is interested to improve upon it.

You can see my live PoC at http://foxpa.ws/crossfire/.

Crossfire Ping Monitoring Part 2: Collecting the Data

In the previous installment of this series we set up a ping monitoring script on each of our servers set up to ping every other server in the pool. For this example the logs are made available for download via HTTP.

We’re going to re-use the HTTP GET class I posted earlier this week: Portable PHP HTTP(S) GET Request with HTTP Basic Authentication.

Create two tables, one will contain our hosts and one will contain the logs:

CREATE TABLE `hosts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `ip` varchar(64) NOT NULL,
  `location` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO `hosts` VALUES (1,'Waveride','10.10.10.10','http://my.vps.net/crossfire.log'),(2,'VPS Database','10.10.10.11','http://my.vps.net/crossfire.log'),(3,'Iniz','10.10.10.12','http://my.vps.net/crossfire.log'),(4,'Prometeus','10.10.10.13','http://my.vps.net/crossfire.log'),(5,'domVPS','10.10.10.14','http://my.vps.net/crossfire.log');


CREATE TABLE `logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date` int(11) NOT NULL,
  `collected` int(11) NOT NULL,
  `host` int(11) NOT NULL,
  `ip` varchar(64) NOT NULL,
  `packets` int(11) NOT NULL,
  PRIMARY KEY (`id`)
);

Now we’re going to create a cron script that will fetch the data, parse it, and put it into the database. This is a very inefficient script; I’m sure you can find a better way of running the checks. We’re going to run through the entire log file every time we fetch it so if “headquarters” isn’t able to connect to the VPS for a while we won’t miss any entries. A better way of doing this would be to find the last entry from that host in the logs then skip all of the crossfire.log entries to that point.

<?php

$sql_host = '';
$sql_user = '';
$sql_pass = '';
$sql_base = '';

$sql_h = mysql_pconnect($sql_host, $sql_user, $sql_pass);
$sql_d = mysql_select_db($sql_base, $sql_h);

class HTTPRequest
{
    var $_fp;          // HTTP socket
    var $_url;         // full URL
    var $_host;        // HTTP host
    var $_protocol;    // protocol (HTTP/HTTPS)
    var $_uri;         // request URI
    var $_port;        // port
    var $_user;        // HTTP Basic Auth User
    var $_pass;        // HTTP Basic Auth Password
   
    // scan url
    function _scan_url()
    {
        $req = $this->_url;
       
        $pos = strpos($req, '://');
        $this->_protocol = strtolower(substr($req, 0, $pos));
       
        $req = substr($req, $pos+3);
        $pos = strpos($req, '/');
        if($pos === false)
            $pos = strlen($req);
        $host = substr($req, 0, $pos);
       
        if(strpos($host, ':') !== false)
        {
            list($this->_host, $this->_port) = explode(':', $host);
        }
        else
        {
            $this->_host = $host;
            $this->_port = ($this->_protocol == 'https') ? 443 : 80;
        }
       
        $this->_uri = substr($req, $pos);
        if($this->_uri == '')
            $this->_uri = '/';
    }
   
    // constructor
    function HTTPRequest($url, $user='', $pass='')
    {
        $this->_url = $url;
        $this->_scan_url();
        $this->_user = $user;
        $this->_pass = $pass;
    }
   
    // download URL to string
    function DownloadToString()
    {
        $crlf = "\r\n";
       
        // generate request
        $req = 'GET ' . $this->_uri . ' HTTP/1.0' . $crlf
            .    'Host: ' . $this->_host . $crlf;
            if(!empty($this->_user))
                 $req .= "Authorization: Basic " . base64_encode($this->_user . ':' . $this->_pass) . $crlf;
       $req .= $crlf;
       
        // fetch
        $this->_fp = fsockopen(($this->_protocol == 'https' ? 'ssl://' : '') . $this->_host, $this->_port);
        fwrite($this->_fp, $req);
        while(is_resource($this->_fp) && $this->_fp && !feof($this->_fp))
            $response .= fread($this->_fp, 1024);
        fclose($this->_fp);
       
        // split header and body
        $pos = strpos($response, $crlf . $crlf);
        if($pos === false)
            return($response);
        $header = substr($response, 0, $pos);
        $body = substr($response, $pos + 2 * strlen($crlf));
       
        // parse headers
        $headers = array();
        $lines = explode($crlf, $header);
        foreach($lines as $line)
            if(($pos = strpos($line, ':')) !== false)
                $headers[strtolower(trim(substr($line, 0, $pos)))] = trim(substr($line, $pos+1));
       
        // redirection?
        if(isset($headers['location']))
        {
            $http = new HTTPRequest($headers['location']);
            return($http->DownloadToString($http));
        }
        else
        {
            return($body);
        }
    }
}

$h_result = mysql_query("select * from `hosts` order by `id` desc");
while($h_object = mysql_fetch_object($h_result))
{
	$r = new HTTPRequest($h_object->location);
	$response = trim($r->DownloadToString());

	$lines = explode("\n", $response);
	foreach($lines as $line)
	{
		$line = str_replace('  ', ' ', $line);
		$e = explode(' ', $line);
		$date = strtotime($e[0].' '.$e[1].' '.$e[2].' '.$e[3].' '.$e[4].' '.$e[5]);
		$ip = mysql_real_escape_string($e[6]);
		$packets = (int) $e[9];
		$check_result = mysql_query("select * from `logs` where `host` = '$h_object->id' and `date` = '$date' and `ip` = '$ip'");
		if(mysql_num_rows($check_result) == 0)
		{
			mysql_query("insert into `logs` (`date`, `collected`, `host`, `ip`, `packets`) values ('$date', '".time()."', '{$h_object->id}', '$ip', '$packets')");
		}
	}
}

?>

Drop this into your crontab to make it collect every so often, I’m going with 5 minutes here but it doesn’t matter if you only collect the logs daily.

*/5 * * * * apache /usr/bin/php /path/to/import.php
Return top
foxpa.ws
Online Marketing Toplist
Internet
Technology Blogs - Blog Rankings

Internet Blogs - BlogCatalog Blog Directory

Bad Karma Networks

Please Donate!


Made in Canada  •  There's a fox in the Gibson!  •  2010-12