PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/fuel/core/classes/security.php

https://bitbucket.org/codeyash/bootstrap
PHP | 375 lines | 247 code | 44 blank | 84 comment | 17 complexity | 94736f9613b32b65805283969f964b21 MD5 | raw file
Possible License(s): MIT, Apache-2.0
  1. <?php
  2. /**
  3. * Part of the Fuel framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Fuel\Core;
  13. /**
  14. * Security Class
  15. *
  16. * @package Fuel
  17. * @category Core
  18. * @author Dan Horrigan
  19. * @link http://docs.fuelphp.com/classes/security.html
  20. */
  21. class Security
  22. {
  23. /**
  24. * @var string the token as submitted in the cookie from the previous request
  25. */
  26. protected static $csrf_old_token = false;
  27. /**
  28. * @var string the array key for cookie & post vars to check for the token
  29. */
  30. protected static $csrf_token_key = false;
  31. /**
  32. * @var string the token for the next request
  33. */
  34. protected static $csrf_token = false;
  35. /**
  36. * Class init
  37. *
  38. * Fetches CSRF settings and current token
  39. */
  40. public static function _init()
  41. {
  42. static::$csrf_token_key = \Config::get('security.csrf_token_key', 'fuel_csrf_token');
  43. static::$csrf_old_token = \Input::cookie(static::$csrf_token_key, false);
  44. if (\Config::get('security.csrf_autoload', true))
  45. {
  46. static::check_token();
  47. }
  48. // throw an exception if no the output filter setting is missing from the app config
  49. if (\Config::get('security.output_filter', null) === null)
  50. {
  51. throw new \FuelException('There is no security.output_filter defined in your application config file');
  52. }
  53. }
  54. /**
  55. * Cleans the request URI
  56. *
  57. * @param string $uri uri to clean
  58. * @param bool $strict whether to remove relative directories
  59. */
  60. public static function clean_uri($uri, $strict = false)
  61. {
  62. $filters = \Config::get('security.uri_filter', array());
  63. $filters = is_array($filters) ? $filters : array($filters);
  64. $strict and $uri = preg_replace(array("/\.+\//", '/\/+/'), '/', $uri);
  65. return static::clean($uri, $filters);
  66. }
  67. /**
  68. * Cleans the global $_GET, $_POST and $_COOKIE arrays
  69. */
  70. public static function clean_input()
  71. {
  72. $_GET = static::clean($_GET);
  73. $_POST = static::clean($_POST);
  74. $_COOKIE = static::clean($_COOKIE);
  75. }
  76. /**
  77. * Generic variable clean method
  78. */
  79. public static function clean($var, $filters = null, $type = 'security.input_filter')
  80. {
  81. is_null($filters) and $filters = \Config::get($type, array());
  82. $filters = is_array($filters) ? $filters : array($filters);
  83. foreach ($filters as $filter)
  84. {
  85. // is this filter a callable local function?
  86. if (is_string($filter) and is_callable('static::'.$filter))
  87. {
  88. $var = static::$filter($var);
  89. }
  90. // is this filter a callable function?
  91. elseif (is_callable($filter))
  92. {
  93. if (is_array($var))
  94. {
  95. foreach($var as $key => $value)
  96. {
  97. $var[$key] = call_user_func($filter, $value);
  98. }
  99. }
  100. else
  101. {
  102. $var = call_user_func($filter, $var);
  103. }
  104. }
  105. // assume it's a regex of characters to filter
  106. else
  107. {
  108. if (is_array($var))
  109. {
  110. foreach($var as $key => $value)
  111. {
  112. $var[$key] = preg_replace('#['.$filter.']#ui', '', $value);
  113. }
  114. }
  115. else
  116. {
  117. $var = preg_replace('#['.$filter.']#ui', '', $var);
  118. }
  119. }
  120. }
  121. return $var;
  122. }
  123. public static function xss_clean($value)
  124. {
  125. if ( ! is_array($value))
  126. {
  127. if ( ! function_exists('htmLawed'))
  128. {
  129. import('htmlawed/htmlawed', 'vendor');
  130. }
  131. return htmLawed($value, array('safe' => 1, 'balanced' => 0));
  132. }
  133. foreach ($value as $k => $v)
  134. {
  135. $value[$k] = static::xss_clean($v);
  136. }
  137. return $value;
  138. }
  139. public static function strip_tags($value)
  140. {
  141. if ( ! is_array($value))
  142. {
  143. $value = filter_var($value, FILTER_SANITIZE_STRING);
  144. }
  145. else
  146. {
  147. foreach ($value as $k => $v)
  148. {
  149. $value[$k] = static::strip_tags($v);
  150. }
  151. }
  152. return $value;
  153. }
  154. public static function htmlentities($value, $flags = null, $encoding = null, $double_encode = null)
  155. {
  156. static $already_cleaned = array();
  157. is_null($flags) and $flags = \Config::get('security.htmlentities_flags', ENT_QUOTES);
  158. is_null($encoding) and $encoding = \Fuel::$encoding;
  159. is_null($double_encode) and $double_encode = \Config::get('security.htmlentities_double_encode', false);
  160. // Nothing to escape for non-string scalars, or for already processed values
  161. if (is_bool($value) or is_int($value) or is_float($value) or in_array($value, $already_cleaned, true))
  162. {
  163. return $value;
  164. }
  165. if (is_string($value))
  166. {
  167. $value = htmlentities($value, $flags, $encoding, $double_encode);
  168. }
  169. elseif (is_array($value) or ($value instanceof \Iterator and $value instanceof \ArrayAccess))
  170. {
  171. // Add to $already_cleaned variable when object
  172. is_object($value) and $already_cleaned[] = $value;
  173. foreach ($value as $k => $v)
  174. {
  175. $value[$k] = static::htmlentities($v, $flags, $encoding, $double_encode);
  176. }
  177. }
  178. elseif ($value instanceof \Iterator or get_class($value) == 'stdClass')
  179. {
  180. // Add to $already_cleaned variable
  181. $already_cleaned[] = $value;
  182. foreach ($value as $k => $v)
  183. {
  184. $value->{$k} = static::htmlentities($v, $flags, $encoding, $double_encode);
  185. }
  186. }
  187. elseif (is_object($value))
  188. {
  189. // Check if the object is whitelisted and return when that's the case
  190. foreach (\Config::get('security.whitelisted_classes', array()) as $class)
  191. {
  192. if (is_a($value, $class))
  193. {
  194. // Add to $already_cleaned variable
  195. $already_cleaned[] = $value;
  196. return $value;
  197. }
  198. }
  199. // Throw exception when it wasn't whitelisted and can't be converted to String
  200. if ( ! method_exists($value, '__toString'))
  201. {
  202. throw new \RuntimeException('Object class "'.get_class($value).'" could not be converted to string or '.
  203. 'sanitized as ArrayAccess. Whitelist it in security.whitelisted_classes in app/config/config.php '.
  204. 'to allow it to be passed unchecked.');
  205. }
  206. $value = static::htmlentities((string) $value, $flags, $encoding, $double_encode);
  207. }
  208. return $value;
  209. }
  210. /**
  211. * Check CSRF Token
  212. *
  213. * @param string CSRF token to be checked, checks post when empty
  214. * @return bool
  215. */
  216. public static function check_token($value = null)
  217. {
  218. $value = $value ?: \Input::post(static::$csrf_token_key, 'fail');
  219. // always reset token once it's been checked and still the same
  220. if (static::fetch_token() == static::$csrf_old_token and ! empty($value))
  221. {
  222. static::set_token(true);
  223. }
  224. return $value === static::$csrf_old_token;
  225. }
  226. /**
  227. * Fetch CSRF Token for the next request
  228. *
  229. * @return string
  230. */
  231. public static function fetch_token()
  232. {
  233. if (static::$csrf_token !== false)
  234. {
  235. return static::$csrf_token;
  236. }
  237. static::set_token();
  238. return static::$csrf_token;
  239. }
  240. protected static function set_token($reset = false)
  241. {
  242. // re-use old token when found (= not expired) and expiration is used (otherwise always reset)
  243. if ( ! $reset and static::$csrf_old_token and \Config::get('security.csrf_expiration', 0) > 0)
  244. {
  245. static::$csrf_token = static::$csrf_old_token;
  246. }
  247. // set new token for next session when necessary
  248. else
  249. {
  250. static::$csrf_token = md5(uniqid().time());
  251. $expiration = \Config::get('security.csrf_expiration', 0);
  252. \Cookie::set(static::$csrf_token_key, static::$csrf_token, $expiration);
  253. }
  254. }
  255. /**
  256. * JS fetch token
  257. *
  258. * Produces JavaScript fuel_csrf_token() function that will return the current
  259. * CSRF token when called. Use to fill right field on form submit for AJAX operations.
  260. *
  261. * @return string
  262. */
  263. public static function js_fetch_token()
  264. {
  265. $output = '<script type="text/javascript">
  266. function fuel_csrf_token()
  267. {
  268. if (document.cookie.length > 0)
  269. {
  270. var c_name = "'.static::$csrf_token_key.'";
  271. c_start = document.cookie.indexOf(c_name + "=");
  272. if (c_start != -1)
  273. {
  274. c_start = c_start + c_name.length + 1;
  275. c_end = document.cookie.indexOf(";" , c_start);
  276. if (c_end == -1)
  277. {
  278. c_end=document.cookie.length;
  279. }
  280. return unescape(document.cookie.substring(c_start, c_end));
  281. }
  282. }
  283. return "";
  284. }'.PHP_EOL;
  285. $output .= '</script>'.PHP_EOL;
  286. return $output;
  287. }
  288. /**
  289. * JS set token
  290. *
  291. * Produces JavaScript fuel_set_csrf_token() function that will update the current
  292. * CSRF token in the form when called, based on the value of the csrf cookie
  293. *
  294. * @return string
  295. */
  296. public static function js_set_token()
  297. {
  298. $output = '<script type="text/javascript">
  299. function fuel_set_csrf_token(form)
  300. {
  301. if (document.cookie.length > 0 && typeof form != undefined)
  302. {
  303. var c_name = "'.static::$csrf_token_key.'";
  304. c_start = document.cookie.indexOf(c_name + "=");
  305. if (c_start != -1)
  306. {
  307. c_start = c_start + c_name.length + 1;
  308. c_end = document.cookie.indexOf(";" , c_start);
  309. if (c_end == -1)
  310. {
  311. c_end=document.cookie.length;
  312. }
  313. value=unescape(document.cookie.substring(c_start, c_end));
  314. if (value != "")
  315. {
  316. for(i=0; i<form.elements.length; i++)
  317. {
  318. if (form.elements[i].name == c_name)
  319. {
  320. form.elements[i].value = value;
  321. break;
  322. }
  323. }
  324. }
  325. }
  326. }
  327. }'.PHP_EOL;
  328. $output .= '</script>'.PHP_EOL;
  329. return $output;
  330. }
  331. }