国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

763 lines
24KB

  1. <?php if(!defined('DEDEINC')) exit("Request Error!");
  2. /**
  3. * 数据库类
  4. * 说明:系统底层数据库核心类
  5. * 调用这个类前,请先设定这些外部变量
  6. * $GLOBALS['cfg_dbhost'];
  7. * $GLOBALS['cfg_dbuser'];
  8. * $GLOBALS['cfg_dbpwd'];
  9. * $GLOBALS['cfg_dbname'];
  10. * $GLOBALS['cfg_dbprefix'];
  11. *
  12. * @version $Id: dedesqli.class.php 1 15:00 2011-1-21 tianya $
  13. * @package DedeCMS.Libraries
  14. * @copyright Copyright (c) 2007 - 2019, DesDev, Inc.
  15. * @license http://help.dedecms.com/usersguide/license.html
  16. * @link http://www.dedecms.com
  17. */
  18. @set_time_limit(0);
  19. // 在工程所有文件中均不需要单独初始化这个类,可直接用 $dsql 或 $db 进行操作
  20. // 为了防止错误,操作完后不必关闭数据库
  21. $dsql = $dsqlitete = $db = new DedeSqlite(FALSE);
  22. /**
  23. * Dede MySQLi数据库类
  24. *
  25. * @package DedeSqli
  26. * @subpackage DedeCMS.Libraries
  27. * @link http://www.dedecms.com
  28. */
  29. if (!defined('MYSQL_BOTH')) {
  30. define('MYSQL_BOTH',MYSQLI_BOTH);
  31. }
  32. class DedeSqlite
  33. {
  34. var $linkID;
  35. var $dbHost;
  36. var $dbUser;
  37. var $dbPwd;
  38. var $dbName;
  39. var $dbPrefix;
  40. var $result;
  41. var $queryString;
  42. var $parameters;
  43. var $isClose;
  44. var $safeCheck;
  45. var $showError=false;
  46. var $recordLog=false; // 记录日志到data/mysqli_record_log.inc便于进行调试
  47. var $isInit=false;
  48. var $pconnect=false;
  49. var $_fixObject;
  50. //用外部定义的变量初始类,并连接数据库
  51. function __construct($pconnect=FALSE,$nconnect=FALSE)
  52. {
  53. $this->isClose = FALSE;
  54. $this->safeCheck = TRUE;
  55. $this->pconnect = $pconnect;
  56. if($nconnect)
  57. {
  58. $this->Init($pconnect);
  59. }
  60. }
  61. function DedeSql($pconnect=FALSE,$nconnect=TRUE)
  62. {
  63. $this->__construct($pconnect,$nconnect);
  64. }
  65. function Init($pconnect=FALSE)
  66. {
  67. $this->linkID = 0;
  68. //$this->queryString = '';
  69. //$this->parameters = Array();
  70. $this->dbHost = $GLOBALS['cfg_dbhost'];
  71. $this->dbUser = $GLOBALS['cfg_dbuser'];
  72. $this->dbPwd = $GLOBALS['cfg_dbpwd'];
  73. $this->dbName = $GLOBALS['cfg_dbname'];
  74. $this->dbPrefix = $GLOBALS['cfg_dbprefix'];
  75. $this->result["me"] = 0;
  76. $this->Open($pconnect);
  77. }
  78. //用指定参数初始数据库信息
  79. function SetSource($host,$username,$pwd,$dbname,$dbprefix="dede_")
  80. {
  81. $this->dbHost = $host;
  82. $this->dbUser = $username;
  83. $this->dbPwd = $pwd;
  84. $this->dbName = $dbname;
  85. $this->dbPrefix = $dbprefix;
  86. $this->result["me"] = 0;
  87. }
  88. //设置SQL里的参数
  89. function SetParameter($key,$value)
  90. {
  91. $this->parameters[$key]=$value;
  92. }
  93. //连接数据库
  94. function Open($pconnect=FALSE)
  95. {
  96. global $dsqlite;
  97. //连接数据库
  98. if($dsqlite && !$dsqlite->isClose && $dsqlite->isInit)
  99. {
  100. $this->linkID = $dsqlite->linkID;
  101. }
  102. else
  103. {
  104. $this->linkID = new SQLite3(DEDEDATA.'/'.$this->dbName.'.db');
  105. //复制一个对象副本
  106. CopySQLiPoint($this);
  107. }
  108. //处理错误,成功连接则选择数据库
  109. if(!$this->linkID)
  110. {
  111. $this->DisplayError("DedeCMS错误警告:<font color='red'>连接数据库失败,可能数据库密码不对或数据库服务器出错!</font>");
  112. exit();
  113. }
  114. $this->isInit = TRUE;
  115. return TRUE;
  116. }
  117. //为了防止采集等需要较长运行时间的程序超时,在运行这类程序时设置系统等待和交互时间
  118. function SetLongLink()
  119. {
  120. @mysqli_query("SET interactive_timeout=3600, wait_timeout=3600 ;", $this->linkID);
  121. }
  122. //获得错误描述
  123. function GetError()
  124. {
  125. $str = mysqli_error($this->linkID);
  126. return $str;
  127. }
  128. //关闭数据库
  129. //mysql能自动管理非持久连接的连接池
  130. //实际上关闭并无意义并且容易出错,所以取消这函数
  131. function Close($isok=FALSE)
  132. {
  133. $this->FreeResultAll();
  134. if($isok)
  135. {
  136. $this->linkID->close();
  137. $this->isClose = TRUE;
  138. $GLOBALS['dsql'] = NULL;
  139. }
  140. }
  141. //定期清理死连接
  142. function ClearErrLink()
  143. {
  144. }
  145. //关闭指定的数据库连接
  146. function CloseLink($dblink)
  147. {
  148. }
  149. function Esc( $_str )
  150. {
  151. return addslashes($_str);
  152. }
  153. //执行一个不返回结果的SQL语句,如update,delete,insert等
  154. function ExecuteNoneQuery($sql='')
  155. {
  156. global $dsqlite;
  157. if(!$dsqlite->isInit)
  158. {
  159. $this->Init($this->pconnect);
  160. }
  161. if($dsqlite->isClose)
  162. {
  163. $this->Open(FALSE);
  164. $dsqlite->isClose = FALSE;
  165. }
  166. if(!empty($sql))
  167. {
  168. $this->SetQuery($sql);
  169. }else{
  170. return FALSE;
  171. }
  172. if(is_array($this->parameters))
  173. {
  174. foreach($this->parameters as $key=>$value)
  175. {
  176. $this->queryString = str_replace("@".$key,"'$value'",$this->queryString);
  177. }
  178. }
  179. //SQL语句安全检查
  180. if($this->safeCheck) CheckSql($this->queryString,'update');
  181. $t1 = ExecTime();
  182. $rs = $this->linkID->exec($this->queryString);
  183. //查询性能测试
  184. if($this->recordLog) {
  185. $queryTime = ExecTime() - $t1;
  186. $this->RecordLog($queryTime);
  187. //echo $this->queryString."--{$queryTime}<hr />\r\n";
  188. }
  189. return $rs;
  190. }
  191. //执行一个返回影响记录条数的SQL语句,如update,delete,insert等
  192. function ExecuteNoneQuery2($sql='')
  193. {
  194. global $dsqlite;
  195. if(!$dsqlite->isInit)
  196. {
  197. $this->Init($this->pconnect);
  198. }
  199. if($dsqlite->isClose)
  200. {
  201. $this->Open(FALSE);
  202. $dsqlite->isClose = FALSE;
  203. }
  204. if(!empty($sql))
  205. {
  206. $this->SetQuery($sql);
  207. }
  208. if(is_array($this->parameters))
  209. {
  210. foreach($this->parameters as $key=>$value)
  211. {
  212. $this->queryString = str_replace("@".$key,"'$value'",$this->queryString);
  213. }
  214. }
  215. $t1 = ExecTime();
  216. $this->linkID->exec($this->queryString);
  217. //查询性能测试
  218. if($this->recordLog) {
  219. $queryTime = ExecTime() - $t1;
  220. $this->RecordLog($queryTime);
  221. //echo $this->queryString."--{$queryTime}<hr />\r\n";
  222. }
  223. return $this->linkID->changes();
  224. }
  225. function ExecNoneQuery($sql='')
  226. {
  227. return $this->ExecuteNoneQuery($sql);
  228. }
  229. function GetFetchRow($id='me')
  230. {
  231. return $this->result[$id]->numColumns();
  232. }
  233. function GetAffectedRows()
  234. {
  235. return $this->linkID->changes();
  236. }
  237. //执行一个带返回结果的SQL语句,如SELECT,SHOW等
  238. function Execute($id="me", $sql='')
  239. {
  240. global $dsqlite;
  241. if(!$dsqlite->isInit)
  242. {
  243. $this->Init($this->pconnect);
  244. }
  245. if($dsqlite->isClose)
  246. {
  247. $this->Open(FALSE);
  248. $dsqlite->isClose = FALSE;
  249. }
  250. if(!empty($sql))
  251. {
  252. $this->SetQuery($sql);
  253. }
  254. //SQL语句安全检查
  255. if($this->safeCheck)
  256. {
  257. CheckSql($this->queryString);
  258. }
  259. $t1 = ExecTime();
  260. //var_dump($this->queryString);
  261. $this->result[$id] = $this->linkID->query($this->queryString);
  262. //var_dump(mysql_error());
  263. //查询性能测试
  264. if($this->recordLog) {
  265. $queryTime = ExecTime() - $t1;
  266. $this->RecordLog($queryTime);
  267. //echo $this->queryString."--{$queryTime}<hr />\r\n";
  268. }
  269. if($this->result[$id]===FALSE)
  270. {
  271. $this->DisplayError($this->linkID->lastErrorMsg()." <br />Error sql: <font color='red'>".$this->queryString."</font>");
  272. }
  273. }
  274. function Query($id="me",$sql='')
  275. {
  276. $this->Execute($id,$sql);
  277. }
  278. //执行一个SQL语句,返回前一条记录或仅返回一条记录
  279. function GetOne($sql='',$acctype=MYSQLI_ASSOC)
  280. {
  281. global $dsqlite;
  282. if(!$dsqlite->isInit)
  283. {
  284. $this->Init($this->pconnect);
  285. }
  286. if($dsqlite->isClose)
  287. {
  288. $this->Open(FALSE);
  289. $dsqlite->isClose = FALSE;
  290. }
  291. if(!empty($sql))
  292. {
  293. if(!preg_match("/LIMIT/i",$sql)) $this->SetQuery(preg_replace("/[,;]$/i", '', trim($sql))." LIMIT 0,1;");
  294. else $this->SetQuery($sql);
  295. }
  296. $this->Execute("one");
  297. $arr = $this->GetArray("one", $acctype);
  298. if(!is_array($arr))
  299. {
  300. return '';
  301. }
  302. else
  303. {
  304. $this->result["one"]->reset(); return($arr);
  305. }
  306. }
  307. //执行一个不与任何表名有关的SQL语句,Create等
  308. function ExecuteSafeQuery($sql,$id="me")
  309. {
  310. global $dsqlite;
  311. if(!$dsqlite->isInit)
  312. {
  313. $this->Init($this->pconnect);
  314. }
  315. if($dsqlite->isClose)
  316. {
  317. $this->Open(FALSE);
  318. $dsqlite->isClose = FALSE;
  319. }
  320. $this->result[$id] = $this->linkID->query($sql);
  321. }
  322. //返回当前的一条记录并把游标移向下一记录
  323. // SQLITE3_ASSOC、SQLITE3_NUM、SQLITE3_BOTH
  324. function GetArray($id="me",$acctype=SQLITE3_ASSOC)
  325. {
  326. switch ( $acctype )
  327. {
  328. case MYSQL_ASSOC:
  329. $acctype = SQLITE3_ASSOC;
  330. break;
  331. case MYSQL_NUM:
  332. $acctype = SQLITE3_NUM;
  333. break;
  334. default:
  335. $acctype = SQLITE3_BOTH;
  336. break;
  337. }
  338. if($this->result[$id]===0)
  339. {
  340. return FALSE;
  341. }
  342. else
  343. {
  344. $rs = $this->result[$id]->fetchArray($acctype);
  345. if ( !$rs ) {
  346. $this->result[$id]=0;
  347. return false;
  348. }
  349. return $rs;
  350. }
  351. }
  352. function GetObject($id="me")
  353. {
  354. if ( !isset($this->_fixObject[$id]) )
  355. {
  356. $this->_fixObject[$id] = array();
  357. while ( $row = $this->result[$id]->fetchArray(SQLITE3_ASSOC) )
  358. {
  359. $this->_fixObject[$id][] = (object)$row;
  360. }
  361. $this->result[$id]->reset();
  362. }
  363. return array_shift($this->_fixObject[$id]);
  364. }
  365. // 检测是否存在某数据表
  366. function IsTable($tbname)
  367. {
  368. global $dsqlite;
  369. if(!$dsqlite->isInit)
  370. {
  371. $this->Init($this->pconnect);
  372. }
  373. $prefix="#@__";
  374. $tbname = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $tbname);
  375. $row = $this->linkID->querySingle( "PRAGMA table_info({$tbname});");
  376. if( $row !== null )
  377. {
  378. return TRUE;
  379. }
  380. return FALSE;
  381. }
  382. //获得MySql的版本号
  383. function GetVersion($isformat=TRUE)
  384. {
  385. global $dsqlite;
  386. if(!$dsqlite->isInit)
  387. {
  388. $this->Init($this->pconnect);
  389. }
  390. if($dsqlite->isClose)
  391. {
  392. $this->Open(FALSE);
  393. $dsqlite->isClose = FALSE;
  394. }
  395. $rs = $this->linkID->querySingle("select sqlite_version();");
  396. $sqlite_version = $rs;
  397. if($isformat)
  398. {
  399. $sqlite_versions = explode(".",trim($sqlite_version));
  400. $sqlite_version = number_format($sqlite_versions[0].".".$sqlite_versions[1],2);
  401. }
  402. return $sqlite_version;
  403. }
  404. //获取特定表的信息
  405. function GetTableFields($tbname, $id="me")
  406. {
  407. global $dsqlite;
  408. if(!$dsqlite->isInit)
  409. {
  410. $this->Init($this->pconnect);
  411. }
  412. $prefix="#@__";
  413. $tbname = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $tbname);
  414. $query = "SELECT * FROM {$tbname} LIMIT 0,1";
  415. $this->result[$id] = $this->linkID->query($query);
  416. }
  417. //获取字段详细信息
  418. function GetFieldObject($id="me")
  419. {
  420. $cols = $this->result[$id]->numColumns();
  421. $fields = array();
  422. while ($row = $this->result[$id]->fetchArray()) {
  423. for ($i = 1; $i < $cols; $i++) {
  424. $fields[] = $this->result[$id]->columnName($i);
  425. }
  426. }
  427. return (object)$fields;
  428. }
  429. //获得查询的总记录数
  430. function GetTotalRow($id="me")
  431. {
  432. $queryString = preg_replace("/SELECT(.*)FROM/isU",'SELECT count(*) as dd FROM',$this->queryString);
  433. $rs = $this->linkID->query($queryString);
  434. $row = $rs->fetchArray();
  435. return $row['dd'];
  436. }
  437. //获取上一步INSERT操作产生的ID
  438. function GetLastID()
  439. {
  440. //如果 AUTO_INCREMENT 的列的类型是 BIGINT,则 mysqli_insert_id() 返回的值将不正确。
  441. //可以在 SQL 查询中用 MySQL 内部的 SQL 函数 LAST_INSERT_ID() 来替代。
  442. //$rs = mysqli_query($this->linkID, "Select LAST_INSERT_ID() as lid");
  443. //$row = mysqli_fetch_array($rs);
  444. //return $row["lid"];
  445. return $this->linkID->lastInsertRowID();
  446. }
  447. //释放记录集占用的资源
  448. function FreeResult($id="me")
  449. {
  450. if ( $this->result[$id] )
  451. {
  452. @$this->result[$id]->reset();
  453. }
  454. }
  455. function FreeResultAll()
  456. {
  457. if(!is_array($this->result))
  458. {
  459. return '';
  460. }
  461. foreach($this->result as $kk => $vv)
  462. {
  463. if($vv)
  464. {
  465. @$vv->reset();
  466. }
  467. }
  468. }
  469. //设置SQL语句,会自动把SQL语句里的#@__替换为$this->dbPrefix(在配置文件中为$cfg_dbprefix)
  470. function SetQuery($sql)
  471. {
  472. $prefix="#@__";
  473. $sql = str_replace($prefix,$GLOBALS['cfg_dbprefix'],$sql);
  474. $this->queryString = $sql;
  475. //$this->queryString = preg_replace("/CONCAT\(',', arc.typeid2, ','\)/i","printf(',%s,', arc.typeid2)",$this->queryString);
  476. if ( preg_match("/CONCAT\(([^\)]*?)\)/i",$this->queryString,$matches) )
  477. {
  478. $this->queryString = preg_replace("/CONCAT\(([^\)]*?)\)/i",str_replace(",","||",$matches[1]), $this->queryString);
  479. $this->queryString = str_replace("'||'","','",$this->queryString);
  480. }
  481. $this->queryString = preg_replace("/FIND_IN_SET\('([\w]+)', arc.flag\)>0/i","(',' || arc.flag || ',') LIKE '%,\\1,%'",$this->queryString);
  482. $this->queryString = preg_replace("/FIND_IN_SET\('([\w]+)', arc.flag\)<1/i","(',' || arc.flag || ',') NOT LIKE '%,\\1,%'",$this->queryString);
  483. if ( preg_match("/CREATE TABLE/i",$this->queryString) )
  484. {
  485. $this->queryString = preg_replace("/[\r\n]/",'',$this->queryString);
  486. $this->queryString = preg_replace('/character set (.*?) /i','',$this->queryString);
  487. $this->queryString = preg_replace('/unsigned/i','',$this->queryString);
  488. $this->queryString = str_replace('TYPE=MyISAM','',$this->queryString);
  489. $this->queryString = preg_replace ('/TINYINT\(([\d]+)\)/i','INTEGER',$this->queryString);
  490. $this->queryString = preg_replace ('/mediumint\(([\d]+)\)/i','INTEGER',$this->queryString);
  491. $this->queryString = preg_replace ('/smallint\(([\d]+)\)/i','INTEGER',$this->queryString);
  492. $this->queryString = preg_replace('/int\(([\d]+)\)/i','INTEGER',$this->queryString);
  493. $this->queryString = preg_replace('/auto_increment/i','PRIMARY KEY AUTOINCREMENT',$this->queryString);
  494. $this->queryString = preg_replace('/, KEY(.*?)MyISAM;/i','',$this->queryString);
  495. $this->queryString = preg_replace('/, KEY(.*?);/i',');',$this->queryString);
  496. $this->queryString = preg_replace('/, UNIQUE KEY(.*?);/i',');',$this->queryString);
  497. $this->queryString = preg_replace('/set\(([^\)]*?)\)/','varchar',$this->queryString);
  498. $this->queryString = preg_replace('/enum\(([^\)]*?)\)/','varchar',$this->queryString);
  499. if ( preg_match("/PRIMARY KEY AUTOINCREMENT/",$this->queryString) )
  500. {
  501. $this->queryString = preg_replace('/,([\t\s ]+)PRIMARY KEY \(`([0-9a-zA-Z]+)`\)/i','',$this->queryString);
  502. $this->queryString = str_replace(', PRIMARY KEY (`id`)','',$this->queryString);
  503. }
  504. }
  505. $this->queryString = preg_replace("/SHOW fields FROM `([\w]+)`/i","PRAGMA table_info('\\1') ",$this->queryString);
  506. $this->queryString = preg_replace("/SHOW CREATE TABLE .([\w]+)/i","SELECT 0,sql FROM sqlite_master WHERE name='\\1'; ",$this->queryString);
  507. //var_dump($this->queryString);
  508. $this->queryString = preg_replace("/Show Tables/i","SELECT name FROM sqlite_master WHERE type = \"table\"",$this->queryString);
  509. $this->queryString = str_replace("\'","\"",$this->queryString);
  510. //var_dump($this->queryString);
  511. }
  512. function SetSql($sql)
  513. {
  514. $this->SetQuery($sql);
  515. }
  516. function RecordLog($runtime=0)
  517. {
  518. $RecordLogFile = dirname(__FILE__).'/../data/mysqli_record_log.inc';
  519. $url = $this->GetCurUrl();
  520. $savemsg = <<<EOT
  521. ------------------------------------------
  522. SQL:{$this->queryString}
  523. Page:$url
  524. Runtime:$runtime
  525. EOT;
  526. $fp = @fopen($RecordLogFile, 'a');
  527. @fwrite($fp, $savemsg);
  528. @fclose($fp);
  529. }
  530. //显示数据链接错误信息
  531. function DisplayError($msg)
  532. {
  533. $errorTrackFile = dirname(__FILE__).'/../data/mysqli_error_trace.inc';
  534. if( file_exists(dirname(__FILE__).'/../data/mysqli_error_trace.php') )
  535. {
  536. @unlink(dirname(__FILE__).'/../data/mysqli_error_trace.php');
  537. }
  538. if($this->showError)
  539. {
  540. $emsg = '';
  541. $emsg .= "<div><h3>DedeCMS Error Warning!</h3>\r\n";
  542. $emsg .= "<div><a href='http://bbs.dedecms.com' target='_blank' style='color:red'>Technical Support: http://bbs.dedecms.com</a></div>";
  543. $emsg .= "<div style='line-helght:160%;font-size:14px;color:green'>\r\n";
  544. $emsg .= "<div style='color:blue'><br />Error page: <font color='red'>".$this->GetCurUrl()."</font></div>\r\n";
  545. $emsg .= "<div>Error infos: {$msg}</div>\r\n";
  546. $emsg .= "<br /></div></div>\r\n";
  547. echo $emsg;
  548. }
  549. $savemsg = 'Page: '.$this->GetCurUrl()."\r\nError: ".$msg."\r\nTime".date('Y-m-d H:i:s');
  550. //保存MySql错误日志
  551. $fp = @fopen($errorTrackFile, 'a');
  552. @fwrite($fp, '<'.'?php exit();'."\r\n/*\r\n{$savemsg}\r\n*/\r\n?".">\r\n");
  553. @fclose($fp);
  554. }
  555. //获得当前的脚本网址
  556. function GetCurUrl()
  557. {
  558. if(!empty($_SERVER["REQUEST_URI"]))
  559. {
  560. $scriptName = $_SERVER["REQUEST_URI"];
  561. $nowurl = $scriptName;
  562. }
  563. else
  564. {
  565. $scriptName = $_SERVER["PHP_SELF"];
  566. if(empty($_SERVER["QUERY_STRING"])) {
  567. $nowurl = $scriptName;
  568. }
  569. else {
  570. $nowurl = $scriptName."?".$_SERVER["QUERY_STRING"];
  571. }
  572. }
  573. return $nowurl;
  574. }
  575. }
  576. //复制一个对象副本
  577. function CopySQLiPoint(&$ndsql)
  578. {
  579. $GLOBALS['dsqlite'] = $ndsql;
  580. }
  581. //SQL语句过滤程序,由80sec提供,这里作了适当的修改
  582. if (!function_exists('CheckSql'))
  583. {
  584. function CheckSql($db_string,$querytype='select')
  585. {
  586. global $cfg_cookie_encode;
  587. $clean = '';
  588. $error='';
  589. $old_pos = 0;
  590. $pos = -1;
  591. $log_file = DEDEINC.'/../data/'.md5($cfg_cookie_encode).'_safe.txt';
  592. $userIP = GetIP();
  593. $getUrl = GetCurUrl();
  594. //如果是普通查询语句,直接过滤一些特殊语法
  595. if($querytype=='select')
  596. {
  597. $notallow1 = "[^0-9a-z@\._-]{1,}(union|sleep|benchmark|load_file|outfile)[^0-9a-z@\.-]{1,}";
  598. //$notallow2 = "--|/\*";
  599. if(preg_match("/".$notallow1."/i", $db_string))
  600. {
  601. fputs(fopen($log_file,'a+'),"$userIP||$getUrl||$db_string||SelectBreak\r\n");
  602. exit("<font size='5' color='red'>Safe Alert: Request Error step 1 !</font>");
  603. }
  604. }
  605. //完整的SQL检查
  606. while (TRUE)
  607. {
  608. $pos = strpos($db_string, '\'', $pos + 1);
  609. if ($pos === FALSE)
  610. {
  611. break;
  612. }
  613. $clean .= substr($db_string, $old_pos, $pos - $old_pos);
  614. while (TRUE)
  615. {
  616. $pos1 = strpos($db_string, '\'', $pos + 1);
  617. $pos2 = strpos($db_string, '\\', $pos + 1);
  618. if ($pos1 === FALSE)
  619. {
  620. break;
  621. }
  622. elseif ($pos2 == FALSE || $pos2 > $pos1)
  623. {
  624. $pos = $pos1;
  625. break;
  626. }
  627. $pos = $pos2 + 1;
  628. }
  629. $clean .= '$s$';
  630. $old_pos = $pos + 1;
  631. }
  632. $clean .= substr($db_string, $old_pos);
  633. $clean = trim(strtolower(preg_replace(array('~\s+~s' ), array(' '), $clean)));
  634. if (strpos($clean, '@') !== FALSE OR strpos($clean,'char(')!== FALSE OR strpos($clean,'"')!== FALSE
  635. OR strpos($clean,'$s$$s$')!== FALSE)
  636. {
  637. $fail = TRUE;
  638. if(preg_match("#^create table#i",$clean)) $fail = FALSE;
  639. $error="unusual character";
  640. }
  641. //老版本的Mysql并不支持union,常用的程序里也不使用union,但是一些黑客使用它,所以检查它
  642. if (strpos($clean, 'union') !== FALSE && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0)
  643. {
  644. $fail = TRUE;
  645. $error="union detect";
  646. }
  647. //发布版本的程序可能比较少包括--,#这样的注释,但是黑客经常使用它们
  648. elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== FALSE || strpos($clean, '#') !== FALSE)
  649. {
  650. $fail = TRUE;
  651. $error="comment detect";
  652. }
  653. //这些函数不会被使用,但是黑客会用它来操作文件,down掉数据库
  654. elseif (strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~s', $clean) != 0)
  655. {
  656. $fail = TRUE;
  657. $error="slown down detect";
  658. }
  659. elseif (strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
  660. {
  661. $fail = TRUE;
  662. $error="slown down detect";
  663. }
  664. elseif (strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~s', $clean) != 0)
  665. {
  666. $fail = TRUE;
  667. $error="file fun detect";
  668. }
  669. elseif (strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~s', $clean) != 0)
  670. {
  671. $fail = TRUE;
  672. $error="file fun detect";
  673. }
  674. //老版本的MYSQL不支持子查询,我们的程序里可能也用得少,但是黑客可以使用它来查询数据库敏感信息
  675. elseif (preg_match('~\([^)]*?select~s', $clean) != 0)
  676. {
  677. $fail = TRUE;
  678. $error="sub select detect";
  679. }
  680. if (!empty($fail))
  681. {
  682. fputs(fopen($log_file,'a+'),"$userIP||$getUrl||$db_string||$error\r\n");
  683. exit("<font size='5' color='red'>Safe Alert: Request Error step 2!</font>");
  684. }
  685. else
  686. {
  687. return $db_string;
  688. }
  689. }
  690. }