PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/core/libraries/inputfilter.php

https://github.com/joostina/joostina
PHP | 268 lines | 221 code | 26 blank | 21 comment | 67 complexity | 01d181f1c95d08f53fc547f12298c4c9 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-3.0, LGPL-2.1
  1. <?php defined('_JOOS_CORE') or exit;
  2. /**
  3. * Библиотека обеспечения безопасности при работе со входящими данными
  4. *
  5. * @version 1.0
  6. * @package Core\Libraries
  7. * @subpackage Inputfilter
  8. * @category Libraries
  9. * @author Joostina Team <info@joostina.ru>
  10. * @copyright (C) 2007-2012 Joostina Team
  11. * @license MIT License http://www.opensource.org/licenses/mit-license.php
  12. * Информация об авторах и лицензиях стороннего кода в составе Joostina CMS: docs/copyrights
  13. *
  14. * */
  15. class joosInputFilter
  16. {
  17. /**
  18. * @var joosInputFilter
  19. */
  20. private static $instance;
  21. protected $tagsArray;
  22. protected $attrArray;
  23. protected $tagsMethod;
  24. protected $attrMethod;
  25. protected $xssAuto;
  26. protected $tagBlacklist = array('applet', 'body', 'bgsound', 'base', 'basefont', 'embed', 'frame', 'frameset', 'head', 'html', 'id', 'iframe', 'ilayer', 'layer', 'link', 'meta', 'name', 'object', 'script', 'style', 'title', 'xml');
  27. protected $attrBlacklist = array('action', 'background', 'codebase', 'dynsrc', 'lowsrc');
  28. private function __construct($tagsArray = array(), $attrArray = array(), $tagsMethod = 0, $attrMethod = 0, $xssAuto = 1)
  29. {
  30. $tagsArray = array_map('strtolower', (array) $tagsArray);
  31. $attrArray = array_map('strtolower', (array) $attrArray);
  32. $this->tagsArray = (array) $tagsArray;
  33. $this->attrArray = (array) $attrArray;
  34. $this->tagsMethod = $tagsMethod;
  35. $this->attrMethod = $attrMethod;
  36. $this->xssAuto = $xssAuto;
  37. }
  38. public static function instance($tagsArray = array(), $attrArray = array(), $tagsMethod = 0, $attrMethod = 0, $xssAuto = 1)
  39. {
  40. !JDEBUG ? : joosDebug::inc('joosInputFilter::instance');
  41. if (self::$instance === null) {
  42. self::$instance = new self($tagsArray, $attrArray, $tagsMethod, $attrMethod, $xssAuto);
  43. }
  44. return self::$instance;
  45. }
  46. private function __clone()
  47. {
  48. }
  49. public function process($source)
  50. {
  51. if (is_array($source)) {
  52. foreach ($source as $key => $value) {
  53. if (is_string($value)) {
  54. $source[$key] = $this->remove($this->decode($value));
  55. }
  56. }
  57. return $source;
  58. } else {
  59. if (is_string($source) && !empty($source)) {
  60. return $this->remove($this->decode($source));
  61. } else {
  62. return $source;
  63. }
  64. }
  65. }
  66. protected function remove($source)
  67. {
  68. //$loopCounter = 0;
  69. while ($source != $this->filterTags($source)) {
  70. $source = $this->filterTags($source);
  71. //$loopCounter++;
  72. }
  73. return $source;
  74. }
  75. protected function filterTags($source)
  76. {
  77. $preTag = null;
  78. $postTag = $source;
  79. $tagOpen_start = strpos($source, '<');
  80. while ($tagOpen_start !== false) {
  81. $preTag .= substr($postTag, 0, $tagOpen_start);
  82. $postTag = substr($postTag, $tagOpen_start);
  83. $fromTagOpen = substr($postTag, 1);
  84. $tagOpen_end = strpos($fromTagOpen, '>');
  85. if ($tagOpen_end === false) {
  86. $postTag = substr($postTag, $tagOpen_start + 1);
  87. $tagOpen_start = strpos($postTag, '<');
  88. continue;
  89. }
  90. $tagOpen_nested = strpos($fromTagOpen, '<');
  91. //$tagOpen_nested_end = strpos(substr($postTag,$tagOpen_end),'>');
  92. if (($tagOpen_nested !== false) && ($tagOpen_nested < $tagOpen_end)) {
  93. $preTag .= substr($postTag, 0, ($tagOpen_nested + 1));
  94. $postTag = substr($postTag, ($tagOpen_nested + 1));
  95. $tagOpen_start = strpos($postTag, '<');
  96. continue;
  97. }
  98. //$tagOpen_nested = (strpos($fromTagOpen, '<') + $tagOpen_start + 1);
  99. $currentTag = substr($fromTagOpen, 0, $tagOpen_end);
  100. $tagLength = strlen($currentTag);
  101. $tagLeft = $currentTag;
  102. $attrSet = array();
  103. $currentSpace = strpos($tagLeft, ' ');
  104. if (substr($currentTag, 0, 1) == "/") {
  105. $isCloseTag = true;
  106. list($tagName) = explode(' ', $currentTag);
  107. $tagName = substr($tagName, 1);
  108. } else {
  109. $isCloseTag = false;
  110. list($tagName) = explode(' ', $currentTag);
  111. }
  112. if ((!preg_match("/^[a-z][a-z0-9]*$/iu", $tagName)) || (!$tagName) || ((in_array(strtolower($tagName), $this->tagBlacklist)) && ($this->xssAuto))) {
  113. $postTag = substr($postTag, ($tagLength + 2));
  114. $tagOpen_start = strpos($postTag, '<');
  115. continue;
  116. }
  117. while ($currentSpace !== false) {
  118. $fromSpace = substr($tagLeft, ($currentSpace + 1));
  119. $nextSpace = strpos($fromSpace, ' ');
  120. $openQuotes = strpos($fromSpace, '"');
  121. $closeQuotes = strpos(substr($fromSpace, ($openQuotes + 1)), '"') + $openQuotes + 1;
  122. if (strpos($fromSpace, '=') !== false) {
  123. if (($openQuotes !== false) && (strpos(substr($fromSpace, ($openQuotes + 1)), '"') !== false)) {
  124. $attr = substr($fromSpace, 0, ($closeQuotes + 1));
  125. } else {
  126. $attr = substr($fromSpace, 0, $nextSpace);
  127. }
  128. } else {
  129. $attr = substr($fromSpace, 0, $nextSpace);
  130. }
  131. if (!$attr) {
  132. $attr = $fromSpace;
  133. }
  134. $attrSet[] = $attr;
  135. $tagLeft = substr($fromSpace, strlen($attr));
  136. $currentSpace = strpos($tagLeft, ' ');
  137. }
  138. $tagFound = in_array(strtolower($tagName), $this->tagsArray);
  139. if ((!$tagFound && $this->tagsMethod) || ($tagFound && !$this->tagsMethod)) {
  140. if (!$isCloseTag) {
  141. $attrSet = $this->filterAttr($attrSet);
  142. $preTag .= '<' . $tagName;
  143. for ($i = 0; $i < count($attrSet); $i++) {
  144. $preTag .= ' ' . $attrSet[$i];
  145. }
  146. if (strpos($fromTagOpen, "</" . $tagName)) {
  147. $preTag .= '>';
  148. } else {
  149. $preTag .= ' />';
  150. }
  151. } else {
  152. $preTag .= '</' . $tagName . '>';
  153. }
  154. }
  155. $postTag = substr($postTag, ($tagLength + 2));
  156. $tagOpen_start = strpos($postTag, '<');
  157. }
  158. if ($postTag != '<') {
  159. $preTag .= $postTag;
  160. }
  161. return $preTag;
  162. }
  163. protected function filterAttr($attrSet)
  164. {
  165. $newSet = array();
  166. for ($i = 0; $i < count($attrSet); $i++) {
  167. if (!$attrSet[$i]) {
  168. continue;
  169. }
  170. $attrSubSet = explode('=', trim($attrSet[$i]), 2);
  171. list($attrSubSet[0]) = explode(' ', $attrSubSet[0]);
  172. // TODO eregi - зло
  173. if ((!eregi("^[a-z]*$", $attrSubSet[0])) || (($this->xssAuto) && ((in_array(strtolower($attrSubSet[0]), $this->attrBlacklist)) || (substr(strtolower($attrSubSet[0]), 0, 2) == 'on')))) {
  174. continue;
  175. }
  176. if ($attrSubSet[1]) {
  177. $attrSubSet[1] = str_replace('&#', '', $attrSubSet[1]);
  178. $attrSubSet[1] = preg_replace('/\s+/', '', $attrSubSet[1]);
  179. $attrSubSet[1] = str_replace('"', '', $attrSubSet[1]);
  180. if ((substr($attrSubSet[1], 0, 1) == "'") && (substr($attrSubSet[1], (strlen($attrSubSet[1]) - 1), 1) == "'")) {
  181. $attrSubSet[1] = substr($attrSubSet[1], 1, (strlen($attrSubSet[1]) - 2));
  182. }
  183. $attrSubSet[1] = stripslashes($attrSubSet[1]);
  184. }
  185. if (self::badAttributeValue($attrSubSet)) {
  186. continue;
  187. }
  188. $attrFound = in_array(strtolower($attrSubSet[0]), $this->attrArray);
  189. if ((!$attrFound && $this->attrMethod) || ($attrFound && !$this->attrMethod)) {
  190. if ($attrSubSet[1]) {
  191. $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[1] . '"';
  192. } elseif ($attrSubSet[1] == "0") {
  193. $newSet[] = $attrSubSet[0] . '="0"';
  194. } else {
  195. $newSet[] = $attrSubSet[0] . '="' . $attrSubSet[0] . '"';
  196. }
  197. }
  198. }
  199. return $newSet;
  200. }
  201. public function badAttributeValue($attrSubSet)
  202. {
  203. $attrSubSet[0] = strtolower($attrSubSet[0]);
  204. $attrSubSet[1] = strtolower($attrSubSet[1]);
  205. return (((strpos($attrSubSet[1], 'expression') !== false) && ($attrSubSet[0]) == 'style') || (strpos($attrSubSet[1], 'javascript:') !== false) || (strpos($attrSubSet[1], 'behaviour:') !== false) || (strpos($attrSubSet[1], 'vbscript:') !== false) || (strpos($attrSubSet[1], 'mocha:') !== false) || (strpos($attrSubSet[1], 'livescript:') !== false));
  206. }
  207. protected function decode($source)
  208. {
  209. $source = html_entity_decode($source, ENT_QUOTES, "UTF-8");
  210. $source = preg_replace('/&#(\d+);/me', "chr(\\1)", $source);
  211. $source = preg_replace('/&#x([a-f0-9]+);/mei', "chr(0x\\1)", $source);
  212. return $source;
  213. }
  214. public function safeSQL($source)
  215. {
  216. if (is_array($source)) {
  217. foreach ($source as $key => $value) {
  218. if (is_string($value)) {
  219. $source[$key] = $this->quoteSmart($this->decode($value));
  220. }
  221. }
  222. return $source;
  223. } elseif (is_string($source)) {
  224. if (is_string($source)) {
  225. return $this->quoteSmart($this->decode($source));
  226. }
  227. } else {
  228. return $source;
  229. }
  230. return 'error';
  231. }
  232. protected function quoteSmart($source)
  233. {
  234. $source = $this->escapeString($source);
  235. return $source;
  236. }
  237. protected function escapeString($string)
  238. {
  239. $string = mysql_real_escape_string($string);
  240. return $string;
  241. }
  242. }