PageRenderTime 155ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/zendframework/zend-validator/src/Csrf.php

https://github.com/tmccormi/openemr
PHP | 378 lines | 243 code | 28 blank | 107 comment | 11 complexity | 98e8cec9f51da081e02ab59bca265da1 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Validator;
  10. use Traversable;
  11. use Zend\Math\Rand;
  12. use Zend\Session\Container as SessionContainer;
  13. use Zend\Stdlib\ArrayUtils;
  14. class Csrf extends AbstractValidator
  15. {
  16. /**
  17. * Error codes
  18. * @const string
  19. */
  20. const NOT_SAME = 'notSame';
  21. /**
  22. * Error messages
  23. * @var array
  24. */
  25. protected $messageTemplates = [
  26. self::NOT_SAME => "The form submitted did not originate from the expected site",
  27. ];
  28. /**
  29. * Actual hash used.
  30. *
  31. * @var mixed
  32. */
  33. protected $hash;
  34. /**
  35. * Static cache of the session names to generated hashes
  36. * @todo unused, left here to avoid BC breaks
  37. *
  38. * @var array
  39. */
  40. protected static $hashCache;
  41. /**
  42. * Name of CSRF element (used to create non-colliding hashes)
  43. *
  44. * @var string
  45. */
  46. protected $name = 'csrf';
  47. /**
  48. * Salt for CSRF token
  49. * @var string
  50. */
  51. protected $salt = 'salt';
  52. /**
  53. * @var SessionContainer
  54. */
  55. protected $session;
  56. /**
  57. * TTL for CSRF token
  58. * @var int|null
  59. */
  60. protected $timeout = 300;
  61. /**
  62. * Constructor
  63. *
  64. * @param array|Traversable $options
  65. */
  66. public function __construct($options = [])
  67. {
  68. parent::__construct($options);
  69. if ($options instanceof Traversable) {
  70. $options = ArrayUtils::iteratorToArray($options);
  71. }
  72. if (! is_array($options)) {
  73. $options = (array) $options;
  74. }
  75. foreach ($options as $key => $value) {
  76. switch (strtolower($key)) {
  77. case 'name':
  78. $this->setName($value);
  79. break;
  80. case 'salt':
  81. $this->setSalt($value);
  82. break;
  83. case 'session':
  84. $this->setSession($value);
  85. break;
  86. case 'timeout':
  87. $this->setTimeout($value);
  88. break;
  89. default:
  90. // ignore unknown options
  91. break;
  92. }
  93. }
  94. }
  95. /**
  96. * Does the provided token match the one generated?
  97. *
  98. * @param string $value
  99. * @param mixed $context
  100. * @return bool
  101. */
  102. public function isValid($value, $context = null)
  103. {
  104. if (! is_string($value)) {
  105. return false;
  106. }
  107. $this->setValue($value);
  108. $tokenId = $this->getTokenIdFromHash($value);
  109. $hash = $this->getValidationToken($tokenId);
  110. $tokenFromValue = $this->getTokenFromHash($value);
  111. $tokenFromHash = $this->getTokenFromHash($hash);
  112. if (! $tokenFromValue || ! $tokenFromHash || ($tokenFromValue !== $tokenFromHash)) {
  113. $this->error(self::NOT_SAME);
  114. return false;
  115. }
  116. return true;
  117. }
  118. /**
  119. * Set CSRF name
  120. *
  121. * @param string $name
  122. * @return Csrf
  123. */
  124. public function setName($name)
  125. {
  126. $this->name = (string) $name;
  127. return $this;
  128. }
  129. /**
  130. * Get CSRF name
  131. *
  132. * @return string
  133. */
  134. public function getName()
  135. {
  136. return $this->name;
  137. }
  138. /**
  139. * Set session container
  140. *
  141. * @param SessionContainer $session
  142. * @return Csrf
  143. */
  144. public function setSession(SessionContainer $session)
  145. {
  146. $this->session = $session;
  147. if ($this->hash) {
  148. $this->initCsrfToken();
  149. }
  150. return $this;
  151. }
  152. /**
  153. * Get session container
  154. *
  155. * Instantiate session container if none currently exists
  156. *
  157. * @return SessionContainer
  158. */
  159. public function getSession()
  160. {
  161. if (null === $this->session) {
  162. // Using fully qualified name, to ensure polyfill class alias is used
  163. $this->session = new SessionContainer($this->getSessionName());
  164. }
  165. return $this->session;
  166. }
  167. /**
  168. * Salt for CSRF token
  169. *
  170. * @param string $salt
  171. * @return Csrf
  172. */
  173. public function setSalt($salt)
  174. {
  175. $this->salt = (string) $salt;
  176. return $this;
  177. }
  178. /**
  179. * Retrieve salt for CSRF token
  180. *
  181. * @return string
  182. */
  183. public function getSalt()
  184. {
  185. return $this->salt;
  186. }
  187. /**
  188. * Retrieve CSRF token
  189. *
  190. * If no CSRF token currently exists, or should be regenerated,
  191. * generates one.
  192. *
  193. * @param bool $regenerate default false
  194. * @return string
  195. */
  196. public function getHash($regenerate = false)
  197. {
  198. if ((null === $this->hash) || $regenerate) {
  199. $this->generateHash();
  200. }
  201. return $this->hash;
  202. }
  203. /**
  204. * Get session namespace for CSRF token
  205. *
  206. * Generates a session namespace based on salt, element name, and class.
  207. *
  208. * @return string
  209. */
  210. public function getSessionName()
  211. {
  212. return str_replace('\\', '_', __CLASS__) . '_'
  213. . $this->getSalt() . '_'
  214. . strtr($this->getName(), ['[' => '_', ']' => '']);
  215. }
  216. /**
  217. * Set timeout for CSRF session token
  218. *
  219. * @param int|null $ttl
  220. * @return Csrf
  221. */
  222. public function setTimeout($ttl)
  223. {
  224. $this->timeout = ($ttl !== null) ? (int) $ttl : null;
  225. return $this;
  226. }
  227. /**
  228. * Get CSRF session token timeout
  229. *
  230. * @return int
  231. */
  232. public function getTimeout()
  233. {
  234. return $this->timeout;
  235. }
  236. /**
  237. * Initialize CSRF token in session
  238. *
  239. * @return void
  240. */
  241. protected function initCsrfToken()
  242. {
  243. $session = $this->getSession();
  244. $timeout = $this->getTimeout();
  245. if (null !== $timeout) {
  246. $session->setExpirationSeconds($timeout);
  247. }
  248. $hash = $this->getHash();
  249. $token = $this->getTokenFromHash($hash);
  250. $tokenId = $this->getTokenIdFromHash($hash);
  251. if (! $session->tokenList) {
  252. $session->tokenList = [];
  253. }
  254. $session->tokenList[$tokenId] = $token;
  255. $session->hash = $hash; // @todo remove this, left for BC
  256. }
  257. /**
  258. * Generate CSRF token
  259. *
  260. * Generates CSRF token and stores both in {@link $hash} and element
  261. * value.
  262. *
  263. * @return void
  264. */
  265. protected function generateHash()
  266. {
  267. $token = md5($this->getSalt() . Rand::getBytes(32) . $this->getName());
  268. $this->hash = $this->formatHash($token, $this->generateTokenId());
  269. $this->setValue($this->hash);
  270. $this->initCsrfToken();
  271. }
  272. /**
  273. * @return string
  274. */
  275. protected function generateTokenId()
  276. {
  277. return md5(Rand::getBytes(32));
  278. }
  279. /**
  280. * Get validation token
  281. *
  282. * Retrieve token from session, if it exists.
  283. *
  284. * @param string $tokenId
  285. * @return null|string
  286. */
  287. protected function getValidationToken($tokenId = null)
  288. {
  289. $session = $this->getSession();
  290. /**
  291. * if no tokenId is passed we revert to the old behaviour
  292. * @todo remove, here for BC
  293. */
  294. if (! $tokenId && isset($session->hash)) {
  295. return $session->hash;
  296. }
  297. if ($tokenId && isset($session->tokenList[$tokenId])) {
  298. return $this->formatHash($session->tokenList[$tokenId], $tokenId);
  299. }
  300. return;
  301. }
  302. /**
  303. * @param $token
  304. * @param $tokenId
  305. * @return string
  306. */
  307. protected function formatHash($token, $tokenId)
  308. {
  309. return sprintf('%s-%s', $token, $tokenId);
  310. }
  311. /**
  312. * @param $hash
  313. * @return string
  314. */
  315. protected function getTokenFromHash($hash)
  316. {
  317. $data = explode('-', $hash);
  318. return $data[0] ?: null;
  319. }
  320. /**
  321. * @param $hash
  322. * @return string
  323. */
  324. protected function getTokenIdFromHash($hash)
  325. {
  326. $data = explode('-', $hash);
  327. if (! isset($data[1])) {
  328. return;
  329. }
  330. return $data[1];
  331. }
  332. }