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

避免PHP中代码注入的最佳方法,php,security,code-injection,Php,Security,Code Injection,我的网站最近遭到攻击,在我看来,这是一个无辜的代码: <?php if ( isset( $ _GET['page'] ) ) { include( $ _GET['page'] . ".php" ); } else { include("home.php"); } ?> 这里没有SQL调用,所以我不怕SQL注入。但是,显然,SQL并不是唯一一种注入 本网站提供了避免代码注入的说明和一些示例: 如何保护此代码不受代码注入的影响?我假设您处理同一目录中

我的网站最近遭到攻击,在我看来,这是一个无辜的代码:

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

这里没有SQL调用,所以我不怕SQL注入。但是,显然,SQL并不是唯一一种注入

本网站提供了避免代码注入的说明和一些示例:


如何保护此代码不受代码注入的影响?

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

<?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规则始终是对其进行清理。在这里,在将页面GET变量传递到include之前,不需要对其进行清理。在包含该文件之前,您应该执行基本检查,以查看该文件是否存在于服务器上。

使用白名单,并确保该页面位于白名单中:

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

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

另一种清理输入的方法是确保其中只包含允许的字符(no“/”、“、”:“、…)。但是,不要将黑名单用于坏字符,而将白名单用于允许的字符:

$page = preg_replace('[^a-zA-Z0-9]', '', $page);
。。。后面跟着一个文件u

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

注意:这是一种“黑客行为”,因为用户可以执行“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”。您的网站在某些情况下可能需要它(可能在填写表单时)


顺便说一句:如果你能读德语的话:我也有一个关于这个话题的建议。

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


不要重新发明轮子。使用框架。其中任何一个都比没有好。学习it的初期投资几乎可以立即获得回报。

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

从上一个问题开始,我写道:

对于希望认真对待安全性的任何web开发人员(应该是所有web开发人员)来说,这应该是必读的。有许多原则可以遵循,这有助于在考虑安全性时保持所需的心态

如果你不适合阅读一个大文件,那么看看迈克·安德鲁斯几年前在谷歌举办的研讨会的视频


到目前为止,有一些很好的答案,也值得指出一些PHP细节:

文件打开函数用于支持不同的协议。这包括通过本地windows网络、HTTP和FTP等打开文件的能力。因此,在默认配置中,原始问题中的代码可以很容易地用于打开internet上及以外的任意文件;当然,包括服务器本地磁盘上的所有文件(Web服务器用户可以读取)
/etc/passwd
总是很有趣的一个

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

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


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

@pek-这不起作用,因为您的数组键是0和1,而不是“主页”和“页面”

我相信,这段代码应该可以做到这一点:

<?php

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

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

?>


因为你有一个白名单,所以也不需要
file_exists()

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

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

  if (in_array($_GET['page'], $whitelist)) {
        include($_GET['page'].'.php');
  } else {
        include('home.php');
  }
  • 后端响应实际上独立于前端请求。如果希望以不同的方式设置后端系统,则不必更改前端的任何内容

  • 始终确保以数据库中的硬编码文件名或等效文件名结束(最好是存储过程中的返回值),因为当您使用请求中的信息来构建响应时,这会带来麻烦

  • 因为您的URL独立于后端的交付,所以对于这种更改,您永远不必在htAccess文件中重写URL

  • 向用户表示的URL是用户友好的,通知用户abo