上传文件最安全的方法是什么?

时间:2008-11-01 22:00:31

标签: php security file-upload

我工作的公司最近在我们托管的网站上遇到了许多标头注入和文件上传攻击,虽然我们已经修复了标头注入攻击的问题,但我们尚未控制上传漏洞。

我正在尝试设置即插即用型系列的上传脚本,以便内部使用,设计人员可以将其复制到其网站的结构中,修改一些变量并准备好在他们的网站上传表格。我们希望尽可能地限制我们的曝光(我们已经关闭了fopen和shell命令)。

我在网站上搜索了最后一小时,发现许多不同的答案处理依赖外部来源的特定方法。您认为什么是最好的仅限脚本的解决方案,具体到足以用作可靠的保护方法?另外,如果可能的话,我想将语言限制为PHP或伪代码。

编辑:我找到了我的答案(在下面发布),虽然它确实使用了shell命令exec(),但是如果阻止脚本文件上传(这个解决方案确实如此)非常好),你不会遇到任何问题。

3 个答案:

答案 0 :(得分:65)

  1. 仅允许授权用户上传文件。您也可以添加验证码以阻止原始机器人。

  2. 首先,在您的上传表单中设置MAX_FILE_SIZE ,然后在上设置最大文件sizecount服务器

    ini_set('post_max_size', '40M'); //or bigger by multiple files
    ini_set('upload_max_filesize', '40M');
    ini_set('max_file_uploads', 10);
    

    按上传的文件进行大小检查:

    if ($fileInput['size'] > $sizeLimit)
        ; //handle size error here
    
  3. 您应使用$_FILESmove_uploaded_file() 将上传的文件放入正确的目录中,或者如果您想要处理它,请查看{{ 1}}。 (这些函数用于防止由is_uploaded_file()引起的文件名注入。)

    register_globals

    始终生成随机ID,而不是使用原始文件名

  4. 创建子域 ,例如http://static.example.com 或至少在{{1}之外的新目录对于上传的文件。此子域或目录不应执行任何文件。在服务器配置中设置它,或者在目录下设置in a .htaccess file

    $uploadStoragePath = '/file_storage';
    $fileInput = $_FILES['image'];
    
    if ($fileInput['error'] != UPLOAD_ERR_OK)
        ; //handle upload error here, see http://php.net/manual/en/features.file-upload.errors.php
    
    //size check here
    
    $temporaryName = $fileInput['tmp_name'];
    $extension = pathinfo($fileInput['name'], PATHINFO_EXTENSION);
    
    //mime check, chmod, etc. here
    
    $name = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); //true random id
    
    move_uploaded_file($temporaryName, $uploadStoragePath.'/'.$name.'.'.$extension);
    

    同样设置with chmod()

    public_html

    对新上传的文件也使用 SetHandler none SetHandler default-handler Options -ExecCGI php_flag engine off 并将其设置在目录中。

  5. 您应该检查黑客发送的 mime类型 。您应该创建允许的 mime类型白名单仅限图片,如果不需要任何其他格式。任何其他格式都是安全威胁。图像也是,但至少我们有工具来处理它们...
    损坏的内容例如:图片文件中的 HTML 可能会导致content sniffing vulnerability的浏览器出现 XSS 。当损坏的内容是 PHP 代码时,它可以与 eval injection 漏洞结合使用。

        $noExecMode = 0644;
        chmod($uploadedFile, $noExecMode);
    

    尽量避免这种情况,例如使用chmod()而不是手动包含php文件...
    通过首先处理 javascript注入,您必须关闭浏览器中的 xss 内容嗅探内容嗅探问题是旧版 msie 的典型问题,我认为其他浏览器可以很好地过滤它们。无论如何,你可以用一堆标题来防止这些问题。 (并非每个浏览器都完全支持,但这是您在客户端可以做的最好的。)

    $userContent = '../uploads/malicious.jpg';
    include('includes/'.$userContent);
    

    您可以使用Imagick identify检查文件是否已损坏,但这并不意味着提供完整保护。

    class autoloader

    如果您想提供其他 mime类型,则应始终强制下载 ,绝不要包含他们进入网页,除非你真的知道你在做什么......

    Strict-Transport-Security: max-age={your-max-age}
    X-Content-Type-Options: nosniff
    X-Frame-Options: deny
    X-XSS-Protection: 1; mode=block
    Content-Security-Policy: {your-security-policy}
    
  6. 可以使用包含代码的有效图像文件,例如 exif 数据。因此,如果内容对您不重要,则必须从图像中清除 exif 。您可以使用 try { $uploadedImage = new Imagick($uploadedFile); $attributes = $uploadedImage->identifyImage(); $format = $image->getImageFormat(); var_dump($attributes, $format); } catch (ImagickException $exception) { //handle damaged or corrupted images } X-Download-Options: noopen Content-Disposition: attachment; filename=untrustedfile.html 执行此操作,但这两者都需要重新打包文件。您可以找到 Imagick 作为替代方案。 我认为清除 exif 的最简单方法是使用 GD save them as PNG with highest quality加载图像。因此,图像不会丢失质量,并且 exif 标记将被清除,因为 GD 无法处理它。使用上传为 PNG 的图片也可以这样做...
    如果您要提取 exif 数据,如果GDexiftool来自用户,请不要使用preg_replace(),因为这会导致 eval injection ...如有必要,请使用pattern代替replacement。 (复制粘贴代码中的常见错误。) 如果您的网站存在 eval注入漏洞,则 Exif 数据可能会出现问题,例如,如果您在某处使用preg_replace_callback()

  7. 永远不要使用eval regex flaginclude($userInput)上传文件,将其作为静态投放或使用include()require(),或者任何其他文件读取功能,如果你想控制访问。
    它很少可用,但我认为将file_get_contents()标头与sendfile apache module一起使用的最佳方法。通过标题,永远不要使用用户输入而不进行验证或清理,因为这将导致 HTTP标头注入。 如果您不需要访问控制(表示:只有授权用户才能看到上传的文件),请使用您的网络服务器提供文件。它快得多......

  8. 使用antivir 检查上传的文件(如果有的话)。

  9. 始终使用综合保护,而不仅仅是单一方法。破坏你的防御将更加困难......

答案 1 :(得分:37)

最好的解决方案是恕我直言,将包含上传文件的目录放在“web”环境之外,并使用脚本使其可下载。通过这种方式,即使某人上传了一个脚本,也无法通过从浏览器中调用它来执行它,也不必检查上传文件的类型。

答案 2 :(得分:1)

使用并配置Hardened-PHP使用move_uploaded_file$_FILES superglobal创建普通脚本。最简单的脚本,最安全(至少,与正在运行的PHP版本本身一样安全)