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

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