diff -r 2529833a7731 -r 27377179fe58 includes/dbal.php
--- a/includes/dbal.php Mon Jun 30 17:22:29 2008 -0400
+++ b/includes/dbal.php Wed Jul 02 19:36:44 2008 -0400
@@ -32,7 +32,10 @@
$debug = $debug[0]['file'] . ', line ' . $debug[0]['line'];
echo "$errtype: $errstr
Error source:
$debug
";
}
-
+
+global $db_sql_parse_time;
+$db_sql_parse_time = 0;
+
class mysql {
var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
var $row = array();
@@ -309,79 +312,46 @@
}
/**
- * Checks a SQL query for possible signs of injection attempts
+ * Performs heuristic analysis on a SQL query to check for known attack patterns.
* @param string $q the query to check
* @return bool true if query passed check, otherwise false
*/
function check_query($q, $debug = false)
{
- if($debug) echo "\$db->check_query(): checking query: ".htmlspecialchars($q).'
'."\n";
- $sz = strlen($q);
- $quotechar = false;
- $quotepos = 0;
- $prev_is_quote = false;
- $just_started = false;
- for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
+ global $db_sql_parse_time;
+ $ts = microtime_float();
+
+ // remove properly escaped quotes
+ $q = str_replace(array("\\\"", "\\'"), '', $q);
+
+ // make sure quotes match
+ foreach ( array('"', "'") as $quote )
{
- $next = substr($q, $i+1, 1);
- $next2 = substr($q, $i+2, 1);
- $prev = substr($q, $i-1, 1);
- $prev2 = substr($q, $i-2, 1);
- if(isset($c) && in_array($c, Array('"', "'", '`')))
+ if ( get_char_count($q, $quote) % 2 == 1 )
{
- if($quotechar)
- {
- if (
- ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
- ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
- )
- {
- $quotechar = false;
- if($debug) echo('$db->check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '
');
- $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q));
- if($debug) echo('$db->check_query(): Filtered query: '.$q.'
');
- $i = $quotepos;
- }
- }
- else
- {
- $quotechar = $c;
- $quotepos = $i;
- $just_started = true;
- }
- if($debug) echo '$db->check_query(): found quote char as pos: '.$i.'
';
- continue;
- }
- $just_started = false;
- }
- if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1);
- for($i=0;$i';
- else $e .= $c;
- }
- echo 'Injection attempt caught at pos: '.$i.'
';
- }
+ // mismatched quotes
return false;
}
+ // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
+ $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
}
+ $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
+
+ // quotes are now matched out. does this string have a comment marker in it?
+ if ( strstr($q, '--') )
+ {
+ return false;
+ }
+
if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
{
if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:
' . '' . print_r($match, true) . '
';
return false;
}
+
+ $ts = microtime_float() - $ts;
+ $db_sql_parse_time += $ts;
return true;
}
@@ -1066,72 +1036,39 @@
function check_query($q, $debug = false)
{
- if($debug) echo "\$db->check_query(): checking query: ".htmlspecialchars($q).'
'."\n";
- $sz = strlen($q);
- $quotechar = false;
- $quotepos = 0;
- $prev_is_quote = false;
- $just_started = false;
- for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
+ global $db_sql_parse_time;
+ $ts = microtime_float();
+
+ // remove properly escaped quotes
+ $q = str_replace(array("\\\"", "\\'"), '', $q);
+
+ // make sure quotes match
+ foreach ( array('"', "'") as $quote )
{
- $next = substr($q, $i+1, 1);
- $next2 = substr($q, $i+2, 1);
- $prev = substr($q, $i-1, 1);
- $prev2 = substr($q, $i-2, 1);
- if(isset($c) && in_array($c, Array('"', "'", '`')))
+ if ( get_char_count($q, $quote) % 2 == 1 )
{
- if($quotechar)
- {
- if (
- ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
- ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
- )
- {
- $quotechar = false;
- if($debug) echo('$db->check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '
');
- $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q));
- if($debug) echo('$db->check_query(): Filtered query: '.$q.'
');
- $i = $quotepos;
- }
- }
- else
- {
- $quotechar = $c;
- $quotepos = $i;
- $just_started = true;
- }
- if($debug) echo '$db->check_query(): found quote char as pos: '.$i.'
';
- continue;
- }
- $just_started = false;
- }
- if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1);
- for($i=0;$i';
- else $e .= $c;
- }
- echo 'Injection attempt caught at pos: '.$i.'
';
- }
+ // mismatched quotes
return false;
}
+ // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
+ $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
}
+ $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
+
+ // quotes are now matched out. does this string have a comment marker in it?
+ if ( strstr($q, '--') )
+ {
+ return false;
+ }
+
if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
{
if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:
' . '' . print_r($match, true) . '
';
return false;
}
+
+ $ts = microtime_float() - $ts;
+ $db_sql_parse_time += $ts;
return true;
}