<?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>Joseph Keeler &#187; PHP Security</title>
	<atom:link href="http://josephkeeler.com/category/php-security/feed/" rel="self" type="application/rss+xml" />
	<link>http://josephkeeler.com</link>
	<description>PHP/LAMP Development and Software Process Improvement</description>
	<lastBuildDate>Thu, 05 Nov 2009 15:11:04 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>PHP Security &#8211; Weak salt vulnerabilities</title>
		<link>http://josephkeeler.com/2009/07/php-security-weak-salt-vulnerabilities/</link>
		<comments>http://josephkeeler.com/2009/07/php-security-weak-salt-vulnerabilities/#comments</comments>
		<pubDate>Fri, 17 Jul 2009 08:00:27 +0000</pubDate>
		<dc:creator>Joseph</dc:creator>
				<category><![CDATA[PHP Security]]></category>

		<guid isPermaLink="false">http://josephkeeler.com/?p=79</guid>
		<description><![CDATA[Salt is good; but if the salt becomes unsalty, with what will you make it salty again? - Jesus Salting your hashes is a good thing.  It adds another level of protection to your hash, and prevents the effectiveness of rainbow tables should your hash get out.  It&#8217;s been standard practice to use a salt [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>Salt is good; but if the salt becomes unsalty, with what will you make it salty again?</p></blockquote>
<p>- Jesus</p>
<p><a href="http://en.wikipedia.org/wiki/Salt_(cryptography)">Salting your hashes</a> is a good thing.  It adds another level of protection to your hash, and prevents the effectiveness of rainbow tables should your hash get out.  It&#8217;s been standard practice to use a salt when storing passwords and similar information for almost 30 years now.  Using a weak salt can significantly reduce the protection it will give you.  Also, use of a salt should just be an additional level of protection to your hashes &#8211; not your only protection.  However, the use of a salt doesn&#8217;t suddenly mean that sharing your hashes with the world is a good thing.</p>
<p><span id="more-79"></span></p>
<p>Not too long ago, I was reviewing the code on a smaller open source CMS.  I came across this block of code<br />
<code><br />
function setUserForeverCookie() {<br />
$hashVal = md5(PASSWORD_SALT . $this-&gt;getUserID());<br />
setcookie("ccmUserHash", $this-&gt;getUserID() . ':' . $hashVal, time() + 1209600, DIR_REL . '/');<br />
}</code></p>
<p>What function does is create a forever login cookie value out of an md5 hash of the combination of your user id and a static PASSWORD_SALT value.  The PASSWORD_SALT value is a number between 100,000 and 999,999, created upon the installation of the CMS.  The user id is a publicly availble id used to identify the user throughout the site, such as in the URL to the user&#8217;s profile page.  For the default super admin, the user id is always 1.</p>
<p>By creating an account and getting your own forever login value, you could easily discover this weak salt.<br />
<code>for ($i = 100000; $myForeverLogin != $guess; $i++) {<br />
  $guess = md5($i.$myUserId);<br />
}</p>
<p>echo "Salt is $i\n"<br />
echo "Admin cookie is ".md5($i.1);<br />
</code></p>
<p>In seconds those few lines of code will give you the server salt and the admin&#8217;s forever login cookie, giving you full control of the site.</p>
<p>While the use of something like a forever login cookie inherently poses some security risk and is prohibited from use in such regulated industries as financial or healthcare (you always need to login when you visit your bank, for example), there are times that it&#8217;ll come up as a requirement and it might make sense to do with your web app.</p>
<p>A better implementation of a forever login would combine a stronger salt with a number used once, or nonce, which would be stored in the database with a user record.  A stronger salt would be something like a 32 character key of random letters &#038; numbers.  The hash algorithm probably wouldn&#8217;t be md5 either, and instead use a stronger encryption algorithm.  By using both a strong salt stored outside of the database and a nonce in the database, a breach of either the code or the database alone would not allow a user to create a forever login.</p>
]]></content:encoded>
			<wfw:commentRss>http://josephkeeler.com/2009/07/php-security-weak-salt-vulnerabilities/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP Security &#8211; Escape proof SQL injection in ORDER BY clause</title>
		<link>http://josephkeeler.com/2009/05/php-security-sql-injection-in-order-by/</link>
		<comments>http://josephkeeler.com/2009/05/php-security-sql-injection-in-order-by/#comments</comments>
		<pubDate>Wed, 13 May 2009 16:24:48 +0000</pubDate>
		<dc:creator>Joseph</dc:creator>
				<category><![CDATA[PHP Security]]></category>

		<guid isPermaLink="false">http://josephkeeler.com/?p=46</guid>
		<description><![CDATA[It&#8217;s a well known, well documented, and well abused fact that SQL injection attacks can take place in the WHERE clause of a SQL statement. The commonly applied practice among professionals is to run user input through mysql(i)_real_escape_string(). However, this only protects against user variables within quoted values, and does not protect against SQL injection [...]]]></description>
			<content:encoded><![CDATA[<div class="wp-caption alignnone" style="width: 560px"><img class="      " title="XKCD - Little Bobby Tables" src="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" alt="http://xkcd.com/327/" width="550" height="169" /><p class="wp-caption-text">http://xkcd.com/327/</p></div>
<p>It&#8217;s a well known, well documented, and well abused fact that SQL injection attacks can take place in the WHERE clause of a SQL statement. The commonly applied practice among professionals is to run user input through mysql(i)_real_escape_string(). However, this only protects against user variables within quoted values, and does not protect against SQL injection attacks elsewhere in the query.</p>
<p>One place that is commonly vulnerable is in the ORDER BY clause. Many developers either do not understand that mysql(i)_real_escape_string does not protect them from these types of attacks, or do not think that meaningful SQL injection can be done at this point in the query on a single statement engine like MySQL. As a result, this vulnerability can be found and exploited in many applications and websites, both commercial and open source, personal and corporate.<span id="more-46"></span></p>
<p>Vulnerable code and SQL queries is basically:</p>
<p><code>&lt;php<br />
 <br />
$sortColumn = mysqli_real_escape_string($_GET['sort_column']);<br />
$query ="SELECT * from some_table WHERE active = true ORDER BY $sortColumn DESC";<br />
 <br />
?&gt;</code></p>
<p>This is vulnerable to a SQL injection attack that will allow a hacker to get information from any table in the database, whether it&#8217;s usernames, passwords, credit card account numbers, etc.<br />
 </p>
<h2>How this can be exploited<br />
 </h2>
<p>The core theory behind the exploit is that this vulnerable query allow you to test a tiny piece of information from anywhere in the database in a boolean query that doesn&#8217;t rely on any unescaped characters, then use the value of that boolean to visibly change the output of the query.</p>
<p>Assume that the vulnerable site is a news site and lets you sort the article listings by the date or title column.  When you click on the column header you want to sort by, it sends a &#8216;sort_column&#8217; parameter to the above script of either &#8216;date&#8217; or &#8216;title&#8217;.  </p>
<p>If instead of sending &#8216;date&#8217; or &#8216;title&#8217;, you sent something like the following string, you would be able to start reading information from anywhere in the database.  In this particular case, we&#8217;ll try the users table.</p>
<p>(CASE WHEN (SELECT ASCII(SUBSTRING(password, 1, 1)) FROM users where username = 0x61646D696E) = 65 THEN date ELSE title END)</p>
<p>Assuming that this is the correct table and column names, this injection will allow you to tell whether or not the first character of the admin user&#8217;s password is &#8216;A&#8217;. If it is, the article list will be returned sorted by date.  If not, it will be returned sorted by title.</p>
<p>If it isn&#8217;t a match, then the 65 in the query just needs to be incremented/decremented until the match is made to try other various letters/symbols. Once the match is made and the first character is discovered, the substring offset just needs to be incremented to get the second character, but this time starting with null to see if the end of the string has already been reached. If not, start back at 65, and repeat the process until null matches.</p>
<p>This does require some knowledge about the database schema, which can be guessed, looked up on open source applications, or can be learned by first querying against a known table like the information schema.</p>
<p>A script can be written to do automate this process very quickly, as an 8 character password with upper and lowercase letters and numbers can be discovered with a maximum of 500 queries. MD5 encoded passwords will have the hashes revealed in less than 512 queries, which can then be brute force decoded (at over 500 million attempts/second, thanks to <a href="http://bvernoux.free.fr/md5/index.php" target="_blank">GPU computing</a>), or directly looked up if the password is a common word or phrase.<br />
 </p>
<h2>Why This Works</h2>
<p>Because each of these queries puts user input in a place in the query that is not enclosed with &#8216;, there is no need to use any of the characters that would be escaped by mysql(i)_real_escape_string(). Instead, SQL can be directly passed directly into the query. In places that strings are normally used when making a query, Hex notation, ASCII or other character conversion can be used to convert strings to or from their numeric values. As demonstrated in these examples, anywhere that SQL can be injected into a query is a major security vulnerability.<br />
 </p>
<h2>How to Secure</h2>
<p>Securing this type of query is a rather simple process.  If a column name is expected, the user input should be validated against a whitelist array. </p>
<p>Applying this on the example query:</p>
<p><code>&lt;php<br />
$columns = array(<br />
'title',<br />
'date'<br />
);<br />
if (in_array($_GET['sort_column'], $columns)) {<br />
$sortColumn = $_GET['sort_column'];<br />
} else {<br />
$sortColumn = 'title';<br />
}</code></p>
<p><code> </code></p>
<p><code>?&gt;</code></p>
<p>As you can see, the above code will ensure that only expected/allowed values make it through to the database. So remember, trust no one, and sanitize everything, regardless of how harmless you may think invalid input will be.</p>
]]></content:encoded>
			<wfw:commentRss>http://josephkeeler.com/2009/05/php-security-sql-injection-in-order-by/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>PHP Upload Security &amp; The 1&#215;1 jpeg Hack</title>
		<link>http://josephkeeler.com/2009/04/php-upload-security-the-1x1-jpeg-hack/</link>
		<comments>http://josephkeeler.com/2009/04/php-upload-security-the-1x1-jpeg-hack/#comments</comments>
		<pubDate>Sat, 04 Apr 2009 00:35:15 +0000</pubDate>
		<dc:creator>Joseph</dc:creator>
				<category><![CDATA[PHP Security]]></category>

		<guid isPermaLink="false">http://josephkeeler.com/?p=3</guid>
		<description><![CDATA[Far too often, I&#8217;ve seen example PHP code, or live PHP code, where file upload checks exclusively using $_FILES['userfile']['type'] == &#8216;image/jpeg&#8217; or getimagesize($filename) or strpos(&#8216;.jpg&#8217;, $_FILES['userfile']['name'] or strstr(&#8216;jpg&#8217;, $_FILES['userfile']['name'] followed by something like move_uploaded_file($_FILES['userfile']['tmpname'], PUBLIC_WEB_UPLOAD_DIR.$_FILES['userfile']['name']); All of these methods leave the server completely vulnerable to remote script uploading and execution with the 1&#215;1 jpeg hack. [...]]]></description>
			<content:encoded><![CDATA[<div class="post-body entry-content">
<div>
<div>Far too often, I&#8217;ve seen example PHP code, or live PHP code, where file upload checks exclusively using</div>
<div>$_FILES['userfile']['type'] == &#8216;image/jpeg&#8217;</div>
<div>or</div>
<div>getimagesize($filename)</div>
<div>or</div>
<div>strpos(&#8216;.jpg&#8217;, $_FILES['userfile']['name'] or strstr(&#8216;jpg&#8217;, $_FILES['userfile']['name']</div>
<div>followed by something like</div>
<div>move_uploaded_file($_FILES['userfile']['tmpname'], PUBLIC_WEB_UPLOAD_DIR.$_FILES['userfile']['name']);</div>
<div>All of these methods leave the server completely vulnerable to remote script uploading and execution with the 1&#215;1 jpeg hack.<br />
 </div>
<div><strong><br />
How this can be exploited<br />
</strong></div>
<div>To execute the 1&#215;1 jpeg hack on a PHP server:</div>
<div>Create a 1&#215;1 jpeg</div>
<div>Put the PHP code you want executed on the server in the embedded jpeg header, surrounded by tags</div>
<div>Name your file some_random_name.jpg.php</div>
<div>Tell your browser/os that a .php file is of type image/jpeg.</div>
<div>Upload the file</div>
<div>When that file is uploaded your file against a server that uses the above method(s) as a &#8220;security&#8221; check to prevent remote file upload &amp; execution it will pass all checks, and will be executed whenever that file is requested by a client browser, whether it&#8217;s a direct request in the browser address or an embedded request in a &lt;img&gt; tag.</div>
<div><strong><span id="more-3"></span><br />
Why this works<br />
</strong></div>
<div>$_FILES['userfile']['type'] comes from the client browser. There is no server-side check to see what type of file it is, there&#8217;s no comparison of that file&#8217;s extension with the server extension list. It&#8217;s simply part of the HTTP header from the browser. By changing the file type association on a client machine that .php files are of type image/jpeg, the browser will upload files with a .php extension with a header type of image/jpeg, passing the first check.</div>
<div>The getimagesize() function will tell you if the uploaded file is a valid image or not. A 1&#215;1 jpeg is a valid image, and could be displayed just fine in a browser. The problem here is what happens when the page is requested by the user. When Apache recieves a request and serves a file, Apache simply looks at the final extension of the file, and decides how to process that file based on that. In all of these cases, $_FILES['userfile']['name'] will be used, and ends with a .php extension. So Apache will process this file as a php file, and run it through the PHP interpreter prior to serving the file, executing the PHP code that was embedded in the jpeg header.</div>
<div><strong><br />
How to secure<br />
</strong></div>
<div>The simple fix that works on 99.9% of the servers out there is to manually change the file extension to .jpg, .png, .gif or whatever type it&#8217;s supposed to be when the file is moved from the temp directory to the public web directory. A very simple, basic step that is too often overlooked when dealing with upload security. But it&#8217;s more effective and less processor intensive than any of the above techniques.</div>
<div>The &lt;0.1% of servers this will not work on are servers that run all files, or image files, through the PHP processor, regardless of extension. Weird, but yes, they are out there. On these servers, to prevent image header script execution, the GD (or Imagemagick) library should be used to recreate the image, and that new file should be saved. This will wipe the image headers, and any embedded code present.</div>
<div>There are more steps that can, and should be taken when dealing with file uploads, such as not putting them directly into a public web folder in the first place, but rather make a link to a file handler. There&#8217;s a whole new set of secure issues with that method for a later post, but done right, it is more effective. But by taking the simple precautions listed here, it will security the majority of sites and servers from most upload attacks.</div>
</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://josephkeeler.com/2009/04/php-upload-security-the-1x1-jpeg-hack/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

