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; }