有没有办法判断用户上传的文件是否是服务器脚本?

时间:2018-05-28 12:34:34

标签: php security

我正在为我的网络主机编写一些服务器端脚本,它将为客户端应用程序执行大量自动更新。只要用户使用应用程序名称和密码正确地将自己标识为所有者,他们就应该能够上传到自动创建的服务器上的存储库;但是,我不希望允许上传任何脚本,以便它可以在服务器上运行。我打算允许人们创建他们自己的存储库而不必问我,所以无论如何阻止这个潜在的漏洞?

这是存储库的设置代码,名为APPSEtUP.php:

<?php
$app = str_replace("_", " ", TDecode($_POST['app']));
$pass = str_replace("_", " ", TDecode($_POST['pass']));
$command = str_replace("_", " ", TDecode($_POST['command']));
$worked = false;
if ($command == "SETUP_API") { $worked = SETUP_API($app, $pass); }
if ($command == "MAKE_DIR") { $worked = Make_Directory($app, $pass, TDecode($_POST['DIR']), TDecode($_POST['up'])); }
if ($worked) { echo "SUCCESS!"; }
return;

function Make_Directory($api, $pw, $dir, $up) {
    $path = $_SERVER['REQUEST_URI'];
    if ($path == "/scripts/APPSETUP.php") { echo "API FAILURE: 008\r\n"; return false; }
    if (!startsWith($path, "/scripts/Apps/")) { echo "API FAILURE: 009\r\n"; return false; }
    if (!Get_API_PW("./security.LOCK", $pass)) { echo "API FAILURE: 010\r\n"; return false; }
    if ($path != "/scripts/Apps/".$api."/APPSETUP.php") { echo "API FAILURE: 011\r\n"; return false; }
    while (startsWith($dir, ".") || startsWith($dir, "/")) { $dir = substr($dir, -(strlen($dir)-1)); }
    while (endsWith($dir, "/")) { $dir = substr($dir, 0, strlen($dir)-1); }
    if (!(file_exists("./".$dir."/") || mkdir("./".$dir."/", "0777", true))) { echo "API FAILURE: 012\r\n"; return false; }
    if ($up == "true" && !(file_exists("./".$dir."/UploadFile.php") || copy("./UploadFile.php", "./".$dir."/UploadFile.php"))) {
        echo "API FAILURE: 013\r\n"; return false;
    } return true;
}

function startsWith($haystack, $needle) {
    $length = strlen($needle);
    return (substr($haystack, 0, $length) === $needle);
}

function endsWith($haystack, $needle) {
    $length = strlen($needle);
    return $length === 0 || (substr($haystack, -$length) === $needle);
}

function SETUP_API($api, $pw) {
    $temp1 = "./Templates/USERLOG.php";
    $temp2 = "./Templates/UploadFile.php";
    $temp3 = "./APPSETUP.php";
    $dest1 = "./Apps/";
    $dest2 = "./Apps/".$api."/";
    $dest3 = "./Apps/".$api."/USERLOG.php";
    $dest4 = "./Apps/".$api."/security.LOCK";
    $dest5 = "./Apps/".$api."/UploadFIle.php";
    $dest6 = "./Apps/".$api."/APPSETUP.php";
    if (!(file_exists($dest1) || mkdir($dest1, 0777, true))) { echo "API FAILURE: 001\r\n"; return false; }
    if (!(file_exists($dest2) || mkdir($dest2, 0777, true))) { echo "API FAILURE: 002\r\n"; return false; }
    if (!file_exists($dest4)) { if (!App_Reset($dest2, $dest4, $pw)) { echo "API FAILURE: 003\r\n"; return false; } } 
    if (!Get_API_PW($dest4, $pw)) { echo "API FAILURE: 004\r\n"; return false; }
    if (!copy($temp1, $dest3)) { echo "API FAILURE: 005\r\n"; return false; }
    if (!copy($temp2, $dest5)) { echo "API FAILURE: 006\r\n"; return false; }
    if (!copy($temp3, $dest6)) { echo "API FAILURE: 007\r\n"; return false; }
    return true;
}

function App_Reset($api, $sec, $pw) {
    try {
        Delete_Bad_App($api);
        $pWriter = fopen($sec, "w");
        fwrite($pWriter, TEncode($pw));
        fclose($pWriter);
        return true;
    } catch (exception $e) { return false; }
}

function Delete_Bad_App($api) {
    $di = new RecursiveDirectoryIterator($api, FilesystemIterator::SKIP_DOTS);
    $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
    foreach ( $ri as $file ) {
        $file->isDir() ?  rmdir($file) : unlink($file);
    } return;
}

function Get_API_PW($sec, $guess) {
    try {
        $pReader = fopen($sec, "r");
        $pw = TDecode(fread($pReader, filesize($sec)));
        fclose($pReader);
        return $pw == $guess;
    } catch (exception $e) { return false; }
}

function TriceInt($c) {
    $b = unpack("C*", $c)[1] % 255;
    $foo = (string)$b;
    while (strlen($foo) < 3) { $foo = "0".$foo; }
    return $foo;
}

function TEncode($str) {
    if (TEncoded($str)) { return $str; }
    return implode(array_map("TriceInt", str_split($str, 1)));
}

function TDecode($str) {
    if (!TEncoded($str)) { return $str; }
    return implode(array_map("chr", array_map('intval', str_split($str, 3))));
}

function TEncoded($str) {
    return (ctype_digit($str) && strlen($str) % 3 == 0);
}
?>

这是用于上传文件的脚本。

<?php
$uploads_dir = './'; 
if ($_FILES["file"]["error"] == UPLOAD_ERR_OK) {
    $tmp_name = $_FILES["file"]["tmp_name"];
    $name = $_FILES["file"]["name"];
    move_uploaded_file($tmp_name, "$uploads_dir/$name");
}
?>

请注意,上传脚本名为UploadFile.php,位于安装脚本中引用的模板文件夹中。

1 个答案:

答案 0 :(得分:1)

因此,根据问题评论中的建议,我尝试使用.htaccess禁用脚本执行,但因为我使用的是由godaddy托管的Windows Server,我使用了错误的文件类型来执行此操作。我发现这个链接解释了如何在web.config中做同样的事情,它适用于Windows服务器。

http://issues.umbraco.org/issue/U4-8472

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <clear />
            <add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" />
        </handlers>
    </system.webServer>
</configuration>
相关问题