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

627 lines
23KB

  1. <?php
  2. if (!defined('DEDEINC')) exit('dedebiz');
  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: dedesqlite.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. if (!extension_loaded("sqlite3")) {
  21. ShowMsg("尚未发现开启sqlite3模块,请在php.ini中启用`extension=sqlite3`","javasctipt:;",-1) ;
  22. exit;
  23. }
  24. //在工程所有文件中均不需要单独初始化这个类,可直接用 $dsql 或 $db 进行操作
  25. //为了防止错误,操作完后不必关闭数据库
  26. $dsql = $dsqlitete = $db = new DedeSqlite(FALSE);
  27. /**
  28. * Dede SQLite3数据库类
  29. *
  30. * @package DedeSqli
  31. * @subpackage DedeBIZ.Libraries
  32. * @link https://www.dedebiz.com
  33. */
  34. if (!defined('MYSQL_BOTH')) {
  35. define('MYSQL_BOTH', MYSQLI_BOTH);
  36. }
  37. if (!defined('MYSQL_ASSOC')) {
  38. define('MYSQL_ASSOC', SQLITE3_ASSOC);
  39. }
  40. if (version_compare(PHP_VERSION, '8.0.0', '>=')) {
  41. mysqli_report(MYSQLI_REPORT_OFF);
  42. }
  43. class DedeSqlite
  44. {
  45. var $linkID;
  46. var $dbHost;
  47. var $dbUser;
  48. var $dbPwd;
  49. var $dbName;
  50. var $dbPrefix;
  51. var $result;
  52. var $queryString;
  53. var $parameters;
  54. var $isClose;
  55. var $safeCheck;
  56. var $showError = true;
  57. var $recordLog = false; //记录日志到data/mysqli_record_log.inc便于进行调试
  58. var $isInit = false;
  59. var $pconnect = false;
  60. var $_fixObject;
  61. //用外部定义的变量初始类,并连接数据库
  62. function __construct($pconnect = FALSE, $nconnect = FALSE)
  63. {
  64. $this->isClose = FALSE;
  65. $this->safeCheck = TRUE;
  66. $this->pconnect = $pconnect;
  67. if ($nconnect) {
  68. $this->Init($pconnect);
  69. }
  70. }
  71. function DedeSql($pconnect = FALSE, $nconnect = TRUE)
  72. {
  73. $this->__construct($pconnect, $nconnect);
  74. }
  75. function Init($pconnect = FALSE)
  76. {
  77. $this->linkID = 0;
  78. //$this->queryString = '';
  79. //$this->parameters = Array();
  80. $this->dbHost = $GLOBALS['cfg_dbhost'];
  81. $this->dbUser = $GLOBALS['cfg_dbuser'];
  82. $this->dbPwd = $GLOBALS['cfg_dbpwd'];
  83. $this->dbName = $GLOBALS['cfg_dbname'];
  84. $this->dbPrefix = $GLOBALS['cfg_dbprefix'];
  85. $this->result["me"] = 0;
  86. $this->Open($pconnect);
  87. }
  88. //用指定参数初始数据库信息
  89. function SetSource($host, $username, $pwd, $dbname, $dbprefix = "dede_")
  90. {
  91. $this->dbHost = $host;
  92. $this->dbUser = $username;
  93. $this->dbPwd = $pwd;
  94. $this->dbName = $dbname;
  95. $this->dbPrefix = $dbprefix;
  96. $this->result["me"] = 0;
  97. }
  98. //设置SQL里的参数
  99. function SetParameter($key, $value)
  100. {
  101. $this->parameters[$key] = $value;
  102. }
  103. //连接数据库
  104. function Open($pconnect = FALSE)
  105. {
  106. global $dsqlite;
  107. //连接数据库
  108. if ($dsqlite && !$dsqlite->isClose && $dsqlite->isInit) {
  109. $this->linkID = $dsqlite->linkID;
  110. } else {
  111. $this->linkID = new SQLite3(DEDEDATA.'/'.$this->dbName.'.db');
  112. //复制一个对象副本
  113. CopySQLiPoint($this);
  114. }
  115. //处理错误,成功连接则选择数据库
  116. if (!$this->linkID) {
  117. $this->DisplayError("系统提示:<span style='color:#ff5722'>连接数据库失败,可能数据库密码不对或数据库服务器出错</span>");
  118. exit();
  119. }
  120. $this->isInit = TRUE;
  121. return TRUE;
  122. }
  123. //为了防止采集等需要较长运行时间的程序超时,在运行这类程序时设置系统等待和交互时间
  124. function SetLongLink()
  125. {
  126. // @mysqli_query("SET interactive_timeout=3600, wait_timeout=3600 ;", $this->linkID);
  127. }
  128. //获得错误描述
  129. function GetError()
  130. {
  131. return $this->linkID->lastErrorMsg();
  132. }
  133. //关闭数据库
  134. //mysql能自动管理非持久连接的连接池
  135. //实际上关闭并无意义并且容易出错,所以取消这函数
  136. function Close($isok = FALSE)
  137. {
  138. $this->FreeResultAll();
  139. if ($isok) {
  140. $this->linkID->close();
  141. $this->isClose = TRUE;
  142. $GLOBALS['dsql'] = NULL;
  143. }
  144. }
  145. //定期清理死连接
  146. function ClearErrLink()
  147. {
  148. }
  149. //关闭指定的数据库连接
  150. function CloseLink($dblink)
  151. {
  152. }
  153. function Esc($_str)
  154. {
  155. global $dsqlite;
  156. if (!$dsqlite->isInit) {
  157. $this->Init($this->pconnect);
  158. }
  159. return $this->linkID->escapeString($_str);
  160. }
  161. //执行一个不返回结果的SQL语句,如update,delete,insert等
  162. function ExecuteNoneQuery($sql = '')
  163. {
  164. global $dsqlite;
  165. if (!$dsqlite->isInit) {
  166. $this->Init($this->pconnect);
  167. }
  168. if ($dsqlite->isClose) {
  169. $this->Open(FALSE);
  170. $dsqlite->isClose = FALSE;
  171. }
  172. if (!empty($sql)) {
  173. $this->SetQuery($sql);
  174. } else {
  175. return FALSE;
  176. }
  177. if (is_array($this->parameters)) {
  178. foreach ($this->parameters as $key => $value) {
  179. $this->queryString = str_replace("@".$key, "'$value'", $this->queryString);
  180. }
  181. }
  182. //SQL语句安全检查
  183. if ($this->safeCheck) CheckSql($this->queryString, 'update');
  184. $t1 = ExecTime();
  185. $rs = $this->linkID->exec($this->queryString);
  186. //查询性能测试
  187. if ($this->recordLog) {
  188. $queryTime = ExecTime() - $t1;
  189. $this->RecordLog($queryTime);
  190. //echo $this->queryString."--{$queryTime}<hr />\r\n";
  191. }
  192. return $rs;
  193. }
  194. //执行一个返回影响记录条数的SQL语句,如update,delete,insert等
  195. function ExecuteNoneQuery2($sql = '')
  196. {
  197. global $dsqlite;
  198. if (!$dsqlite->isInit) {
  199. $this->Init($this->pconnect);
  200. }
  201. if ($dsqlite->isClose) {
  202. $this->Open(FALSE);
  203. $dsqlite->isClose = FALSE;
  204. }
  205. if (!empty($sql)) {
  206. $this->SetQuery($sql);
  207. }
  208. if (is_array($this->parameters)) {
  209. foreach ($this->parameters as $key => $value) {
  210. $this->queryString = str_replace("@".$key, "'$value'", $this->queryString);
  211. }
  212. }
  213. $t1 = ExecTime();
  214. $this->linkID->exec($this->queryString);
  215. //查询性能测试
  216. if ($this->recordLog) {
  217. $queryTime = ExecTime() - $t1;
  218. $this->RecordLog($queryTime);
  219. //echo $this->queryString."--{$queryTime}<hr />\r\n";
  220. }
  221. return $this->linkID->changes();
  222. }
  223. function ExecNoneQuery($sql = '')
  224. {
  225. return $this->ExecuteNoneQuery($sql);
  226. }
  227. function GetFetchRow($id = 'me')
  228. {
  229. return $this->result[$id]->numColumns();
  230. }
  231. function GetAffectedRows()
  232. {
  233. return $this->linkID->changes();
  234. }
  235. //执行一个带返回结果的SQL语句,如SELECT,SHOW等
  236. function Execute($id = "me", $sql = '')
  237. {
  238. global $dsqlite;
  239. if (!$dsqlite->isInit) {
  240. $this->Init($this->pconnect);
  241. }
  242. if ($dsqlite->isClose) {
  243. $this->Open(FALSE);
  244. $dsqlite->isClose = FALSE;
  245. }
  246. if (!empty($sql)) {
  247. $this->SetQuery($sql);
  248. }
  249. //SQL语句安全检查
  250. if ($this->safeCheck) {
  251. CheckSql($this->queryString);
  252. }
  253. $t1 = ExecTime();
  254. //var_dump($this->queryString);
  255. $this->result[$id] = $this->linkID->query($this->queryString);
  256. if (!$this->result[$id]) {
  257. $this->DisplayError("执行SQL错误:{$this->linkID->lastErrorMsg()}");
  258. exit;
  259. }
  260. //var_dump(mysql_error());
  261. //查询性能测试
  262. if ($this->recordLog) {
  263. $queryTime = ExecTime() - $t1;
  264. $this->RecordLog($queryTime);
  265. //echo $this->queryString."--{$queryTime}<hr />\r\n";
  266. }
  267. if ($this->result[$id] === FALSE) {
  268. $this->DisplayError($this->linkID->lastErrorMsg()." <br>Error sql: <span style='color:#ff5722'>".$this->queryString."</span>");
  269. }
  270. }
  271. function Query($id = "me", $sql = '')
  272. {
  273. $this->Execute($id, $sql);
  274. }
  275. //执行一个SQL语句,返回前一条记录或仅返回一条记录
  276. function GetOne($sql = '', $acctype = SQLITE3_ASSOC)
  277. {
  278. global $dsqlite;
  279. if (!$dsqlite->isInit) {
  280. $this->Init($this->pconnect);
  281. }
  282. if ($dsqlite->isClose) {
  283. $this->Open(FALSE);
  284. $dsqlite->isClose = FALSE;
  285. }
  286. if (!empty($sql)) {
  287. if (!preg_match("/LIMIT/i", $sql)) $this->SetQuery(preg_replace("/[,;]$/i", '', trim($sql))." LIMIT 0,1;");
  288. else $this->SetQuery($sql);
  289. }
  290. $this->Execute("one");
  291. $arr = $this->GetArray("one", $acctype);
  292. if (!is_array($arr)) {
  293. return '';
  294. } else {
  295. $this->result["one"]->reset();
  296. return ($arr);
  297. }
  298. }
  299. //执行一个不与任何表名有关的SQL语句,Create等
  300. function ExecuteSafeQuery($sql, $id = "me")
  301. {
  302. global $dsqlite;
  303. if (!$dsqlite->isInit) {
  304. $this->Init($this->pconnect);
  305. }
  306. if ($dsqlite->isClose) {
  307. $this->Open(FALSE);
  308. $dsqlite->isClose = FALSE;
  309. }
  310. $this->result[$id] = $this->linkID->query($sql);
  311. }
  312. //返回当前的一条记录并把游标移向下一记录
  313. //SQLITE3_ASSOC、SQLITE3_NUM、SQLITE3_BOTH
  314. function GetArray($id = "me", $acctype = SQLITE3_ASSOC)
  315. {
  316. switch ($acctype) {
  317. case MYSQL_ASSOC:
  318. $acctype = SQLITE3_ASSOC;
  319. break;
  320. case MYSQL_NUM:
  321. $acctype = SQLITE3_NUM;
  322. break;
  323. default:
  324. $acctype = SQLITE3_BOTH;
  325. break;
  326. }
  327. if ($this->result[$id] === 0) {
  328. return FALSE;
  329. } else {
  330. $rs = $this->result[$id]->fetchArray($acctype);
  331. if (!$rs) {
  332. $this->result[$id] = 0;
  333. return false;
  334. }
  335. return $rs;
  336. }
  337. }
  338. function GetObject($id = "me")
  339. {
  340. if (!isset($this->_fixObject[$id])) {
  341. $this->_fixObject[$id] = array();
  342. while ($row = $this->result[$id]->fetchArray(SQLITE3_ASSOC)) {
  343. $this->_fixObject[$id][] = (object)$row;
  344. }
  345. $this->result[$id]->reset();
  346. }
  347. return array_shift($this->_fixObject[$id]);
  348. }
  349. //检测是否存在某数据表
  350. function IsTable($tbname)
  351. {
  352. global $dsqlite;
  353. if (!$dsqlite->isInit) {
  354. $this->Init($this->pconnect);
  355. }
  356. $prefix = "#@__";
  357. $tbname = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $tbname);
  358. $row = $this->linkID->querySingle("PRAGMA table_info({$tbname});");
  359. if ($row !== null) {
  360. return TRUE;
  361. }
  362. return FALSE;
  363. }
  364. //获得MySql的版本号
  365. function GetVersion($isformat = TRUE)
  366. {
  367. global $dsqlite;
  368. if (!$dsqlite->isInit) {
  369. $this->Init($this->pconnect);
  370. }
  371. if ($dsqlite->isClose) {
  372. $this->Open(FALSE);
  373. $dsqlite->isClose = FALSE;
  374. }
  375. $rs = $this->linkID->querySingle("select sqlite_version();");
  376. $sqlite_version = $rs;
  377. if ($isformat) {
  378. $sqlite_versions = explode(".", trim($sqlite_version));
  379. $sqlite_version = number_format($sqlite_versions[0].".".$sqlite_versions[1], 2);
  380. }
  381. return $sqlite_version;
  382. }
  383. //获取特定表的信息
  384. function GetTableFields($tbname, $id = "me")
  385. {
  386. global $dsqlite;
  387. if (!$dsqlite->isInit) {
  388. $this->Init($this->pconnect);
  389. }
  390. $prefix = "#@__";
  391. $tbname = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $tbname);
  392. $query = "SELECT * FROM {$tbname} LIMIT 0,1";
  393. $this->result[$id] = $this->linkID->query($query);
  394. }
  395. //获取字段详细信息
  396. function GetFieldObject($id = "me")
  397. {
  398. $cols = $this->result[$id]->numColumns();
  399. $fields = array();
  400. while ($row = $this->result[$id]->fetchArray()) {
  401. for ($i = 1; $i < $cols; $i++) {
  402. $fields[] = $this->result[$id]->columnName($i);
  403. }
  404. }
  405. return (object)$fields;
  406. }
  407. //获得查询的总记录数
  408. function GetTotalRow($id = "me")
  409. {
  410. $queryString = preg_replace("/SELECT(.*)FROM/isU", 'SELECT count(*) as dd FROM', $this->queryString);
  411. $rs = $this->linkID->query($queryString);
  412. $row = $rs->fetchArray();
  413. return $row['dd'];
  414. }
  415. //获取上一步INSERT操作产生的ID
  416. function GetLastID()
  417. {
  418. //如果 AUTO_INCREMENT 的列的类型是 BIGINT,则 mysqli_insert_id() 返回的值将不正确
  419. //可以在 SQL 查询中用 MySQL 内部的 SQL 函数 LAST_INSERT_ID() 来替代
  420. //$rs = mysqli_query($this->linkID, "Select LAST_INSERT_ID() as lid");
  421. //$row = mysqli_fetch_array($rs);
  422. //return $row["lid"];
  423. return $this->linkID->lastInsertRowID();
  424. }
  425. //释放记录集占用的资源
  426. function FreeResult($id = "me")
  427. {
  428. if ($this->result[$id]) {
  429. @$this->result[$id]->reset();
  430. }
  431. }
  432. function FreeResultAll()
  433. {
  434. if (!is_array($this->result)) {
  435. return '';
  436. }
  437. foreach ($this->result as $kk => $vv) {
  438. if ($vv) {
  439. @$vv->reset();
  440. }
  441. }
  442. }
  443. //设置SQL语句,会自动把SQL语句里的#@__替换为$this->dbPrefix(在配置文件中为$cfg_dbprefix)
  444. function SetQuery($sql)
  445. {
  446. $prefix = "#@__";
  447. $sql = str_replace($prefix, $GLOBALS['cfg_dbprefix'], $sql);
  448. $this->queryString = $sql;
  449. //$this->queryString = preg_replace("/CONCAT\(',', arc.typeid2, ','\)/i","printf(',%s,', arc.typeid2)",$this->queryString);
  450. if (preg_match("/CONCAT\(([^\)]*?)\)/i", $this->queryString, $matches)) {
  451. $this->queryString = preg_replace("/CONCAT\(([^\)]*?)\)/i", str_replace(",", "||", $matches[1]), $this->queryString);
  452. $this->queryString = str_replace("'||'", "','", $this->queryString);
  453. }
  454. $this->queryString = preg_replace("/FIND_IN_SET\('([\w]+)', arc.flag\)>0/i", "(',' || arc.flag || ',') LIKE '%,\\1,%'", $this->queryString);
  455. $this->queryString = preg_replace("/FIND_IN_SET\('([\w]+)', arc.flag\)<1/i", "(',' || arc.flag || ',') NOT LIKE '%,\\1,%'", $this->queryString);
  456. if (preg_match("/CREATE TABLE/i", $this->queryString)) {
  457. $this->queryString = preg_replace("/[\r\n]/", '', $this->queryString);
  458. $this->queryString = preg_replace('/character set (.*?) /i', '', $this->queryString);
  459. $this->queryString = preg_replace('/unsigned/i', '', $this->queryString);
  460. $this->queryString = str_replace('TYPE=MyISAM', '', $this->queryString);
  461. $this->queryString = preg_replace('/TINYINT\(([\d]+)\)/i', 'INTEGER', $this->queryString);
  462. $this->queryString = preg_replace('/mediumint\(([\d]+)\)/i', 'INTEGER', $this->queryString);
  463. $this->queryString = preg_replace('/smallint\(([\d]+)\)/i', 'INTEGER', $this->queryString);
  464. $this->queryString = preg_replace('/int\(([\d]+)\)/i', 'INTEGER', $this->queryString);
  465. $this->queryString = preg_replace('/auto_increment/i', 'PRIMARY KEY AUTOINCREMENT', $this->queryString);
  466. $this->queryString = preg_replace('/, KEY(.*?)MyISAM;/i', '', $this->queryString);
  467. $this->queryString = preg_replace('/, KEY(.*?);/i', ');', $this->queryString);
  468. $this->queryString = preg_replace('/, UNIQUE KEY(.*?);/i', ');', $this->queryString);
  469. $this->queryString = preg_replace('/set\(([^\)]*?)\)/', 'varchar', $this->queryString);
  470. $this->queryString = preg_replace('/enum\(([^\)]*?)\)/', 'varchar', $this->queryString);
  471. if (preg_match("/PRIMARY KEY AUTOINCREMENT/", $this->queryString)) {
  472. $this->queryString = preg_replace('/,([\t\s ]+)PRIMARY KEY \(`([0-9a-zA-Z]+)`\)/i', '', $this->queryString);
  473. $this->queryString = str_replace(', PRIMARY KEY (`id`)', '', $this->queryString);
  474. }
  475. }
  476. $this->queryString = preg_replace("/SHOW fields FROM `([\w]+)`/i", "PRAGMA table_info('\\1') ", $this->queryString);
  477. $this->queryString = preg_replace("/SHOW CREATE TABLE .([\w]+)/i", "SELECT 0,sql FROM sqlite_master WHERE name='\\1'; ", $this->queryString);
  478. //var_dump($this->queryString);
  479. $this->queryString = preg_replace("/Show Tables/i", "SELECT name FROM sqlite_master WHERE type = \"table\"", $this->queryString);
  480. $this->queryString = str_replace("\'", "\"", $this->queryString);
  481. $this->queryString = str_replace('\t\n', "", $this->queryString);
  482. //var_dump($this->queryString);
  483. }
  484. function SetSql($sql)
  485. {
  486. $this->SetQuery($sql);
  487. }
  488. function RecordLog($runtime = 0)
  489. {
  490. $RecordLogFile = DEDEDATA.'/mysqli_record_log.inc';
  491. $url = $this->GetCurUrl();
  492. $savemsg = <<<EOT
  493. ------------------------------------------
  494. SQL:{$this->queryString}
  495. Page:$url
  496. Runtime:$runtime
  497. EOT;
  498. $fp = @fopen($RecordLogFile, 'a');
  499. @fwrite($fp, $savemsg);
  500. @fclose($fp);
  501. }
  502. //显示数据链接错误信息
  503. function DisplayError($msg)
  504. {
  505. $errorTrackFile = DEDEDATA.'/sqlite_error_trace.inc';
  506. if ($this->showError) {
  507. ShowMsg("{$msg}", "javascript:;", -1);
  508. exit;
  509. }
  510. $savemsg = 'Page: '.$this->GetCurUrl()."\r\nError: ".$msg."\r\nTime".date('Y-m-d H:i:s');
  511. //保存SQLite错误日志
  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("<span>Safe Alert: Request Error step 1 !</span>");
  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("<span>Safe Alert: Request Error step 2!</span>");
  622. } else {
  623. return $db_string;
  624. }
  625. }
  626. }