Invalid Prepared Statements In The Identifier By albro

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@albro·
0.000 HBD
Invalid Prepared Statements In The Identifier By albro
<center>![Invalid Prepared Statements In The Identifier.jpg](https://files.peakd.com/file/peakd-hive/albro/245wSQpCmV4qz4XW4RzKNcUjFLG3ttVGeNyxcXMYVtWH5UUYXKBaWLWEeAK4qznYczefh.jpg)</center>

<p>
    Can prepared statements be used to protect identifiers? Can they be used in any case? Today I want to talk about the weakness of prepared statements.
</p>
<h3>
    The inability of prepared statements
</h3>
<p>
    In the <a href="https://ecency.com/hive-169321/@albro/formatting-rules-in-sql-injection-by-albro">previous post</a>, we got acquainted with simple and beginner examples. Although those simple examples are widely used in the real world, we will also write more advanced queries. Queries that are more dynamic and complex and include identifiers or any more complex structure such as arrays.
</p>
<p>
    Unfortunately, there is no way to protect identifiers against SQL injection attacks except for the same manual formatting:
</p>
<pre><code class="language-php">$field = "`".str_replace("`","``",$field)."`";
$sql   = "SELECT * FROM t ORDER BY $field";
$data  = $db-&gt;query($sql)-&gt;fetchAll();</code></pre>
<p>
    For arrays, we also do this in order to create a query:
</p>
<pre><code class="language-php">$ids = array(1,2,3);
$in  = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in) AND category=?";
$stm = $db-&gt;prepare($sql);
$ids[] = $category; //adding another member to array
$stm-&gt;execute($ids);
$data = $stm-&gt;fetchAll();</code></pre>
<p>
    In fact, it can be said that to format identifiers in MySQL, you must follow the following two rules:
</p>
<ol>
    <li>
        Place the identifiers between two <code>`</code> (this symbol is called backtick).
    </li>
    <li>
        Escape backticks by doubling them.
    </li>
</ol>
<p>
    For example, look at this query:
</p>
<pre><code class="language-php">$table = "`".str_replace("`","``",$table)."`";</code></pre>
<p>
    This code first finds the sign <code>`</code> in the name of the table and then doubles it (i.e. <code>``</code>) then puts the whole string inside two <code>`</code> signs. After such formatting, it is completely safe to enter <code>$table</code> in the query. These rules are different for other databases, but the thing you should keep in mind is that separators alone are not enough and they must be escaped as well.
</p>
<p>
    In fact, you should try to check dynamic identifiers with a list of allowed values:
</p>
<pre><code class="language-php">$orders  = ["name","price","qty"]; //Field name
$key     = array_search($_GET['sort'],$orders); // Check if such a name exists. 
$orderby = $orders[$key]; //اIf it does not exist, the first one will be selected automatically
$query   = "SELECT * FROM `table` ORDER BY $orderby"; //Our quantity is safe</code></pre>
<p>
    As I have explained in the code itself as a comment, instead of blindly receiving any value from the user, this code measures it with predetermined values to see if such a value is allowed at all. This method is called <i>whitelisting</i>.
</p>
<p>
    I can do the same for the <code>INSERT</code> and <code>UPDATE</code> statements because MySQL supports the <code>SET</code> statement for both:
</p>
<pre><code class="language-php">$data = ['name' =&gt; 'foo', 'submit' =&gt; 'submit']; // data for insert
$allowed = ["name", "surname", "email"]; // allowed fields
$values = [];
$set = "";
foreach ($allowed as $field) {
    if (isset($data[$field])) {
        $set.="`".str_replace("`", "``", $field)."`". "=:$field, ";
        $values[$field] = $data[$field];
    }
}
$set = substr($set, 0, -2);</code></pre>
<p>
    In fact, the statement above creates the proper order for the <code>SET</code> operator, which only has fields and placeholders allowed, as in the following example:
</p>
<pre><code class="language-php">`name`=:foo</code></pre>
<p>
    To deal with these problems, various libraries are provided on GitHub that you can use. Some of these libraries are:
</p>
<ul>
    <li>
        <a href="https://dibiphp.com/">Dibi</a>
    </li>
    <li>
        <a href="http://en.dklab.ru/lib/DbSimple/">DBSimple</a>
    </li>
</ul>
<p>
    You can choose your libraries based on your needs and use them in your projects by referring to the pages of these libraries in GitHub. The way these libraries work in simple language is that they bring placeholders along with their type. For example, %s means string or %d means digit. If you go to their page, you will easily understand how they work. All of them have explained how to work with the library on their page.
</p>
<p>
    Prepared statements are very useful, but they are not enough everywhere, as you can see, they were not the answer in the case of identifiers. There are two general cases in which prepared statements are either impractical or insufficient:
</p>
<ul>
    <li>
        We have no way to use prepared statement in SQL keywords.
    </li>
    <li>
        When we have a list of dynamic identifiers that we receive from the user, but there are also fields that the user does not have permission to access. One of the usual cases is that we receive the <code>key</code> and <code>value</code> values from the <code>$_POST</code> array, and although we can format both, there are fields such as <code>admin</code> and <code>permissions</code> that should only be set by the site admin.
    </li>
</ul>
<p>
    To solve both cases, we have to act in the above-mentioned way, i.e. whitelisting. In this method, you write the dynamic parameters in advance in your script, and all allowed values must be selected from them. For example, for an order, we can write like this:
</p>
<pre><code class="language-php">$orders  = array("name","price","qty");
$key     = array_search($_GET['sort'],$orders));
$orderby = $orders[$key];
$query   = "SELECT * FROM `table` ORDER BY $orderby";</code></pre>
<p>
    And for SQL keywords, we have no other way than whitelisting, because no formatting is possible for them, so:
</p>
<pre><code class="language-php">$dir = $_GET['dir'] == 'DESC' ? 'DESC' : 'ASC'; 
$sql = "SELECT * FROM t ORDER BY field $dir";</code></pre>
<p>
    For example, the SafeMysql library provides two functions for whitelisting; One in the form of key=&gt;value arrays and the other for keywords alone.
</p>
<p>
    &nbsp;
</p>
<p>
    [Hive: @albro]
</p>
👍 , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,