{"id":2463,"date":"2016-05-13T11:19:29","date_gmt":"2016-05-13T18:19:29","guid":{"rendered":"http:\/\/www.wellgolly.com\/?p=2463"},"modified":"2016-05-13T11:19:29","modified_gmt":"2016-05-13T18:19:29","slug":"sanitizing-database-query-input","status":"publish","type":"post","link":"https:\/\/www.wellgolly.com\/?p=2463","title":{"rendered":"Sanitizing Database Query Input"},"content":{"rendered":"<p>As part of my site rewrite and migration to MySQL PDO I am making sure that all of the input is sanitized before using\u2014which has the side effect of stopping injection attempts, as discussed in the previous post\u2014and either using prepared statements or whitelisted inputs.<\/p>\n<p>Here\u2019s the sanitizing portion of a crossword solver page. The input is the number of letters in the word and up to 14 letters. There should only be one letter in each space, the space can be empty, and the numbers can be from 1 to 14. I haven\u2019t had any attacks on my forms yet, so I\u2019ll assume any invalid input is due to fat fingers and make reasonable changes.<br \/>\n<pre><code class=\"preserve-code-formatting\">\n&lt;?php\n$MAX_LETTERS = 14;\n$letters = array();\n\n\/\/ Read in the letters and number of letters first so you can repopulate the fields.\n\/\/ If it\u2019s not a single letter in the letter field, or a valid number in the number field,\n\/\/ don\u2019t let it into the query.\n\/\/ Log it in case we get lots of injection attempts.\nif(isset($_POST)) {\n&nbsp;&nbsp;&nbsp;&nbsp;$submitType = $_POST[&#039;submitType&#039;];\n\n&nbsp;&nbsp;&nbsp;&nbsp;if ($submitType != &#039;Clear&#039;) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/ Get and validate letters\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for($i = 1; $i &lt;= $MAX_LETTERS; $i++) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$letters[$i] = $_POST[&#039;letter&#039; . $i];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$letters[$i] = str_replace(&quot; &quot;,&quot;&quot;,$letters[$i]);\n\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!preg_match(&quot;\/^[a-zA-Z]$\/&quot;,$letters[$i]) &amp;&amp; ($letters[$i] &lt;&gt; &#039;&#039;) ) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($showError) error_log(&quot;Not a letter {$letters[$i]} in $calledFileName&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$letters[$i] = &quot;&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\/\/ Get and validate the number of letters\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$num_letters = (integer)$_POST[&#039;num_letters&#039;];\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!is_integer($num_letters) ) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($showError) error_log(&quot;Not a number in $calledFileName&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$num_letters = (integer)$num_letters;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ( $num_letters &gt; $MAX_LETTERS ) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$num_letters = $MAX_LETTERS;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($showError) error_log(&quot;Too many numbers in $calledFileName&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else if ($num_letters &lt; 0 ) {\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$num_letters = $num_letters * -1;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ($showError) error_log(&quot;Negative number in $calledFileName&quot;);\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}\n&nbsp;&nbsp;&nbsp;&nbsp;}\n}<\/code><\/pre><\/p>\n<p>Note that I don\u2019t use htmlspecialchars or mysql_real_escape_string when getting input  because I explicitly allow only letters or numbers when validating the output. I don\u2019t think that they would hurt anything, but they aren\u2019t necessary.<br \/>\n<pre><code class=\"preserve-code-formatting\">\n$letters[$i] = htmlspecialchars($_POST[&#039;letter&#039; . $i]);\n$letters[$i] = mysql_real_escape_string($_POST[&#039;letter&#039; . $i]);<\/code><\/pre><\/p>\n<p>Some of my pages allow more than one letter in each input space. I just add .* to the pre_match to allow more than one letter. I also allow a wildcard, *, in the web page so I need to escape it in the pre_match.<br \/>\n<pre><code class=\"preserve-code-formatting\">\nif (!preg_match(&quot;\/[a-zA-Z\\*].*\/&quot;,$letters[$i]) &amp;&amp; ($letters[$i] &lt;&gt; &#039;&#039;) ) {<\/code><\/pre><\/p>\n<p>The safest way to sanitize input is to whitelist the query. There are lots of ways to do this. One way is to construct the query based on the input.<br \/>\n<pre><code class=\"preserve-code-formatting\">\nswitch ($loc) {\n&nbsp;&nbsp;&nbsp;&nbsp;case &quot;I&quot;:\n&nbsp;&nbsp;&nbsp;&nbsp;case &quot;i&quot;:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$location = &quot;Initial&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$searchString = &quot;^{$letters}[A-Z ]*&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;\n&nbsp;&nbsp;&nbsp;&nbsp;case &quot;M&quot;:\n&nbsp;&nbsp;&nbsp;&nbsp;case &quot;m&quot;:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$location = &quot;Medial&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$searchString = &quot;[A-Z ]+{$letters}[A-Z ]+&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;\n&nbsp;&nbsp;&nbsp;&nbsp;case &quot;F&quot;:\n&nbsp;&nbsp;&nbsp;&nbsp;case &quot;f&quot;:\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$location = &quot;Final&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$searchString = &quot;[A-Z ]*{$letters}\\$&quot;;\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;\n.....<\/code><\/pre><br \/>\nThe user provides a location\u2014initial, medial, or final\u2014and I construct the search string based on their input. No injection is possible because the user input is not seen by the search query.<\/p>\n<p>Another example of whitelisting is to only allow certain values. It works if the list is small and not changed often.<br \/>\n<pre><code class=\"preserve-code-formatting\">\nif ($input == &#039;first value&#039; || $input == &#039;second value&#039; || $input == &#039;third value&#039;) {\n&nbsp;&nbsp;&nbsp;&nbsp;-- do stuff with the database\n} else {\n&nbsp;&nbsp; include_once(&#039;dieInAFire&#039;);\n}<\/code><\/pre><\/p>\n<p>You can do something similar by checking whether the input is part of a hard-coded array. The problem with the last two approaches is that they only work well if the list of acceptable values doesn\u2019t change often. They can work to sanitize user input for things like states and provinces, occupation, and taxable status.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As part of my site rewrite and migration to MySQL PDO I am making sure that all of the input is sanitized before using\u2014which has the side effect of stopping injection attempts, as discussed in the previous post\u2014and either using prepared statements or whitelisted inputs. Here\u2019s the sanitizing portion of a crossword solver page. The &hellip; <a href=\"https:\/\/www.wellgolly.com\/?p=2463\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Sanitizing Database Query Input<\/span><\/a><\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27],"tags":[],"class_list":["post-2463","post","type-post","status-publish","format-standard","hentry","category-mysql"],"_links":{"self":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/posts\/2463","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2463"}],"version-history":[{"count":0,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=\/wp\/v2\/posts\/2463\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2463"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2463"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.wellgolly.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2463"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}