国内流行的内容管理系统(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.

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