在 PHP 中避免代码注入的最佳方法

Posted

技术标签:

【中文标题】在 PHP 中避免代码注入的最佳方法【英文标题】:Best way to avoid code injection in PHP 【发布时间】:2010-09-07 12:31:10 【问题描述】:

我的网站最近被一个在我看来是无辜的代码攻击:

<?php
  if ( isset( $ _GET['page'] ) ) 
    include( $ _GET['page'] . ".php" );
   else 
    include("home.php");
  
?>

那里没有 SQL 调用,所以我不害怕 SQL 注入。但是,显然,SQL 并不是唯一的注入方式。

这个网站有一个解释和几个避免代码注入的例子:http://www.theserverpages.com/articles/webmasters/php/security/Code_Injection_Vulnerabilities_Explained.html

您将如何保护此代码免受代码注入?

【问题讨论】:

【参考方案1】:

使用白名单并确保页面在白名单中:

  $whitelist = array('home', 'page');

  if (in_array($_GET['page'], $whitelist)) 
        include($_GET['page'].'.php');
   else 
        include('home.php');
  

【讨论】:

如果可能,请避免完全动态地包含文件。 include,正如你所经历的,几乎和 eval 一样危险。【参考方案2】:

清理输入的另一种方法是确保其中只有允许的字符(没有“/”、“.”、“:”、...)。但是不要为 bad 字符使用黑名单,而是为允许的字符使用白名单:

$page = preg_replace('[^a-zA-Z0-9]', '', $page);

... 后跟一个 file_exists。

这样您可以确保只执行您想要执行的脚本(例如,这将排除“blabla.inc.php”,因为不允许使用“.”)。

注意:这是一种“hack”,因为用户可以执行“h.o.m.e”,它会给出“主页”页面,因为它所做的只是删除所有被禁止的字符。这并不是为了阻止那些想要在你的页面上添加可爱的东西的“聪明人”,但它会阻止人们做非常糟糕的事情。

顺便说一句:您可以在 .htaccess 文件中做的另一件事是防止明显的攻击尝试:

RewriteEngine on
RewriteCond %QUERY_STRING http[:%] [NC]
RewriteRule .* /–http– [F,NC]
RewriteRule http: /–http– [F,NC]

这样,所有使用“http:”url(和查询字符串)的页面访问都会导致“禁止”错误消息,甚至无法到达 php 脚本。这会减少服务器负载。

但请记住,查询字符串中不允许出现“http”。在某些情况下(可能在填写表格时),您的网站可能需要它。

顺便说一句:如果您能阅读德语:我也有一个关于该主题的blog post。

【讨论】:

我现在对 htaccess 文件知之甚少。对于可以使用特殊字符和空格的路由系统,我有类似的东西。您能否发布一些被您的配置文件阻止的禁止 URL 示例?谢谢 这种方法会比html特殊字符功能更好用吗?【参考方案3】:

接受用户输入时的第一条规则始终是对其进行清理。在这里,您不会在将页面 GET 变量传递给包含之前对其进行清理。在包含该文件之前,您应该执行基本检查以查看该文件是否存在于您的服务器上。

【讨论】:

这仍然不能解决 =n 注入攻击!要对其进行清理,您需要确保输入是安全的、允许的文件。只有白名单就足够了。【参考方案4】:

Pek,除了 sql 注入,甚至是不同类型的代码注入,还有很多事情需要担心。现在可能是深入了解 Web 应用程序安全性的好时机。

在moving from desktop to web development 的上一个问题中,我写道:

OWASP Guide to Building Secure Web Applications and Web Services 应该是任何希望认真对待安全性的网络开发人员(应该是所有网络开发人员)的必读内容。在考虑安全性时,需要遵循许多原则来帮助形成所需的心态。

如果您不适合阅读大篇幅文档,请观看几年前 Mike Andrews 在 Google 举办的关于 How To Break Web Software 的研讨会视频。

【讨论】:

【参考方案5】:

我假设您处理同一目录中的文件:

<?php
if (isset($_GET['page']) && !empty($_GET['page'])) 
  $page = urldecode($_GET['page']);
  $page = basename($page);
  $file = dirname(__FILE__) . "/$page.php";
  if (!file_exists($file)) 
    $file = dirname(__FILE__) . '/home.php';
  
 else 
  $file = dirname(__FILE__) . '/home.php';

include $file;
?>

这不是太漂亮,但应该可以解决您的问题。

【讨论】:

1.你不需要urldecode$_GET。 PHP 总是为你解码。您应该明确指出,basename 在这段代码中是至关重要的。没有它,攻击者可以从父目录中读取敏感文件。【参考方案6】:

