避免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');
}