security.lib.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. /*
  3. ==============================================================================
  4. Dokeos - elearning and course management software
  5. Copyright (c) 2007 Dokeos S.A.
  6. For a full list of contributors, see "credits.txt".
  7. The full license can be read in "license.txt".
  8. This program is free software; you can redistribute it and/or
  9. modify it under the terms of the GNU General Public License
  10. as published by the Free Software Foundation; either version 2
  11. of the License, or (at your option) any later version.
  12. See the GNU General Public License for more details.
  13. ==============================================================================
  14. */
  15. /**
  16. ==============================================================================
  17. * This is the security library for Dokeos.
  18. *
  19. * This library is based on recommendations found in the PHP5 Certification
  20. * Guide published at PHP|Architect, and other recommendations found on
  21. * http://www.phpsec.org/
  22. * The principles here are that all data is tainted (most scripts of Dokeos are
  23. * open to the public or at least to a certain public that could be malicious
  24. * under specific circumstances). We use the white list approach, where as we
  25. * consider that data can only be used in the database or in a file if it has
  26. * been filtered.
  27. *
  28. * For session fixation, use ...
  29. * For session hijacking, use get_ua() and check_ua()
  30. * For Cross-Site Request Forgeries, use get_token() and check_tocken()
  31. * For basic filtering, use filter()
  32. * For files inclusions (using dynamic paths) use check_rel_path() and check_abs_path()
  33. *
  34. * @package dokeos.library
  35. ==============================================================================
  36. */
  37. /**
  38. * Security class
  39. *
  40. * Include/require it in your code and call Security::function()
  41. * to use its functionalities.
  42. *
  43. * This class can also be used as a container for filtered data, by creating
  44. * a new Security object and using $secure->filter($new_var,[more options])
  45. * and then using $secure->clean['var'] as a filtered equivalent, although
  46. * this is *not* mandatory at all.
  47. *
  48. * @author Yannick Warnier <yannick.warnier@dokeos.com>
  49. */
  50. class Security {
  51. public static $clean = array();
  52. /**
  53. * Checks if the absolute path (directory) given is really under the
  54. * checker path (directory)
  55. * @param string Absolute path to be checked (with trailing slash)
  56. * @param string Checker path under which the path should be (absolute path, with trailing slash, get it from api_get_path(SYS_COURSE_PATH))
  57. * @return bool True if the path is under the checker, false otherwise
  58. */
  59. public static function check_abs_path ($abs_path,$checker_path) {
  60. if (empty($checker_path)) {return false;} //checker path must be set
  61. $true_path=str_replace("\\", "/", realpath($abs_path));
  62. $found = strpos($true_path.'/',$checker_path);
  63. if ($found===0) {
  64. return true;
  65. }
  66. return false;
  67. }
  68. /**
  69. * Checks if the relative path (directory) given is really under the
  70. * checker path (directory)
  71. * @param string Relative path to be checked (relative to the current directory) (with trailing slash)
  72. * @param string Checker path under which the path should be (absolute path, with trailing slash, get it from api_get_path(SYS_COURSE_PATH))
  73. * @return bool True if the path is under the checker, false otherwise
  74. */
  75. public static function check_rel_path ($rel_path,$checker_path) {
  76. if (empty($checker_path)){return false;} //checker path must be set
  77. $current_path = getcwd(); //no trailing slash
  78. if (substr($rel_path,-1,1)!='/') {
  79. $rel_path = '/'.$rel_path;
  80. }
  81. $abs_path = $current_path.$rel_path;
  82. $true_path=str_replace("\\", "/", realpath($abs_path));
  83. $found = strpos($true_path.'/',$checker_path);
  84. if ($found===0) {
  85. return true;
  86. }
  87. return false;
  88. }
  89. /**
  90. * Filters dangerous filenames (*.php[.]?* and .htaccess) and returns it in
  91. * a non-executable form (for PHP and htaccess, this is still vulnerable to
  92. * other languages' files extensions)
  93. * @param string Unfiltered filename
  94. * @param string Filtered filename
  95. */
  96. public static function filter_filename ($filename) {
  97. require_once(api_get_path(LIBRARY_PATH).'fileUpload.lib.php');
  98. return disable_dangerous_file($filename);
  99. }
  100. /**
  101. * This function checks that the token generated in get_token() has been kept (prevents
  102. * Cross-Site Request Forgeries attacks)
  103. * @param string The array in which to get the token ('get' or 'post')
  104. * @return bool True if it's the right token, false otherwise
  105. */
  106. public static function check_token ($array='post') {
  107. switch ($array) {
  108. case 'get':
  109. if (isset($_SESSION['sec_token']) && isset($_GET['sec_token']) && $_SESSION['sec_token'] === $_GET['sec_token']) {
  110. return true;
  111. }
  112. return false;
  113. case 'post':
  114. if (isset($_SESSION['sec_token']) && isset($_POST['sec_token']) && $_SESSION['sec_token'] === $_POST['sec_token']) {
  115. return true;
  116. }
  117. return false;
  118. default:
  119. if (isset($_SESSION['sec_token']) && isset($array) && $_SESSION['sec_token'] === $array) {
  120. return true;
  121. }
  122. return false;
  123. }
  124. return false; //just in case, don't let anything slip
  125. }
  126. /**
  127. * Checks the user agent of the client as recorder by get_ua() to prevent
  128. * most session hijacking attacks.
  129. * @return bool True if the user agent is the same, false otherwise
  130. */
  131. public static function check_ua () {
  132. if (isset($_SESSION['sec_ua']) and $_SESSION['sec_ua'] === $_SERVER['HTTP_USER_AGENT'].$_SESSION['sec_ua_seed']) {
  133. return true;
  134. }
  135. return false;
  136. }
  137. /**
  138. * Clear the security token from the session
  139. * @return void
  140. */
  141. public static function clear_token () {
  142. $_SESSION['sec_token'] = null;
  143. unset($_SESSION['sec_token']);
  144. }
  145. /**
  146. * This function sets a random token to be included in a form as a hidden field
  147. * and saves it into the user's session. Returns an HTML form element
  148. * This later prevents Cross-Site Request Forgeries by checking that the user is really
  149. * the one that sent this form in knowingly (this form hasn't been generated from
  150. * another website visited by the user at the same time).
  151. * Check the token with check_token()
  152. * @return string Hidden-type input ready to insert into a form
  153. */
  154. public static function get_HTML_token () {
  155. $token = md5(uniqid(rand(),TRUE));
  156. $string = '<input type="hidden" name="sec_token" value="'.$token.'"/>';
  157. $_SESSION['sec_token'] = $token;
  158. return $string;
  159. }
  160. /**
  161. * This function sets a random token to be included in a form as a hidden field
  162. * and saves it into the user's session.
  163. * This later prevents Cross-Site Request Forgeries by checking that the user is really
  164. * the one that sent this form in knowingly (this form hasn't been generated from
  165. * another website visited by the user at the same time).
  166. * Check the token with check_token()
  167. * @return string Token
  168. */
  169. public static function get_token () {
  170. $token = md5(uniqid(rand(),TRUE));
  171. $_SESSION['sec_token'] = $token;
  172. return $token;
  173. }
  174. /**
  175. * Gets the user agent in the session to later check it with check_ua() to prevent
  176. * most cases of session hijacking.
  177. * @return void
  178. */
  179. public static function get_ua () {
  180. $_SESSION['sec_ua_seed'] = uniqid(rand(),TRUE);
  181. $_SESSION['sec_ua'] = $_SERVER['HTTP_USER_AGENT'].$_SESSION['sec_ua_seed'];
  182. }
  183. /**
  184. * This function filters a variable to the type given, with the options given
  185. * @param mixed The variable to be filtered
  186. * @param string The type of variable we expect (bool,int,float,string)
  187. * @param array Additional options
  188. * @return bool True if variable was filtered and added to the current object, false otherwise
  189. */
  190. public static function filter ($var,$type='string',$options=array()) {
  191. //This function is not finished! Do not use!
  192. $result = false;
  193. //get variable name and value
  194. $args = func_get_args();
  195. $names =array_keys($args);
  196. $name = $names[0];
  197. $value = $args[$name];
  198. switch ($type) {
  199. case 'bool':
  200. $result = (bool) $var;
  201. break;
  202. case 'int':
  203. $result = (int) $var;
  204. break;
  205. case 'float':
  206. $result = (float) $var;
  207. break;
  208. case 'string':
  209. break;
  210. case 'array':
  211. //an array variable shouldn't be given to the filter
  212. return false;
  213. default:
  214. return false;
  215. }
  216. if (!empty($option['save'])) {
  217. $this->clean[$name]=$result;
  218. }
  219. return $result;
  220. }
  221. /**
  222. * This function returns a variable from the clean array. If the variable doesn't exist,
  223. * it returns null
  224. * @param string Variable name
  225. * @return mixed Variable or NULL on error
  226. */
  227. public static function get ($varname) {
  228. if (isset(self::$clean[$varname])) {
  229. return self::$clean[$varname];
  230. }
  231. return NULL;
  232. }
  233. /**
  234. * This function tackles the XSS injections.
  235. * Filtering for XSS is very easily done by using the htmlentities() function.
  236. * This kind of filtering prevents JavaScript snippets to be understood as such.
  237. * @param mixed The variable to filter for XSS, this params can be a string or an array (example : array(x,y))
  238. * @param integer The user status,constant allowed(STUDENT,COURSEMANAGER,ANONYMOUS,COURSEMANAGERLOWSECURITY)
  239. * @return mixed Filtered string or array
  240. */
  241. public static function remove_XSS ($var,$user_status=ANONYMOUS) {
  242. global $charset;
  243. $purifier = new HTMLPurifier(null,$user_status);
  244. if (is_array($var)) {
  245. return $purifier->purifyArray($var);
  246. } else {
  247. return $purifier->purify($var);
  248. }
  249. }
  250. }