pek,对于短期修复,请应用其他用户建议的解决方案之一。对于中长期计划,您应该考虑迁移到现有的 Web 框架之一。它们以可靠、安全的方式处理所有低级内容,例如路由和文件包含,因此您可以专注于核心功能。

不要重新发明***。使用框架。其中任何一个都比没有好。最初的学习时间投资几乎可以立即得到回报。

【讨论】:

【参考方案7】:

到目前为止,一些不错的答案,还值得指出几个 PHP 细节:

文件打开函数使用wrappers 来支持不同的协议。这包括通过本地 Windows 网络、HTTP 和 FTP 等打开文件的能力。因此,在默认配置中,原始问题中的代码可以轻松地用于打开 Internet 及其他地方的任意文件;当然,包括服务器本地磁盘上的所有文件(webbserver 用户可以读取)。 /etc/passwd 总是很有趣。

安全模式和open_basedir 可用于限制特定目录之外的文件被访问。

配置设置allow_url_fopen 也很有用,它可以在使用文件打开功能时禁用对文件的 URL 访问。 ini-set 可用于在运行时设置和取消设置此值。

这些都是不错的后备安全防护,但请使用白名单来包含文件。

【讨论】:

【参考方案8】:

我知道这是一篇非常古老的帖子,我希望您不再需要答案,但我仍然错过了一个非常重要的方面,恕我直言,我喜欢与其他阅读这篇文章的人分享。在基于变量值包含文件的代码中,您在字段值和请求结果之间建立了直接链接(页面变为 page.php)。我认为最好避免这种情况。 对某个页面的请求与该页面的交付之间存在差异。如果您做出这种区分,您可以使用非常好的 url,它们对用户和 SEO 非常友好。您可以创建一个类似于“Spinoza-Ethica”的 URL,而不是像“page”这样的字段值。这是白名单中的键或数据库表中的主键,将返回硬编码的文件名或值。除了普通的白名单之外,该方法还有几个优点:

    后端响应实际上独立于前端请求。如果您想以不同的方式设置后端系统,则无需在前端进行任何更改。

    始终确保以硬编码文件名或数据库中的等效文件名结尾(最好是存储过程的返回值),因为当您使用请求中的信息来构建回复。

    由于您的 URL 独立于后端的交付,因此您永远不必为此类更改重写 htAccess 文件中的 URL。

    呈现给用户的 URL 是用户友好的,告知用户文档的内容。

    漂亮的 URL 对 SEO 非常有利,因为搜索引擎正在搜索相关内容,当您的 URL 与内容一致时,它会获得更好的速度。至少比您的内容与您的内容不符时更好。

    如果您不直接链接到 php 文件,您可以在处理之前将漂亮的 URL 转换为任何其他类型的请求。这为程序员提供了更大的灵活性。

    您必须清理请求,因为您从标准的不可信来源(Web 的其余部分)获取信息。仅使用好的 URL 作为可能的输入使 URL 的清理过程更加简单,因为您可以检查返回的 URL 是否符合您自己的格式。确保 nice URL 的格式不包含在漏洞利用中广泛使用的字符(如 ',",,-,&,; 等)。

【讨论】:

【参考方案9】:

@pek - 这行不通,因为您的数组键是 0 和 1,而不是 'home' 和 'page'。

我相信这段代码应该可以解决问题:

<?php

$whitelist = array(
  'home',
  'page',
);

if(in_array($_GET['page'], $whitelist)) 
  include($_GET['page'] . '.php');
 else 
  include('home.php');


?>

由于您有一个白名单,因此也不需要file_exists()

【讨论】:

【参考方案10】:

认为网址是这样的格式:

www.yourwebsite.com/index.php?page=http://malicodes.com/shellcode.txt

如果 shellcode.txt 运行 SQL 或 PHP 注入,那么您的网站将处于危险之中,对吗?考虑一下,使用白名单会有所帮助。

有一种方法可以过滤所有变量以避免黑客攻击。您可以使用 PHP IDS 或 OSE 安全套件来帮助避免黑客攻击。安装安全套件后,您需要激活套件,这里是指南:

http://www.opensource-excellence.com/shop/ose-security-suite/item/414.html

我建议你打开二层保护,然后所有的POST和GET变量都会被过滤掉,尤其是我提到的那个,如果发现攻击,它会立即报告给你/

安全永远是重中之重

【讨论】:

以上是关于在 PHP 中避免代码注入的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

扯一把 Spring 的三种注入方式,到底哪种注入方式最佳?

MySQL/PHP:允许特殊字符并避免 SQL 注入

当前在 PHP 中防止电子邮件注入攻击的最佳实践是啥?

PHP 代码审计之死磕 SQL 注入

如何防止代码注入攻击在PHP

依赖注入最佳实践和反模式