每个专业的PHP开发人员都知道用户上传的文件极其危险。后端和前端黑客都可以使用它们来做事。大约一个月前,我在reddit上看到一个PHP上传漏洞检测,因此,我决定写一篇文章。用户darpernter问了一个棘手的问题:即使我将其重命名为“helloworld.txt”,攻击者仍然能够运行他的php脚本吗?最上面的回答是:如果文件后缀改成.txt那么就不会是作为php文件执行的,所以大可放心,不过要仔细检查一下是不是上传后缀为.php的。TXT。对不起,问题的正确答案不是。虽然并非所有上述答案都是错误的,但它们显然并不全面。令人惊讶的是,大多数答案都非常相似。我想把问题解释清楚。所以我要谈论的话题变得更大了,我决定让它变得更大。问题人们允许用户上传文件,但担心用户上传的文件会在服务器上执行。首先看看php文件是如何执行的。假设一个有php环境的服务器,它通常有两种方式来对外执行php文件。一种是直接使用URL请求文件,例如http://example.com/somefile.php。第二个就是现在常用的php,把所有的请求都转发到index.php,然后莫名其妙的导入这个文件里面的其他文件。因此,从php文件运行代码有两种方式:执行文件或者使用include/include_once/require/require_once引入其他需要运行的文件。实际上还有第三种方式:eval()函数。它可以将传入的字符串作为php代码执行。大多数CMS系统都使用此函数来执行存储在数据库中的代码。eval()函数是非常危险的,但是如果你使用它,通常意味着你确定你在做危险的操作,并且你没有其他选择。实际上,eval()有其用途,在某些情况下非常有用。但如果您是新手,我不建议您使用它。请参阅OWASP上的这篇文章。我在上面写了很多。所以,执行文件中的代码有两种方式:直接执行或者包含在要执行的文件中。那么如何避免这种情况的发生呢?解决方案?我们怎么知道一个文件包含php代码呢?查看扩展名,如果它以.php结尾,比如somefile.php我们认为它里面有php代码。如果网站根目录下有somefile.php文件,那么在浏览器中访问http://example.com/somefile.php,就会执行这个文件,并将内容输出到浏览器。但是如果我重命名这个文件会发生什么?如果我将它重命名为somefile.txt或somefile.jpg会怎样?我会得到什么我会得到它的内容。它不会被执行。它将直接从磁盘(或缓存)发送。reddit社区上的答案在这一点上是正确的。重命名可以防止文件被意外执行,那么为什么我认为这个解决方案是错误的呢?我确定您注意到了我在“解决方法”之后放置的问号。这个问号是有道理的。如今,大多数网站的URL上几乎看不到单个php文件。而且即使有,也是人为伪造的,因为URL上需要.php以实现与旧URL的向后兼容。现在大部分php代码都是动态导入的,因为所有请求都发送到网站根目录中的index.php。这个文件按照一定的规则导入其他的php文件。此类规则可能(或将会)被恶意使用。如果您的应用程序的规则允许引入用户的文件,那么该应用程序就容易受到攻击,您应该立即采取措施防止用户的文件被执行。如何防止用户上传的文件被带入?重命名文件名可以吗?---‰不,这不可能!PHP解析器不关心文件扩展名。事实上,所有的程序都不关心。双击该文件,该文件将被相应的程序打开。文件扩展名只是帮助操作系统识别用于打开文件的程序。只要程序具有读取文件的能力,程序就可以打开任何文件。有时程序拒绝打开和操作文件。但这不是因为后缀,而是因为文件的内容。服务器通常设置为执行.php文件并输出执行结果。如果您请求图像.jpg---将完全从磁盘返回它。如果您要求服务器以某种方式运行jpeg图像,会发生什么情况?服务器会不会执行?图片来源:Echo/Cultura/GettyImages该程序不关心文件名。它甚至不关心文件是否有名称,或者它是否是一个文件。从文件执行PHP代码需要什么?至少有两种情况PHP可以执行代码:代码在标记之间代码在=和?>标记之间该标签内的内容仍将被执行。这是给你的照片:那张照片没有任何问题,现在很纯净。但是您可能知道JPEG格式允许将一些注释添加到文件中。例如,拍摄照片的相机型号或坐标地址。如果我们尝试将一些PHP代码放入其中并尝试include或require怎么办?让我们找出答案!问题!1将此图像下载到您的硬盘。或者您可以自己获取JPEG图像。使用什么文件格式并不重要。我推荐一个JPEG文件来演示,主要是因为它是一张图片,而且很容易在里面进行文本编辑。我使用的是Windows笔记本电脑,但手边没有Apple或Linux(或其他基于UNIX的系统)笔记本电脑。所以我稍后会发布这个操作系统的屏幕截图。但我相信你也能做到。使用以下PHP代码创建一个文件:
Problem?
是的,有问题!";php信息();?>刷新页面!显然出了点问题!您在页面上看到此图像。该页面的PHP代码中也存在相同的图像。图像的代码也被执行。我们该怎么办?!!1长话短说:如果我们不在程序中包含这些不安全的文件,文件中的脚本将不会执行。仔细看下面的例子。最终答案?如果有人看到我在某个地方错了-请纠正我,这是一个严重的问题。PHP是一种脚本语言。您总是需要引用一些具有动态组合路径的文件。因此,为了保护您的服务器,您必须检查路径并防止您的站点文件与用户上传或创建的文件混淆。如果用户的文件与应用程序文件是分开的,您可以在使用上传或创建文件之前检查文件的路径。如果它位于您的应用程序脚本允许的文件夹中-那么它可以使用include_once或require或require_once引入此文件。如果不是——那么就不要介绍它。如何检查?这很简单。您只需将$folder(文件)路径与允许程序导入文件的路径文件夹($file)进行比较。//不好的例子,不要使用它!if(substr($file,0,strlen($folder))===$folder){include$file;}如果$folder的存放路径是/path/to/folder,$file的存放路径是/path/to/folder/and/file,然后我们在代码中使用substr()函数将它们的路径转为负数字符串进行判断。如果文件位于不同的文件夹中---字符串将不相等。反之亦然。上面的代码有两个重要的问题。如果文件路径是/path/to/folderABC/and/file,显然文件也不在允许导入的文件夹中。这可以通过向两个路径添加斜杠来防止。我们在这里为文件路径添加斜线并不重要,因为我们只需要比较两个字符串。例如:如果文件夹路径为/path/to/folder并且文件路径为/path/to/folder/and/file,则从文件中提取的字符数与文件夹的字符数相同,则$folder将为/path/to/文件夹。又如文件夹路径为/path/to/folder,文件路径为/path/to/folderABC/and/file,则从文件中提取文件夹的字符数与$folder相同,又会变成/path/to/folder,这是错误的,这不是我们期望的结果。因此,/path/to/folder/加斜线后,与/path/to/folder/and/file的提取部分/path/to/folder/相同是安全的。如果将/path/to/folder/与/path/to/folderABC/和/file/path/to/folderA的提取部分进行比较,很明显这两个字符串是不一样的。这就是我们所期望的。但是还有一个问题。这并不明显。我敢肯定,如果我问你,你会看到这里有一个灾难性的洞——而且你不会猜到它在哪里。你可能在你的经验中使用过这些东西,甚至可能在今天。现在,您将看到漏洞是如何微妙而明显的。向下看。/../想象一个很常见的场景。有这样一个网站。用户可以将文件上传到站点。所有文件都位于特定目录中。有一个包含用户文件的脚本。该脚本从上到下查找是否包含用户输入的(直接或间接)路径---那么该脚本可以通过以下方式伪造路径:/path/to/folder/../../。./../../../../another/path/from/root/例如。用户发起请求,您的脚本包含一个基于用户输入的路径的文件,如下所示:include$folder。“/”。$_GET['一些'];//或$_POST,或任何你有大麻烦的地方。用户发送../../../../../../etc/.passwd或其他请求的那一天,哭泣。否则。如果有人告诉您的脚本加载他们想要的文件,那您就完蛋了。它不必只存在于用户文件中。它可能是您的CMS或您自己的文件的某个插件(不要相信任何人),甚至是您的应用程序逻辑中的错误等。或者用户可能会上传一个名为file.php的文件,您可以像任何其他用户文件一样将其放在特定文件夹中:move_uploaded_file($filename,$folder.'/'.$filename);user文件存储在那里,你必须经常检查这个文件夹中没有文件,到目前为止,一切看起来都很正常。通常,用户发送给你的文件不会包含斜杠或其他特殊字符,因为这是系统文件系统所禁止的。这是因为通常浏览器发送给您的文件是在真实文件系统中创建的,其名称是某个真实文件的名称。但是http请求允许用户发送任何字符。因此,如果有人伪造一个创建名为../../../../../../var/www/yoursite.com/index.php的文件的请求——这行代码将覆盖您的index.php文件,如果index.php在上面的路径中。所有初学者都希望通过过滤“..”或斜线来解决这个问题,但这种做法是错误的,因为你对安全还很陌生。同时,您必须(是的,必须)了解一件简单的事情:您永远无法获得足够的安全和密码学知识。这句话的意思是,如果你理解了“双点和斜杠”漏洞,并不意味着你知道所有其他的漏洞、攻击和其他特殊字符。数据库时可能发生的代码转换。解决方案和答案为了解决这个问题,PHP中内置了一些特殊的函数方法,专门用于这种情况。basename()第一个解决方案---basename()它从路径的末尾提取路径的一部分,直到遇到第一个斜杠,但忽略字符串末尾的斜杠,参见示例。在任何情况下,您都会收到一个安全的文件名。如果您感到安全-那么是的,它是安全的。如果它被用于恶意上传-您可以使用它来验证文件名是否安全。realpath()另一种解决方案---realpath()将上传的文件路径转换为规范化的绝对??路径名,从根开始,完全不包含任何不安全元素。它甚至会将符号链接转换为该符号链接指向的路径。所以可以使用这两个函数来查看上传文件的路径。检查此文件路径是否真的属于此文件夹路径。我的代码我写了一个函数来提供上面的检查。我不是专家,所以使用风险自负。代码如下所示。