<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Shawn Wilsher &#187; PHP</title>
	<atom:link href="http://shawnwilsher.com/archives/category/code/php/feed" rel="self" type="application/rss+xml" />
	<link>http://shawnwilsher.com</link>
	<description></description>
	<lastBuildDate>Sun, 04 Dec 2011 10:37:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Secure PHP on the Web</title>
		<link>http://shawnwilsher.com/archives/83</link>
		<comments>http://shawnwilsher.com/archives/83#comments</comments>
		<pubDate>Tue, 17 Apr 2007 02:45:52 +0000</pubDate>
		<dc:creator>Shawn Wilsher</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://shawnwilsher.com/archives/83</guid>
		<description><![CDATA[PHP is a powerful and easy language to learn. As a developer, you must use that power in a safe and effective manner. While being easy to learn means that it is easy to pick up, it also means that you may not have any experience with writing secure code. Writing secure code is a [...]]]></description>
			<content:encoded><![CDATA[<p>PHP is a powerful and easy language to learn. As a developer, you must use that power in a safe and effective manner. While being easy to learn means that it is easy to pick up, it also means that you may not have any experience with writing secure code. Writing secure code is a must, however, if you do not want your server to be compromised, user data stolen, your site destroyed, or quite possibly worse.  In this article, you will learn about writing more secure PHP code.<br />
<span id="more-83"></span></p>
<h3>Trusting the User</h3>
<p>First of all, just don&#8217;t do it. The number one, most important rule you can have as a developer is to never, ever, trust the data you receive from your users. <strong>Ever.</strong> A large number of the vulnerabilities (some of which I&#8217;ll be discussing) can actually be avoiding if the proper precautions are taken and the user&#8217;s data is properly processed. As a developer you might think “none of the people that are interested in my web site would be malicious, so I don&#8217;t have to worry about that.”  That isn&#8217;t a wise idea to have, however, as a user could unintentionally enter some data in that exploits a security hole, and now you are up a creek without a paddle.</p>
<p>Let me reiterate this point. Never, ever trust data from the user. Even if you check something client side with JavaScript, you should not trust it. Remember that some people turn off JavaScript, making those checks completely useless. Being paranoid about user input and assuming everything they can do could be malicious will greatly prevent many of the common security vulnerabilities found in PHP applications.</p>
<h3>Descriptive Error Messages</h3>
<p>While these can be a big help when debugging, descriptive error messages can also be a big help to potential attackers. Based on the information given in an error message, a malicious user could find out information about your application&#8217;s database structure, file system layout, or other worse things.  Make sure that any error checking you do with the users data will give the least amount of information needed by the user to correct whatever they did wrong.</p>
<p>In addition to your own generated errors, PHP can report a variety of script errors, warnings, and notices, all of which a malicious user could use against you. The good news is that you can disable these on your production server with an <code>.htaccess</code> directive or in your <code>php.ini</code> file by setting <code>error_reporting</code> to <code>0</code>. Do not try using <code>ini_set</code> though! If a fatal error occurs in your script, the value you specify with <code>ini_set</code> will not have any affect, so everything you are trying to prevent being displayed will still be displayed.</p>
<p>The best way to keep track of the errors, warnings, and notices that are not important to the user, but are useful to you when debugging is to use PHP&#8217;s built in logging functions. You can do this just like you do with error reporting by setting the <code>error_logging</code> directive to <code>true</code> and setting the <code>error_log</code> directive to the filename that you want to log to. If these are set with <code>ini_set</code>, the error simply won&#8217;t be logged so it isn&#8217;t a security risk (just harder for you as the developer to debug).  Once you have the log file setup, simply use the <code>trigger_error</code> function like so:</p>
<pre lang="php">trigger_error('A serious error occurred: DETAILS', E_USER_ERROR);</pre>
<p>The first parameter is a string with whatever you want to be logged. The second parameter is the PHP error type, which defaults to <code>E_USER_NOTICE</code> if left unspecified.</p>
<h3>Insecure Database Setup</h3>
<p>The default username and password for popular databases are well known. Leaving these at their default value allows for easy entry to anyone who is able to figure out where your database server is.  The default setup is the first thing an attacker would try in an attempt to get in, so try not to make their job easier!</p>
<p>It is also a good idea to set up different users with specific privileges for your web application. Ideally, anything that a normal user can do should fall under a limited account. By only allowing the client&#8217;s database user to insert and update tuples, or rows in the table, you drastically cut back on the type of damage they can do. Of course, you&#8217;ll probably need to have some ability to delete an arbitrary tuple, but with this restriction it seems impossible to do. The good news is that there is a trivial way to get around this. Simply add another field to the database called something like <code>is_deleted</code> and have it set to <code>0</code> by default. When you want to delete something, simply set it to <code>1</code>. When you are querying data, just look for results that do not have <code>is_deleted</code> set to <code>1</code>, and it will appear to be deleted. The administrators should use a different database user that allows for deleting, and every so often they can run a script that deletes these entries.</p>
<h3>SQL Injection Attacks</h3>
<p>SQL injection attacks come about from developers trusting their users to not be malicious. These attacks are the most common security hazard when communicating with a database. Since the most popular database system in use today with PHP applications is MySQL, these examples are going to be written with its api functions. Let&#8217;s take a look at the common example of a login script. Generally, they&#8217;ll be some query like this:</p>
<pre lang="php" line="1">$sql = “SELECT * FROM `user` WHERE `uname` = '{$_POST['uname']}' AND `pwd` = '{$_POST['pwd']}'”;
$query = mysql_query($sql);</pre>
<p>While this might look safe, it isn&#8217;t. What if some user entered in “<code>' OR 1 = 1 #</code>” as their username? Now the query will result in all the rows in the table being returned, but that&#8217;s clearly not what we want! The query that is executed will look like this:</p>
<pre lang="sql">SELECT * FROM `user` WHERE `uname` = '' OR 1 = 1 # AND `pwd` = ''</pre>
<p>This clearly isn&#8217;t what we wanted, so let&#8217;s take a look why this is the result. The hash symbol (#) indicates a comment, and MySQL will simply ignore everything after it. Since one always equals one, all tuples will be returned. Now, if the developer didn&#8217;t check for the number of rows and just grabs the first result returned by the query, the attacker will now be logged in as the first user in the user table.  You&#8217;ll often find that the first user is an administrator of some type, so this attacker can now go around your site as an administrator. With a bit more effort, the attacker could create an account for themselves, modify entries in the table, or worse. This all happened because we violated our first rule and trusted user input.</p>
<p>There are two solutions to this problem, and both of which are fairly easy to implement. If you are determined to use the <code>mysql_</code>* functions (or are required to use PHP < 5.1), you have to escape the users input like so:</p>
<pre lang="php" line="1">$uname = mysql_real_escape_string($_POST['uname']);
$pwd = mysql_real_escape_string($_POST['pwd']);
$sql = “SELECT * FROM `user` WHERE `uname` = &#8216;$uname&#8217; AND `pwd` = &#8216;$pwd&#8217;”;
$query = mysql_query($sql);</pre>
<p>Be careful if the <code>magic_quotes_gpc</code> directive is set, as you'll have to remove the slashes it adds before calling <code>mysql_real_escape_string</code> because some things will get escaped twice. The <code>magic_quotes_gpc</code> directive adds a slash before single-quotes, double-quotes, backslashes, and NULs.  MySQL's function escapes three additional characters; \n, \r, and \x1a. As a result, some things are escaped twice, which produces results other than what we intended.</p>
<p>If you are lucky enough to be using PHP 5.1 or later, you'll have the ability to use the PHP Data Object (PDO) class. The PDO class is an abstraction layer used to interface with databases. The best part about it is that it was designed in a way that defending against SQL injection attacks is just part of the process. Let's take a look at how we can write our query with PDO:</p>
<pre lang="php" line="1">$stmt = $db->prepare(“SELECT * FROM user WHERE uname = :uname AND pwd
= :pwd”);
$stmt->execute(array(':uname' => $_POST['uname'], ':pwd' => $_POST['pwd']));
$result = $stmt->fetch();</pre>
<p>Calling <code>PDO->prepare</code> and then <code>PDOStatement->execute</code> gives you two benefits. First, it will properly escape the variables you pass in, preventing the SQL injection attack. Secondly, if you plan on issuing a SQL statement multiple times, calling <code>PDO->prepare</code> initially, then calling <code>PDOStatement->execute</code> each time you want to run the query, your calls will be optimized by the database driver by caching the query plan and meta data. This can result in huge performance benefits. There is also a function that is just like <code>mysql_query</code>, and that's <code>PDO->query</code>, but I wouldn't recommend using it because of the benefits obtained by <code>PDO->prepare</code> and <code>PDOStatement->execute</code>.</p>
<h3>Improper Handling of Includes</h3>
<p>Some websites like to dynamically include another page with a url parameter. While this can be incredibly useful, it can also be incredibly dangerous. The PHP code to include this file might look something like this:</p>
<pre lang="php"><?php include $_GET['p']; ?></pre>
<p>Now, a url like <code>index.php?p=news.html</code>. works just fine. However, we are trusting our user here to not enter something malicious in the query string. What if the url is <code>index.php?p=.htpasswd</code>?  The malicious user suddenly has our <code>.htpasswd</code> file, and with the right resources, could figure out passwords to log in to secured parts of the website. The attacker could potentially get any file that your webserver has permission to access. The fix for this is to check the filename against a list of ones that you are expecting. You should also set the PHP directive <code>open_basedir</code> to the paths that you will be opening files from. PHP will then make sure that you never open a file outside of the specified path(s).</p>
<p>If your server is not configured properly, this hole could be used to execute a remote php script as well, letting the attacker run arbitrary code on your server. To prevent this, set the <code>allow_url_fopen</code> directive to <code>false</code>. This can only be set in your <code>.htaccess</code> file or <code>php.ini</code>, however, so don't try using <code>ini_set</code>.</p>
<h3>Conclusion</h3>
<p>Assuming that your site is safe from attackers without actually taking steps to ensure that it is safe is an accident waiting to happen. Assuming that everyone is out to get you and your site, while it might seem to be a symptom of paranoia, can save you a lot of headaches if something bad were to happen.  Most of the common security problems with PHP are the result of trusting the user, so remember our first rule, never, ever trust the data you receive from your user.</p>
]]></content:encoded>
			<wfw:commentRss>http://shawnwilsher.com/archives/83/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Syntax Bug, or Program Bug?</title>
		<link>http://shawnwilsher.com/archives/16</link>
		<comments>http://shawnwilsher.com/archives/16#comments</comments>
		<pubDate>Sat, 17 Sep 2005 21:07:54 +0000</pubDate>
		<dc:creator>Shawn Wilsher</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://sdwilsh.mine.nu/?p=16</guid>
		<description><![CDATA[So, while coding today I came across a very strange bug in PHP. It seems as though subtracting two arrays will return null. $_POST['value']-$var['value']=null However, if I wrap that in the floor() function, it works as expected. floor($_POST['tons']-$bot['armor_tons'])=number Go figure. Took me 30 minutes to find that annoying bug.]]></description>
			<content:encoded><![CDATA[<p>So, while coding today I came across a very strange bug in PHP.  It seems as though subtracting two arrays will return null.<br />
<textarea name="code" rows="1" cols="60" class="php:nocontrols">$_POST['value']-$var['value']=null</textarea><br />
However, if I wrap that in the floor() function, it works as expected.<br />
<textarea name="code" rows="1" cols="60" class="php:nocontrols">floor($_POST['tons']-$bot['armor_tons'])=number</textarea></p>
<p>Go figure.  Took me 30 minutes to find that annoying bug.</p>
]]></content:encoded>
			<wfw:commentRss>http://shawnwilsher.com/archives/16/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

