includes/dbal.php
changeset 592 27377179fe58
parent 536 218a627eb53e
child 616 e311f5e6f904
equal deleted inserted replaced
591:2529833a7731 592:27377179fe58
    30   if ( !isset($debug[0]['file']) )
    30   if ( !isset($debug[0]['file']) )
    31     return false;
    31     return false;
    32   $debug = $debug[0]['file'] . ', line ' . $debug[0]['line'];
    32   $debug = $debug[0]['file'] . ', line ' . $debug[0]['line'];
    33   echo "<b>$errtype:</b> $errstr<br />Error source:<pre>$debug</pre>";
    33   echo "<b>$errtype:</b> $errstr<br />Error source:<pre>$debug</pre>";
    34 }
    34 }
    35  
    35 
       
    36 global $db_sql_parse_time;
       
    37 $db_sql_parse_time = 0;
       
    38 
    36 class mysql {
    39 class mysql {
    37   var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
    40   var $num_queries, $query_backtrace, $query_times, $query_sources, $latest_result, $latest_query, $_conn, $sql_stack_fields, $sql_stack_values, $debug;
    38   var $row = array();
    41   var $row = array();
    39 	var $rowset = array();
    42 	var $rowset = array();
    40   var $errhandler;
    43   var $errhandler;
   307     $this->disable_errorhandler();
   310     $this->disable_errorhandler();
   308     return $r;
   311     return $r;
   309   }
   312   }
   310   
   313   
   311   /**
   314   /**
   312    * Checks a SQL query for possible signs of injection attempts
   315    * Performs heuristic analysis on a SQL query to check for known attack patterns.
   313    * @param string $q the query to check
   316    * @param string $q the query to check
   314    * @return bool true if query passed check, otherwise false
   317    * @return bool true if query passed check, otherwise false
   315    */
   318    */
   316   
   319   
   317   function check_query($q, $debug = false)
   320   function check_query($q, $debug = false)
   318   {
   321   {
   319     if($debug) echo "\$db-&gt;check_query(): checking query: ".htmlspecialchars($q).'<br />'."\n";
   322     global $db_sql_parse_time;
   320     $sz = strlen($q);
   323     $ts = microtime_float();
   321     $quotechar = false;
   324     
   322     $quotepos  = 0;
   325     // remove properly escaped quotes
   323     $prev_is_quote = false;
   326     $q = str_replace(array("\\\"", "\\'"), '', $q);
   324     $just_started = false;
   327     
   325     for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
   328     // make sure quotes match
   326     {
   329     foreach ( array('"', "'") as $quote )
   327       $next = substr($q, $i+1, 1);
   330     {
   328       $next2 = substr($q, $i+2, 1);
   331       if ( get_char_count($q, $quote) % 2 == 1 )
   329       $prev = substr($q, $i-1, 1);
   332       {
   330       $prev2 = substr($q, $i-2, 1);
   333         // mismatched quotes
   331       if(isset($c) && in_array($c, Array('"', "'", '`')))
       
   332       {
       
   333         if($quotechar)
       
   334         {
       
   335           if (
       
   336               ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
       
   337               ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
       
   338             )
       
   339           {
       
   340             $quotechar = false;
       
   341             if($debug) echo('$db-&gt;check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '<br />');
       
   342             $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q));
       
   343             if($debug) echo('$db-&gt;check_query(): Filtered query: '.$q.'<br />');
       
   344             $i = $quotepos;
       
   345           }
       
   346         }
       
   347         else
       
   348         {
       
   349           $quotechar = $c;
       
   350           $quotepos  = $i;
       
   351           $just_started = true;
       
   352         }
       
   353         if($debug) echo '$db-&gt;check_query(): found quote char as pos: '.$i.'<br />';
       
   354         continue;
       
   355       }
       
   356       $just_started = false;
       
   357     }
       
   358     if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1);
       
   359     for($i=0;$i<strlen($q);$i++,$c=substr($q, $i, 1))
       
   360     {
       
   361       if ( 
       
   362            ( ( $c == ';' && $i != $sz-1 ) || $c . substr($q, $i+1, 1) == '--' )
       
   363         || ( in_array($c, Array('"', "'", '`')) )
       
   364          ) // Don't permit semicolons in mid-query, and never allow comments
       
   365       {
       
   366         // Injection attempt!
       
   367         if($debug)
       
   368         {
       
   369           $e = '';
       
   370           for($j=$i-5;$j<$i+5;$j++)
       
   371           {
       
   372             if($j == $i) $e .= '<span style="color: red; text-decoration: underline;">' . $c . '</span>';
       
   373             else $e .= $c;
       
   374           }
       
   375           echo 'Injection attempt caught at pos: '.$i.'<br />';
       
   376         }
       
   377         return false;
   334         return false;
   378       }
   335       }
   379     }
   336       // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
       
   337       $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
       
   338     }
       
   339     $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
       
   340     
       
   341     // quotes are now matched out. does this string have a comment marker in it?
       
   342     if ( strstr($q, '--') )
       
   343     {
       
   344       return false;
       
   345     }
       
   346     
   380     if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
   347     if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
   381     {
   348     {
   382       if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
   349       if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
   383       return false;
   350       return false;
   384     }
   351     }
       
   352     
       
   353     $ts = microtime_float() - $ts;
       
   354     $db_sql_parse_time += $ts;
   385     return true;
   355     return true;
   386   }
   356   }
   387   
   357   
   388   /**
   358   /**
   389    * Set the internal result pointer to X
   359    * Set the internal result pointer to X
  1064    * @return bool true if query passed check, otherwise false
  1034    * @return bool true if query passed check, otherwise false
  1065    */
  1035    */
  1066   
  1036   
  1067   function check_query($q, $debug = false)
  1037   function check_query($q, $debug = false)
  1068   {
  1038   {
  1069     if($debug) echo "\$db-&gt;check_query(): checking query: ".htmlspecialchars($q).'<br />'."\n";
  1039     global $db_sql_parse_time;
  1070     $sz = strlen($q);
  1040     $ts = microtime_float();
  1071     $quotechar = false;
  1041     
  1072     $quotepos  = 0;
  1042     // remove properly escaped quotes
  1073     $prev_is_quote = false;
  1043     $q = str_replace(array("\\\"", "\\'"), '', $q);
  1074     $just_started = false;
  1044     
  1075     for ( $i = 0; $i < strlen($q); $i++, $c = substr($q, $i, 1) )
  1045     // make sure quotes match
  1076     {
  1046     foreach ( array('"', "'") as $quote )
  1077       $next = substr($q, $i+1, 1);
  1047     {
  1078       $next2 = substr($q, $i+2, 1);
  1048       if ( get_char_count($q, $quote) % 2 == 1 )
  1079       $prev = substr($q, $i-1, 1);
  1049       {
  1080       $prev2 = substr($q, $i-2, 1);
  1050         // mismatched quotes
  1081       if(isset($c) && in_array($c, Array('"', "'", '`')))
       
  1082       {
       
  1083         if($quotechar)
       
  1084         {
       
  1085           if (
       
  1086               ( $quotechar == $c && $quotechar != $next && ( $quotechar != $prev || $just_started ) && $prev != '\\') ||
       
  1087               ( $prev2 == '\\' && $prev == $quotechar && $quotechar == $c )
       
  1088             )
       
  1089           {
       
  1090             $quotechar = false;
       
  1091             if($debug) echo('$db-&gt;check_query(): just finishing a quote section, quoted string: '.htmlspecialchars(substr($q, $quotepos, $i - $quotepos + 1)) . '<br />');
       
  1092             $q = substr($q, 0, $quotepos) . 'SAFE_QUOTE' . substr($q, $i + 1, strlen($q));
       
  1093             if($debug) echo('$db-&gt;check_query(): Filtered query: '.$q.'<br />');
       
  1094             $i = $quotepos;
       
  1095           }
       
  1096         }
       
  1097         else
       
  1098         {
       
  1099           $quotechar = $c;
       
  1100           $quotepos  = $i;
       
  1101           $just_started = true;
       
  1102         }
       
  1103         if($debug) echo '$db-&gt;check_query(): found quote char as pos: '.$i.'<br />';
       
  1104         continue;
       
  1105       }
       
  1106       $just_started = false;
       
  1107     }
       
  1108     if(substr(trim($q), strlen(trim($q))-1, 1) == ';') $q = substr(trim($q), 0, strlen(trim($q))-1);
       
  1109     for($i=0;$i<strlen($q);$i++,$c=substr($q, $i, 1))
       
  1110     {
       
  1111       if ( 
       
  1112            ( ( $c == ';' && $i != $sz-1 ) || $c . substr($q, $i+1, 1) == '--' )
       
  1113         || ( in_array($c, Array('"', "'", '`')) )
       
  1114          ) // Don't permit semicolons in mid-query, and never allow comments
       
  1115       {
       
  1116         // Injection attempt!
       
  1117         if($debug)
       
  1118         {
       
  1119           $e = '';
       
  1120           for($j=$i-5;$j<$i+5;$j++)
       
  1121           {
       
  1122             if($j == $i) $e .= '<span style="color: red; text-decoration: underline;">' . $c . '</span>';
       
  1123             else $e .= $c;
       
  1124           }
       
  1125           echo 'Injection attempt caught at pos: '.$i.'<br />';
       
  1126         }
       
  1127         return false;
  1051         return false;
  1128       }
  1052       }
  1129     }
  1053       // this quote is now confirmed to be matching; we can safely move all quoted strings out and replace with a token
       
  1054       $q = preg_replace("/$quote(.*?)$quote/s", 'SAFE_QUOTE', $q);
       
  1055     }
       
  1056     $q = preg_replace("/(SAFE_QUOTE)+/", 'SAFE_QUOTE', $q);
       
  1057     
       
  1058     // quotes are now matched out. does this string have a comment marker in it?
       
  1059     if ( strstr($q, '--') )
       
  1060     {
       
  1061       return false;
       
  1062     }
       
  1063     
  1130     if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
  1064     if ( preg_match('/[\s]+(SAFE_QUOTE|[\S]+)=\\1($|[\s]+)/', $q, $match) )
  1131     {
  1065     {
  1132       if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
  1066       if ( $debug ) echo 'Found always-true test in query, injection attempt caught, match:<br />' . '<pre>' . print_r($match, true) . '</pre>';
  1133       return false;
  1067       return false;
  1134     }
  1068     }
       
  1069     
       
  1070     $ts = microtime_float() - $ts;
       
  1071     $db_sql_parse_time += $ts;
  1135     return true;
  1072     return true;
  1136   }
  1073   }
  1137   
  1074   
  1138   /**
  1075   /**
  1139    * Set the internal result pointer to X
  1076    * Set the internal result pointer to X