AttackCheckMiddleware.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <?php
  2. namespace app\agent\middleware;
  3. use Lettered\Support\Exceptions\ForbiddenException;
  4. class AttackCheckMiddleware
  5. {
  6. private $config = [];
  7. /**
  8. * AttackCheckMiddleware constructor.
  9. */
  10. public function __construct()
  11. {
  12. //默认配置
  13. $_config = [
  14. 'url' => $_SERVER['REQUEST_URI'], //验证的url
  15. 'white_urls' => [
  16. '^/admin/*', //路径白名单 (^)匹配开头(*)匹配任意字符 不含括号
  17. '^/ajax_format.html=>post.code,get.code,cookie.code', //路径参数白名单 当匹配到指定地址时 不验证指定的参数
  18. ],
  19. 'http_html' =>'PCFET0NUWVBFIEhUTUw+DQo8aHRtbD4NCjxoZWFkPg0KPG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLTgiIC8+DQo8dGl0bGU+572R56uZ6Ziy54Gr5aKZPC90aXRsZT4NCjxzdHlsZT4NCip7cGFkZGluZzowO21hcmdpbjowfQ0KYm9keXtmb250OjE0cHgvMS41IE1pY3Jvc29mdCBZYWhlaSzlrovkvZMsc2Fucy1zZXJpZjtjb2xvcjojNTU1O2xpbmUtaGVpZ2h0OjI1cHh9DQo8L3N0eWxlPg0KPC9oZWFkPg0KPGJvZHk+DQo8ZGl2IHN0eWxlPSJ3aWR0aDo2MDBweDtjbGVhcjpib3RoO21hcmdpbjowIGF1dG87bWFyZ2luLXRvcDoxMCUiPg0KICA8ZGl2IHN0eWxlPSJsaW5lLWhlaWdodDo0MHB4O2NvbG9yOiNmZmY7Zm9udC1zaXplOjE2cHg7YmFja2dyb3VuZDojNmJiM2Y2O3BhZGRpbmctbGVmdDoyMHB4OyI+572R56uZ6Ziy54Gr5aKZPC9kaXY+DQogIDxkaXYgc3R5bGU9ImJvcmRlcjoxcHggZGFzaGVkICNjZGNlY2U7Ym9yZGVyLXRvcDpub25lO2ZvbnQtc2l6ZToxNHB4O2hlaWdodDoyMjBweDtwYWRkaW5nOjIwcHg7YmFja2dyb3VuZDojZjNmN2Y5OyI+DQogICAgPHAgc3R5bGU9ImZvbnQtd2VpZ2h0OjYwMDtjb2xvcjojZmM0ZjAzOyI+5oKo55qE6K+35rGC5bim5pyJ5LiN5ZCI5rOV5Y+C5pWw77yM5bey6KKr572R56uZ566h55CG5ZGY6K6+572u5oum5oiq77yBPC9wPg0KICAgIDxwPuWPr+iDveWOn+WboO+8muaCqOaPkOS6pOeahOWGheWuueWMheWQq+WNsemZqeeahOaUu+WHu+ivt+axgjwvcD4NCiAgICA8cCBzdHlsZT0ibWFyZ2luLXRvcDoxNXB4OyI+5aaC5L2V6Kej5Yaz77yaPC9wPg0KICAgIDxwPjHvvInmo4Dmn6Xmj5DkuqTlhoXlrrnvvJs8L3A+DQogICAgPHA+Mu+8ieWmgue9keermeaJmOeuoe+8jOivt+iBlOezu+epuumXtOaPkOS+m+WVhu+8mzwvcD4NCiAgICA8cD4z77yJ5pmu6YCa572R56uZ6K6/5a6i77yM6K+36IGU57O7572R56uZ566h55CG5ZGY77ybPC9wPg0KICA8L2Rpdj4NCjwvZGl2Pg0KPC9ib2R5Pg0KPC9odG1sPg==',
  20. 'rules' => [
  21. 'get' => [
  22. 'content' => $_GET,
  23. 'filter' => "\\<.+javascript:window\\[.{1}\\\\x|<.*=(&#\\d+?;?)+?>|<.*(data|src)=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[a-z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)",
  24. ],
  25. 'post' => [
  26. 'content' => $_POST,
  27. 'filter' => "<.*=(&#\\d+?;?)+?>|<.*data=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)",
  28. ],
  29. 'cookie' => [
  30. 'content' => $_COOKIE,
  31. 'filter' => "benchmark\s*?\(.*\)|sleep\s*?\(.*\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)",
  32. ],
  33. 'referer' => [
  34. 'content' => isset($_SERVER['HTTP_REFERER']) ?: '',
  35. 'filter' => "\\<.+javascript:window\\[.{1}\\\\x|<.*=(&#\\d+?;?)+?>|<.*(data|src)=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\(.*\)|sleep\s*?\(.*\)|\\b(group_)?concat[\\s\\/\\*]*?\\([^\\)]+?\\)|\bcase[\s\/\*]*?when[\s\/\*]*?\([^\)]+?\)|load_file\s*?\\()|<[a-z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)|UPDATE\s*(\(.+\)\s*|@{1,2}.+?\s*|\s+?.+?|(`|'|\").*?(`|'|\")\s*)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)@{0,2}(\\(.+\\)|\\s+?.+?\\s+?|(`|'|\").*?(`|'|\"))FROM(\\(.+\\)|\\s+?.+?|(`|'|\").*?(`|'|\"))|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)",
  36. ],
  37. ],
  38. ];
  39. //默认配置
  40. if (empty($config)) {
  41. $config = $_config;
  42. }
  43. //格式化白名单
  44. if (!is_array($config['white_urls']) && $config['white_urls']) {
  45. $config['white_urls'] = explode("\n", $config['white_urls']);
  46. }
  47. //遍历规则排除白名单参数 并删除 该白名单网址
  48. foreach ($config['white_urls'] as $index => $url) {
  49. $url = trim($url);
  50. if (strpos($url, '=>')) {
  51. //获取白名单参数
  52. list($_url, $_param) = explode('=>', $url);
  53. //判断是否符合当前url
  54. $_url = preg_quote($_url, '/');
  55. $_url = str_replace('\^', '^', $_url);
  56. $_url = str_replace('\*', '[\s\S]*', $_url);
  57. if (preg_match("/" . $_url . "/is", $config['url'], $matches)) {
  58. $params = explode(',', $_param);
  59. foreach ($params as $param) {
  60. list($type, $name) = explode('.', $param);
  61. unset($config['rules'][$type]['content'][$name]);
  62. }
  63. }
  64. unset($config['white_urls'][$index]);
  65. }
  66. }
  67. $this->config = $config;
  68. }
  69. /**
  70. *
  71. *
  72. * @author 许祖兴 < zuxing.xu@lettered.cn>
  73. * @date 2020/3/23 16:42
  74. *
  75. * @param $request
  76. * @param \Closure $next
  77. * @return bool|mixed
  78. * @throws ForbiddenException
  79. */
  80. public function handle($request, \Closure $next)
  81. {
  82. //判断白名单
  83. if (!$this->inWhiteList()) {
  84. //遍历判断规则
  85. foreach ($this->config['rules'] as $rule) {
  86. if ($rule['content'] && $rule['filter']) {
  87. $content = var_export($rule['content'], true);
  88. if (preg_match("/" . $rule['filter'] . "/is", $content, $matches)) {
  89. //exit(base64_decode($this->config['http_html']));
  90. throw new ForbiddenException([
  91. 'errmsg' => 'Forbidden: Invalid request params!'
  92. ]);
  93. }
  94. }
  95. }
  96. }
  97. return $next($request);
  98. }
  99. /**
  100. * 判断是否在白名单里
  101. *
  102. * @author 许祖兴 < zuxing.xu@lettered.cn>
  103. * @date 2020/3/23 16:40
  104. *
  105. * @return bool
  106. */
  107. private function inWhiteList()
  108. {
  109. if (!$this->config['white_urls']) {
  110. return false;
  111. }
  112. //判断白名单(^)匹配开头(*)匹配任意字符 不含括号
  113. foreach ($this->config['white_urls'] as $url) {
  114. $url = trim($url);
  115. $url = preg_quote($url, '/');
  116. $url = str_replace('\^', '^', $url);
  117. $url = str_replace('\*', '[\s\S]*', $url);
  118. if (preg_match("/" . $url . "/is", $this->config['url'], $matches)) {
  119. return true;
  120. }
  121. }
  122. return false;
  123. }
  124. }