vendor/contao/core-bundle/src/Resources/contao/library/Contao/Input.php line 1027

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Contao.
  4. *
  5. * (c) Leo Feyer
  6. *
  7. * @license LGPL-3.0-or-later
  8. */
  9. namespace Contao;
  10. /**
  11. * Safely read the user input
  12. *
  13. * The class functions as an adapter for the global input arrays ($_GET, $_POST,
  14. * $_COOKIE) and safely returns their values. To prevent XSS vulnerabilities,
  15. * you should always use the class when reading user input.
  16. *
  17. * Usage:
  18. *
  19. * if (Input::get('action') == 'register')
  20. * {
  21. * $username = Input::post('username');
  22. * $password = Input::post('password');
  23. * }
  24. */
  25. class Input
  26. {
  27. /**
  28. * Object instance (Singleton)
  29. * @var Input
  30. */
  31. protected static $objInstance;
  32. /**
  33. * Cache
  34. * @var array
  35. */
  36. protected static $arrCache = array();
  37. /**
  38. * Unused $_GET parameters
  39. * @var array
  40. */
  41. protected static $arrUnusedGet = array();
  42. /**
  43. * Magic quotes setting
  44. * @var boolean
  45. */
  46. protected static $blnMagicQuotes = false;
  47. /**
  48. * Clean the global GPC arrays
  49. */
  50. public static function initialize()
  51. {
  52. $_GET = static::cleanKey($_GET);
  53. $_POST = static::cleanKey($_POST);
  54. $_COOKIE = static::cleanKey($_COOKIE);
  55. }
  56. /**
  57. * Return a $_GET variable
  58. *
  59. * @param string $strKey The variable name
  60. * @param boolean $blnDecodeEntities If true, all entities will be decoded
  61. * @param boolean $blnKeepUnused If true, the parameter will not be marked as used (see #4277)
  62. *
  63. * @return mixed The cleaned variable value
  64. */
  65. public static function get($strKey, $blnDecodeEntities=false, $blnKeepUnused=false)
  66. {
  67. if (!isset($_GET[$strKey]))
  68. {
  69. return null;
  70. }
  71. $strCacheKey = $blnDecodeEntities ? 'getDecoded' : 'getEncoded';
  72. if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  73. {
  74. $varValue = $_GET[$strKey];
  75. $varValue = static::decodeEntities($varValue);
  76. $varValue = static::xssClean($varValue, true);
  77. $varValue = static::stripTags($varValue);
  78. if (!$blnDecodeEntities)
  79. {
  80. $varValue = static::encodeSpecialChars($varValue);
  81. }
  82. $varValue = static::encodeInsertTags($varValue);
  83. static::$arrCache[$strCacheKey][$strKey] = $varValue;
  84. }
  85. // Mark the parameter as used (see #4277)
  86. if (!$blnKeepUnused)
  87. {
  88. unset(static::$arrUnusedGet[$strKey]);
  89. }
  90. return static::$arrCache[$strCacheKey][$strKey];
  91. }
  92. /**
  93. * Return a $_POST variable
  94. *
  95. * @param string $strKey The variable name
  96. * @param boolean $blnDecodeEntities If true, all entities will be decoded
  97. *
  98. * @return mixed The cleaned variable value
  99. */
  100. public static function post($strKey, $blnDecodeEntities=false)
  101. {
  102. $strCacheKey = $blnDecodeEntities ? 'postDecoded' : 'postEncoded';
  103. if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  104. {
  105. $varValue = static::findPost($strKey);
  106. if ($varValue === null)
  107. {
  108. return null;
  109. }
  110. $varValue = static::decodeEntities($varValue);
  111. $varValue = static::xssClean($varValue, true);
  112. $varValue = static::stripTags($varValue);
  113. if (!$blnDecodeEntities)
  114. {
  115. $varValue = static::encodeSpecialChars($varValue);
  116. }
  117. if (!\defined('TL_MODE') || TL_MODE != 'BE')
  118. {
  119. $varValue = static::encodeInsertTags($varValue);
  120. }
  121. static::$arrCache[$strCacheKey][$strKey] = $varValue;
  122. }
  123. return static::$arrCache[$strCacheKey][$strKey];
  124. }
  125. /**
  126. * Return a $_POST variable preserving allowed HTML tags
  127. *
  128. * @param string $strKey The variable name
  129. * @param boolean $blnDecodeEntities If true, all entities will be decoded
  130. *
  131. * @return mixed The cleaned variable value
  132. */
  133. public static function postHtml($strKey, $blnDecodeEntities=false)
  134. {
  135. $strCacheKey = $blnDecodeEntities ? 'postHtmlDecoded' : 'postHtmlEncoded';
  136. if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  137. {
  138. $varValue = static::findPost($strKey);
  139. if ($varValue === null)
  140. {
  141. return null;
  142. }
  143. $varValue = static::decodeEntities($varValue);
  144. $varValue = static::xssClean($varValue);
  145. $varValue = static::stripTags($varValue, Config::get('allowedTags'), Config::get('allowedAttributes'));
  146. if (!$blnDecodeEntities)
  147. {
  148. $varValue = static::encodeSpecialChars($varValue);
  149. }
  150. if (!\defined('TL_MODE') || TL_MODE != 'BE')
  151. {
  152. $varValue = static::encodeInsertTags($varValue);
  153. }
  154. static::$arrCache[$strCacheKey][$strKey] = $varValue;
  155. }
  156. return static::$arrCache[$strCacheKey][$strKey];
  157. }
  158. /**
  159. * Return a raw, unsafe $_POST variable
  160. *
  161. * @param string $strKey The variable name
  162. *
  163. * @return mixed The raw variable value
  164. */
  165. public static function postRaw($strKey)
  166. {
  167. $strCacheKey = 'postRaw';
  168. if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  169. {
  170. $varValue = static::findPost($strKey);
  171. if ($varValue === null)
  172. {
  173. return null;
  174. }
  175. $varValue = static::preserveBasicEntities($varValue);
  176. $varValue = static::xssClean($varValue);
  177. if (!\defined('TL_MODE') || TL_MODE != 'BE')
  178. {
  179. $varValue = static::encodeInsertTags($varValue);
  180. }
  181. static::$arrCache[$strCacheKey][$strKey] = $varValue;
  182. }
  183. return static::$arrCache[$strCacheKey][$strKey];
  184. }
  185. /**
  186. * Return a raw, unsafe and unfiltered $_POST variable
  187. *
  188. * @param string $strKey The variable name
  189. *
  190. * @return mixed The raw variable value
  191. */
  192. public static function postUnsafeRaw($strKey)
  193. {
  194. $strCacheKey = 'postUnsafeRaw';
  195. if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  196. {
  197. $varValue = static::findPost($strKey);
  198. if ($varValue === null)
  199. {
  200. return null;
  201. }
  202. static::$arrCache[$strCacheKey][$strKey] = $varValue;
  203. }
  204. return static::$arrCache[$strCacheKey][$strKey];
  205. }
  206. /**
  207. * Return a $_COOKIE variable
  208. *
  209. * @param string $strKey The variable name
  210. * @param boolean $blnDecodeEntities If true, all entities will be decoded
  211. *
  212. * @return mixed The cleaned variable value
  213. */
  214. public static function cookie($strKey, $blnDecodeEntities=false)
  215. {
  216. if (!isset($_COOKIE[$strKey]))
  217. {
  218. return null;
  219. }
  220. $strCacheKey = $blnDecodeEntities ? 'cookieDecoded' : 'cookieEncoded';
  221. if (!isset(static::$arrCache[$strCacheKey][$strKey]))
  222. {
  223. $varValue = $_COOKIE[$strKey];
  224. $varValue = static::decodeEntities($varValue);
  225. $varValue = static::xssClean($varValue, true);
  226. $varValue = static::stripTags($varValue);
  227. if (!$blnDecodeEntities)
  228. {
  229. $varValue = static::encodeSpecialChars($varValue);
  230. }
  231. $varValue = static::encodeInsertTags($varValue);
  232. static::$arrCache[$strCacheKey][$strKey] = $varValue;
  233. }
  234. return static::$arrCache[$strCacheKey][$strKey];
  235. }
  236. /**
  237. * Set a $_GET variable
  238. *
  239. * @param string $strKey The variable name
  240. * @param mixed $varValue The variable value
  241. * @param boolean $blnAddUnused If true, the value usage will be checked
  242. */
  243. public static function setGet($strKey, $varValue, $blnAddUnused=false)
  244. {
  245. // Convert special characters (see #7829)
  246. $strKey = str_replace(array(' ', '.', '['), '_', $strKey);
  247. $strKey = static::cleanKey($strKey);
  248. unset(static::$arrCache['getEncoded'][$strKey], static::$arrCache['getDecoded'][$strKey]);
  249. if ($varValue === null)
  250. {
  251. unset($_GET[$strKey]);
  252. }
  253. else
  254. {
  255. $_GET[$strKey] = $varValue;
  256. if ($blnAddUnused)
  257. {
  258. static::setUnusedGet($strKey, $varValue); // see #4277
  259. }
  260. }
  261. }
  262. /**
  263. * Set a $_POST variable
  264. *
  265. * @param string $strKey The variable name
  266. * @param mixed $varValue The variable value
  267. */
  268. public static function setPost($strKey, $varValue)
  269. {
  270. $strKey = static::cleanKey($strKey);
  271. unset(
  272. static::$arrCache['postEncoded'][$strKey],
  273. static::$arrCache['postDecoded'][$strKey],
  274. static::$arrCache['postHtmlEncoded'][$strKey],
  275. static::$arrCache['postHtmlDecoded'][$strKey],
  276. static::$arrCache['postRaw'][$strKey],
  277. static::$arrCache['postUnsafeRaw'][$strKey]
  278. );
  279. if ($varValue === null)
  280. {
  281. unset($_POST[$strKey]);
  282. }
  283. else
  284. {
  285. $_POST[$strKey] = $varValue;
  286. }
  287. }
  288. /**
  289. * Set a $_COOKIE variable
  290. *
  291. * @param string $strKey The variable name
  292. * @param mixed $varValue The variable value
  293. */
  294. public static function setCookie($strKey, $varValue)
  295. {
  296. $strKey = static::cleanKey($strKey);
  297. unset(static::$arrCache['cookieEncoded'][$strKey], static::$arrCache['cookieDecoded'][$strKey]);
  298. if ($varValue === null)
  299. {
  300. unset($_COOKIE[$strKey]);
  301. }
  302. else
  303. {
  304. $_COOKIE[$strKey] = $varValue;
  305. }
  306. }
  307. /**
  308. * Reset the internal cache
  309. */
  310. public static function resetCache()
  311. {
  312. static::$arrCache = array();
  313. }
  314. /**
  315. * Return whether there are unused GET parameters
  316. *
  317. * @return boolean True if there are unused GET parameters
  318. */
  319. public static function hasUnusedGet()
  320. {
  321. return \count(static::$arrUnusedGet) > 0;
  322. }
  323. /**
  324. * Return the unused GET parameters as array
  325. *
  326. * @return array The unused GET parameter array
  327. */
  328. public static function getUnusedGet()
  329. {
  330. return array_keys(static::$arrUnusedGet);
  331. }
  332. /**
  333. * Set an unused GET parameter
  334. *
  335. * @param string $strKey The array key
  336. * @param mixed $varValue The array value
  337. */
  338. public static function setUnusedGet($strKey, $varValue)
  339. {
  340. static::$arrUnusedGet[$strKey] = $varValue;
  341. }
  342. /**
  343. * Reset the unused GET parameters
  344. */
  345. public static function resetUnusedGet()
  346. {
  347. static::$arrUnusedGet = array();
  348. }
  349. /**
  350. * Sanitize the variable names (thanks to Andreas Schempp)
  351. *
  352. * @param mixed $varValue A variable name or an array of variable names
  353. *
  354. * @return mixed The clean name or array of names
  355. */
  356. public static function cleanKey($varValue)
  357. {
  358. // Recursively clean arrays
  359. if (\is_array($varValue))
  360. {
  361. $return = array();
  362. foreach ($varValue as $k=>$v)
  363. {
  364. $k = static::cleanKey($k);
  365. if (\is_array($v))
  366. {
  367. $v = static::cleanKey($v);
  368. }
  369. $return[$k] = $v;
  370. }
  371. return $return;
  372. }
  373. $varValue = static::decodeEntities($varValue);
  374. $varValue = static::xssClean($varValue, true);
  375. $varValue = static::stripTags($varValue);
  376. return $varValue;
  377. }
  378. /**
  379. * Strip slashes
  380. *
  381. * @param mixed $varValue A string or array
  382. *
  383. * @return mixed The string or array without slashes
  384. *
  385. * @deprecated Deprecated since Contao 3.5, to be removed in Contao 5.
  386. * Since get_magic_quotes_gpc() always returns false in PHP 5.4+, the method was never actually executed.
  387. */
  388. public static function stripSlashes($varValue)
  389. {
  390. return $varValue;
  391. }
  392. /**
  393. * Strip HTML and PHP tags preserving HTML comments
  394. *
  395. * @param mixed $varValue A string or array
  396. * @param string $strAllowedTags A string of tags to preserve
  397. * @param string $strAllowedAttributes A serialized string of attributes to preserve
  398. *
  399. * @return mixed The cleaned string or array
  400. */
  401. public static function stripTags($varValue, $strAllowedTags='', $strAllowedAttributes='')
  402. {
  403. if ($strAllowedTags !== '' && \func_num_args() < 3)
  404. {
  405. trigger_deprecation('contao/core-bundle', '4.4', 'Using %s() with $strAllowedTags but without $strAllowedAttributes has been deprecated and will no longer work in Contao 5.0.', __METHOD__);
  406. $strAllowedAttributes = Config::get('allowedAttributes');
  407. }
  408. if (!$varValue)
  409. {
  410. return $varValue;
  411. }
  412. // Recursively clean arrays
  413. if (\is_array($varValue))
  414. {
  415. foreach ($varValue as $k=>$v)
  416. {
  417. $varValue[$k] = static::stripTags($v, $strAllowedTags, $strAllowedAttributes);
  418. }
  419. return $varValue;
  420. }
  421. $arrAllowedAttributes = array();
  422. foreach (StringUtil::deserialize($strAllowedAttributes, true) as $arrRow)
  423. {
  424. if (!empty($arrRow['key']) && !empty($arrRow['value']))
  425. {
  426. $arrAllowedAttributes[trim($arrRow['key'])] = StringUtil::trimsplit(',', $arrRow['value']);
  427. }
  428. }
  429. // Encode opening arrow brackets (see #3998)
  430. $varValue = preg_replace_callback(
  431. '@</?([^\s<>/]*)@',
  432. static function ($matches) use ($strAllowedTags)
  433. {
  434. if (!$matches[1] || stripos($strAllowedTags, '<' . strtolower($matches[1]) . '>') === false)
  435. {
  436. $matches[0] = str_replace('<', '&lt;', $matches[0]);
  437. }
  438. return $matches[0];
  439. },
  440. $varValue
  441. );
  442. // Strip the tags
  443. $varValue = strip_tags($varValue, $strAllowedTags);
  444. // Restore HTML comments and recheck for encoded null bytes
  445. $varValue = str_replace(array('&lt;!--', '&lt;![', '\\0'), array('<!--', '<![', '&#92;0'), $varValue);
  446. // Strip attributes
  447. if ($strAllowedTags)
  448. {
  449. $varValue = self::stripAttributes($varValue, $strAllowedTags, $arrAllowedAttributes);
  450. }
  451. return $varValue;
  452. }
  453. /**
  454. * Strip HTML attributes and normalize them to lowercase and double quotes
  455. *
  456. * @param string $strHtml
  457. * @param string $strAllowedTags
  458. * @param array $arrAllowedAttributes
  459. *
  460. * @return string
  461. */
  462. private static function stripAttributes($strHtml, $strAllowedTags, $arrAllowedAttributes)
  463. {
  464. // Skip if all attributes are allowed on all tags
  465. if (\in_array('*', $arrAllowedAttributes['*'] ?? array(), true))
  466. {
  467. return $strHtml;
  468. }
  469. $blnCommentOpen = false;
  470. $strOpenRawtext = null;
  471. // Match every single starting and closing tag or special characters outside of tags
  472. return preg_replace_callback(
  473. '@</?([^\s<>/]*)([^<>]*)>?|-->|[>"\'=]+@',
  474. static function ($matches) use ($strAllowedTags, $arrAllowedAttributes, &$blnCommentOpen, &$strOpenRawtext)
  475. {
  476. $strTagName = strtolower($matches[1] ?? '');
  477. if ($strOpenRawtext === $strTagName && '/' === $matches[0][1])
  478. {
  479. $strOpenRawtext = null;
  480. return '</' . $strTagName . '>';
  481. }
  482. if (null !== $strOpenRawtext)
  483. {
  484. return $matches[0];
  485. }
  486. if ($blnCommentOpen && substr($matches[0], -3) === '-->')
  487. {
  488. $blnCommentOpen = false;
  489. return static::encodeSpecialChars(substr($matches[0], 0, -3)) . '-->';
  490. }
  491. if (!$blnCommentOpen && 0 === strncmp($matches[0], '<!--', 4))
  492. {
  493. if (substr($matches[0], -3) === '-->')
  494. {
  495. return '<!--' . static::encodeSpecialChars(substr($matches[0], 4, -3)) . '-->';
  496. }
  497. $blnCommentOpen = true;
  498. return '<!--' . static::encodeSpecialChars(substr($matches[0], 4));
  499. }
  500. // Matched special characters or tag is invalid or not allowed, return everything encoded
  501. if ($strTagName == '' || stripos($strAllowedTags, '<' . $strTagName . '>') === false)
  502. {
  503. return static::encodeSpecialChars($matches[0]);
  504. }
  505. // Closing tags have no attributes
  506. if ('/' === substr($matches[0], 1, 1))
  507. {
  508. return '</' . $strTagName . '>';
  509. }
  510. // Special parsing for RCDATA and RAWTEXT elements https://html.spec.whatwg.org/#rcdata-state
  511. if (!$blnCommentOpen && \in_array($strTagName, array('script', 'title', 'textarea', 'style', 'xmp', 'iframe', 'noembed', 'noframes', 'noscript')))
  512. {
  513. $strOpenRawtext = $strTagName;
  514. }
  515. $arrAttributes = self::getAttributesFromTag($matches[2]);
  516. // Only keep allowed attributes
  517. $arrAttributes = array_filter(
  518. $arrAttributes,
  519. static function ($strAttribute) use ($strTagName, $arrAllowedAttributes)
  520. {
  521. // Skip if all attributes are allowed
  522. if (\in_array('*', $arrAllowedAttributes[$strTagName] ?? array(), true))
  523. {
  524. return true;
  525. }
  526. $arrCandidates = array($strAttribute);
  527. // Check for wildcard attributes like data-*
  528. if (false !== $intDashPosition = strpos($strAttribute, '-'))
  529. {
  530. $arrCandidates[] = substr($strAttribute, 0, $intDashPosition + 1) . '*';
  531. }
  532. foreach ($arrCandidates as $strCandidate)
  533. {
  534. if (
  535. \in_array($strCandidate, $arrAllowedAttributes['*'] ?? array(), true)
  536. || \in_array($strCandidate, $arrAllowedAttributes[$strTagName] ?? array(), true)
  537. ) {
  538. return true;
  539. }
  540. }
  541. return false;
  542. },
  543. ARRAY_FILTER_USE_KEY
  544. );
  545. // Build the tag in its normalized form
  546. $strReturn = '<' . $strTagName;
  547. foreach ($arrAttributes as $strAttributeName => $strAttributeValue)
  548. {
  549. // The value was already encoded by the getAttributesFromTag() method
  550. $strReturn .= ' ' . $strAttributeName . '="' . $strAttributeValue . '"';
  551. }
  552. $strReturn .= '>';
  553. return $strReturn;
  554. },
  555. $strHtml
  556. );
  557. }
  558. /**
  559. * Get the attributes as key/value pairs with the values already encoded for HTML
  560. *
  561. * @param string $strAttributes
  562. *
  563. * @return array
  564. */
  565. private static function getAttributesFromTag($strAttributes)
  566. {
  567. // Match every attribute name value pair
  568. if (!preg_match_all('@\s+([a-z][a-z0-9_:-]*)(?:\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]*))?@i', $strAttributes, $matches, PREG_SET_ORDER))
  569. {
  570. return array();
  571. }
  572. $arrAttributes = array();
  573. foreach ($matches as $arrMatch)
  574. {
  575. $strAttribute = strtolower($arrMatch[1]);
  576. // Skip attributes that end with dashes or use a double dash
  577. if (substr($strAttribute, -1) === '-' || false !== strpos($strAttribute, '--'))
  578. {
  579. continue;
  580. }
  581. // Default to empty string for the value
  582. $strValue = $arrMatch[2] ?? '';
  583. // Remove the quotes if matched by the regular expression
  584. if (
  585. (strpos($strValue, '"') === 0 && substr($strValue, -1) === '"')
  586. || (strpos($strValue, "'") === 0 && substr($strValue, -1) === "'")
  587. ) {
  588. $strValue = substr($strValue, 1, -1);
  589. }
  590. // Encode all special characters and insert tags that are not encoded yet
  591. if (1 === preg_match('((?:^|:)(?:src|srcset|href|action|formaction|codebase|cite|background|longdesc|profile|usemap|classid|data|icon|manifest|poster|archive)$)', $strAttribute))
  592. {
  593. $strValue = StringUtil::specialcharsUrl($strValue);
  594. }
  595. else
  596. {
  597. $strValue = StringUtil::specialcharsAttribute($strValue);
  598. }
  599. $arrAttributes[$strAttribute] = $strValue;
  600. }
  601. return $arrAttributes;
  602. }
  603. /**
  604. * Clean a value and try to prevent XSS attacks
  605. *
  606. * @param mixed $varValue A string or array
  607. * @param boolean $blnStrictMode If true, the function removes also JavaScript event handlers
  608. *
  609. * @return mixed The cleaned string or array
  610. */
  611. public static function xssClean($varValue, $blnStrictMode=false)
  612. {
  613. if (!$varValue)
  614. {
  615. return $varValue;
  616. }
  617. // Recursively clean arrays
  618. if (\is_array($varValue))
  619. {
  620. foreach ($varValue as $k=>$v)
  621. {
  622. $varValue[$k] = static::xssClean($v);
  623. }
  624. return $varValue;
  625. }
  626. // Return if the value is not a string
  627. if (\is_bool($varValue) || is_numeric($varValue))
  628. {
  629. return $varValue;
  630. }
  631. // Validate standard character entities and UTF16 two byte encoding
  632. $varValue = preg_replace('/(&#*\w+)[\x00-\x20]+;/i', '$1;', $varValue);
  633. // Remove carriage returns
  634. $varValue = preg_replace('/\r+/', '', $varValue);
  635. // Replace unicode entities
  636. $varValue = preg_replace_callback('~&#x([0-9a-f]+);~i', static function ($matches) { return mb_chr(hexdec($matches[1])); }, $varValue);
  637. $varValue = preg_replace_callback('~&#([0-9]+);~', static function ($matches) { return mb_chr($matches[1]); }, $varValue);
  638. // Remove null bytes
  639. $varValue = str_replace(array(\chr(0), '\\0'), array('', '&#92;0'), $varValue);
  640. // Define a list of keywords
  641. $arrKeywords = array
  642. (
  643. '/\bj\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\b/is', // javascript
  644. '/\bv\s*b\s*s\s*c\s*r\s*i\s*p\s*t\b/is', // vbscript
  645. '/\bv\s*b\s*s\s*c\s*r\s*p\s*t\b/is', // vbscrpt
  646. '/\bs\s*c\s*r\s*i\s*p\s*t\b/is', //script
  647. '/\ba\s*p\s*p\s*l\s*e\s*t\b/is', // applet
  648. '/\ba\s*l\s*e\s*r\s*t\b/is', // alert
  649. '/\bd\s*o\s*c\s*u\s*m\s*e\s*n\s*t\b/is', // document
  650. '/\bw\s*r\s*i\s*t\s*e\b/is', // write
  651. '/\bc\s*o\s*o\s*k\s*i\s*e\b/is', // cookie
  652. '/\bw\s*i\s*n\s*d\s*o\s*w\b/is' // window
  653. );
  654. // Compact exploded keywords like "j a v a s c r i p t"
  655. foreach ($arrKeywords as $strKeyword)
  656. {
  657. $arrMatches = array();
  658. preg_match_all($strKeyword, $varValue, $arrMatches);
  659. foreach ($arrMatches[0] as $strMatch)
  660. {
  661. $varValue = str_replace($strMatch, preg_replace('/\s*/', '', $strMatch), $varValue);
  662. }
  663. }
  664. $arrRegexp[] = '/<(a|img)[^>]*[^a-z](<script|<xss)[^>]*>/is';
  665. $arrRegexp[] = '/<(a|img)[^>]*[^a-z]document\.cookie[^>]*>/is';
  666. $arrRegexp[] = '/<(a|img)[^>]*[^a-z]vbscri?pt\s*:[^>]*>/is';
  667. $arrRegexp[] = '/<(a|img)[^>]*[^a-z]expression\s*\([^>]*>/is';
  668. // Also remove event handlers and JavaScript in strict mode
  669. if ($blnStrictMode)
  670. {
  671. $arrRegexp[] = '/vbscri?pt\s*:/is';
  672. $arrRegexp[] = '/javascript\s*:/is';
  673. $arrRegexp[] = '/<\s*embed.*swf/is';
  674. $arrRegexp[] = '/<(a|img)[^>]*[^a-z]alert\s*\([^>]*>/is';
  675. $arrRegexp[] = '/<(a|img)[^>]*[^a-z]javascript\s*:[^>]*>/is';
  676. $arrRegexp[] = '/<(a|img)[^>]*[^a-z]window\.[^>]*>/is';
  677. $arrRegexp[] = '/<(a|img)[^>]*[^a-z]document\.[^>]*>/is';
  678. $arrRegexp[] = '/<[^>]*[^a-z]onabort\s*=[^>]*>/is';
  679. $arrRegexp[] = '/<[^>]*[^a-z]onblur\s*=[^>]*>/is';
  680. $arrRegexp[] = '/<[^>]*[^a-z]onchange\s*=[^>]*>/is';
  681. $arrRegexp[] = '/<[^>]*[^a-z]onclick\s*=[^>]*>/is';
  682. $arrRegexp[] = '/<[^>]*[^a-z]onerror\s*=[^>]*>/is';
  683. $arrRegexp[] = '/<[^>]*[^a-z]onfocus\s*=[^>]*>/is';
  684. $arrRegexp[] = '/<[^>]*[^a-z]onkeypress\s*=[^>]*>/is';
  685. $arrRegexp[] = '/<[^>]*[^a-z]onkeydown\s*=[^>]*>/is';
  686. $arrRegexp[] = '/<[^>]*[^a-z]onkeyup\s*=[^>]*>/is';
  687. $arrRegexp[] = '/<[^>]*[^a-z]onload\s*=[^>]*>/is';
  688. $arrRegexp[] = '/<[^>]*[^a-z]onmouseover\s*=[^>]*>/is';
  689. $arrRegexp[] = '/<[^>]*[^a-z]onmouseup\s*=[^>]*>/is';
  690. $arrRegexp[] = '/<[^>]*[^a-z]onmousedown\s*=[^>]*>/is';
  691. $arrRegexp[] = '/<[^>]*[^a-z]onmouseout\s*=[^>]*>/is';
  692. $arrRegexp[] = '/<[^>]*[^a-z]onreset\s*=[^>]*>/is';
  693. $arrRegexp[] = '/<[^>]*[^a-z]onselect\s*=[^>]*>/is';
  694. $arrRegexp[] = '/<[^>]*[^a-z]onsubmit\s*=[^>]*>/is';
  695. $arrRegexp[] = '/<[^>]*[^a-z]onunload\s*=[^>]*>/is';
  696. $arrRegexp[] = '/<[^>]*[^a-z]onresize\s*=[^>]*>/is';
  697. }
  698. $varValue = preg_replace($arrRegexp, '', $varValue);
  699. // Recheck for encoded null bytes
  700. $varValue = str_replace('\\0', '&#92;0', $varValue);
  701. return $varValue;
  702. }
  703. /**
  704. * Decode HTML entities
  705. *
  706. * @param mixed $varValue A string or array
  707. *
  708. * @return mixed The decoded string or array
  709. */
  710. public static function decodeEntities($varValue)
  711. {
  712. if (!$varValue)
  713. {
  714. return $varValue;
  715. }
  716. // Recursively clean arrays
  717. if (\is_array($varValue))
  718. {
  719. foreach ($varValue as $k=>$v)
  720. {
  721. $varValue[$k] = static::decodeEntities($v);
  722. }
  723. return $varValue;
  724. }
  725. // Preserve basic entities
  726. $varValue = static::preserveBasicEntities($varValue);
  727. $varValue = html_entity_decode($varValue, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, System::getContainer()->getParameter('kernel.charset'));
  728. return $varValue;
  729. }
  730. /**
  731. * Preserve basic entities by replacing them with square brackets (e.g. &amp; becomes [amp])
  732. *
  733. * @param mixed $varValue A string or array
  734. *
  735. * @return mixed The string or array with the converted entities
  736. */
  737. public static function preserveBasicEntities($varValue)
  738. {
  739. if (!$varValue)
  740. {
  741. return $varValue;
  742. }
  743. // Recursively clean arrays
  744. if (\is_array($varValue))
  745. {
  746. foreach ($varValue as $k=>$v)
  747. {
  748. $varValue[$k] = static::preserveBasicEntities($v);
  749. }
  750. return $varValue;
  751. }
  752. $varValue = str_replace
  753. (
  754. array('[&amp;]', '&amp;', '[&lt;]', '&lt;', '[&gt;]', '&gt;', '[&nbsp;]', '&nbsp;', '[&shy;]', '&shy;'),
  755. array('[&]', '[&]', '[lt]', '[lt]', '[gt]', '[gt]', '[nbsp]', '[nbsp]', '[-]', '[-]'),
  756. $varValue
  757. );
  758. return $varValue;
  759. }
  760. /**
  761. * Encode special characters which are potentially dangerous
  762. *
  763. * @param mixed $varValue A string or array
  764. *
  765. * @return mixed The encoded string or array
  766. */
  767. public static function encodeSpecialChars($varValue)
  768. {
  769. if (!$varValue)
  770. {
  771. return $varValue;
  772. }
  773. // Recursively clean arrays
  774. if (\is_array($varValue))
  775. {
  776. foreach ($varValue as $k=>$v)
  777. {
  778. $varValue[$k] = static::encodeSpecialChars($v);
  779. }
  780. return $varValue;
  781. }
  782. $arrSearch = array(
  783. '#', '<', '>', '(', ')', '\\', '=', '"', "'",
  784. // Revert double encoded #
  785. '&&#35;35;', '&&#35;60;', '&&#35;62;', '&&#35;40;', '&&#35;41;', '&&#35;92;', '&&#35;61;', '&&#35;34;', '&&#35;39;',
  786. );
  787. $arrReplace = array(
  788. '&#35;', '&#60;', '&#62;', '&#40;', '&#41;', '&#92;', '&#61;', '&#34;', '&#39;',
  789. '&#35;', '&#60;', '&#62;', '&#40;', '&#41;', '&#92;', '&#61;', '&#34;', '&#39;',
  790. );
  791. return str_replace($arrSearch, $arrReplace, $varValue);
  792. }
  793. /**
  794. * Encode the opening and closing delimiters of insert tags
  795. *
  796. * @param string|array $varValue The input string
  797. *
  798. * @return string|array The encoded input string
  799. */
  800. public static function encodeInsertTags($varValue)
  801. {
  802. // Recursively encode insert tags
  803. if (\is_array($varValue))
  804. {
  805. foreach ($varValue as $k=>$v)
  806. {
  807. $varValue[$k] = static::encodeInsertTags($v);
  808. }
  809. return $varValue;
  810. }
  811. $varValue = str_replace(array('{{', '}}'), array('&#123;&#123;', '&#125;&#125;'), (string) $varValue);
  812. // Encode single curly braces at the beginning and end of the string
  813. return preg_replace(array('/^(\s*)\{|\{(\s*)$/', '/^(\s*)\}|\}(\s*)$/'), array('$1&#123;$2', '$1&#125;$2'), $varValue);
  814. }
  815. /**
  816. * Fallback to the session form data if there is no post data
  817. *
  818. * @param string $strKey The variable name
  819. *
  820. * @return mixed The variable value
  821. */
  822. public static function findPost($strKey)
  823. {
  824. if (isset($_POST[$strKey]))
  825. {
  826. return $_POST[$strKey];
  827. }
  828. // Do not check for $request->hasPreviousSession() and early return here (see #3971)
  829. if (isset($_SESSION['FORM_DATA'][$strKey]))
  830. {
  831. return ($strKey == 'FORM_SUBMIT') ? preg_replace('/^auto_/i', '', $_SESSION['FORM_DATA'][$strKey]) : $_SESSION['FORM_DATA'][$strKey];
  832. }
  833. return null;
  834. }
  835. /**
  836. * Clean the keys of the request arrays
  837. *
  838. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  839. * The Input class is now static.
  840. */
  841. protected function __construct()
  842. {
  843. static::initialize();
  844. }
  845. /**
  846. * Prevent cloning of the object (Singleton)
  847. *
  848. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  849. * The Input class is now static.
  850. */
  851. final public function __clone()
  852. {
  853. }
  854. /**
  855. * Return the object instance (Singleton)
  856. *
  857. * @return Input The object instance
  858. *
  859. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  860. * The Input class is now static.
  861. */
  862. public static function getInstance()
  863. {
  864. trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Input::getInstance()" has been deprecated and will no longer work in Contao 5.0. The "Contao\Input" class is now static.');
  865. if (static::$objInstance === null)
  866. {
  867. static::$objInstance = new static();
  868. }
  869. return static::$objInstance;
  870. }
  871. }
  872. class_alias(Input::class, 'Input');