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

689 lines
24KB

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