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

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