Added feature #16437: Introduce a form protection API (Thanks to the Security Team...
[Packages/TYPO3.CMS.git] / t3lib / formprotection / class.t3lib_formprotection_backendformprotection.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2010 Oliver Klee <typo3-coding@oliverklee.de>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * Class t3lib_formprotection_BackendFormProtection.
27 *
28 * This class provides protection against cross-site request forgery (XSRF/CSRF)
29 * for forms in the BE.
30 *
31 * How to use:
32 *
33 * For each form in the BE (or link that changes some data), create a token and
34 * insert is as a hidden form element. The name of the form element does not
35 * matter; you only need it to get the form token for verifying it.
36 *
37 * <pre>
38 * $formToken = t3lib_formprotection_Factory::get(
39 * t3lib_formprotection_Factory::TYPE_BACK_END
40 * )->generateToken(
41 * 'BE user setup', 'edit'
42 * );
43 * $this->content .= '<input type="hidden" name="formToken" value="' .
44 * $formToken . '" />';
45 * </pre>
46 *
47 * The three parameters $formName, $action and $formInstanceName can be
48 * arbitrary strings, but they should make the form token as specific as
49 * possible. For different forms (e.g. BE user setup and editing a tt_content
50 * record) or different records (with different UIDs) from the same table,
51 * those values should be different.
52 *
53 * For editing a tt_content record, the call could look like this:
54 *
55 * <pre>
56 * $formToken = t3lib_formprotection_Factory::get(
57 * t3lib_formprotection_Factory::TYPE_BACK_END
58 * )->getFormProtection()->generateToken(
59 * 'tt_content', 'edit', $uid
60 * );
61 * </pre>
62 *
63 * At the end of the form, you need to persist the tokens. This makes sure that
64 * generated tokens get saved, and also that removed tokens stay removed:
65 *
66 * <pre>
67 * t3lib_formprotection_Factory::get(
68 * t3lib_formprotection_Factory::TYPE_BACK_END
69 * )->persistTokens();
70 * </pre>
71 *
72 * In BE lists, it might be necessary to generate hundreds of tokens. So the
73 * tokens do not get automatically persisted after creation for performance
74 * reasons.
75 *
76 *
77 * When processing the data that has been submitted by the form, you can check
78 * that the form token is valid like this:
79 *
80 * <pre>
81 * if ($dataHasBeenSubmitted && t3lib_formprotection_Factory::get(
82 * t3lib_formprotection_Factory::TYPE_BACK_END
83 * )->validateToken(
84 * (string) t3lib_div::_POST('formToken'),
85 * 'BE user setup', 'edit
86 * )
87 * ) {
88 * // processes the data
89 * } else {
90 * // no need to do anything here as the BE form protection will create a
91 * // flash message for an invalid token
92 * }
93 * </pre>
94 *
95 * Note that validateToken invalidates the token with the token ID. So calling
96 * validate with the same parameters two times in a row will always return FALSE
97 * for the second call.
98 *
99 * It is important that the tokens get validated <em>before</em> the tokens are
100 * persisted. This makes sure that the tokens that get invalidated by
101 * validateToken cannot be used again.
102 *
103 * $Id$
104 *
105 * @package TYPO3
106 * @subpackage t3lib
107 *
108 * @author Oliver Klee <typo3-coding@oliverklee.de>
109 */
110 class t3lib_formprotection_BackendFormProtection extends t3lib_formprotection_Abstract {
111 /**
112 * the maximum number of tokens that can exist at the same time
113 *
114 * @var integer
115 */
116 protected $maximumNumberOfTokens = 20000;
117
118 /**
119 * Only allow construction if we have a backend session
120 */
121 public function __construct() {
122 if (!isset($GLOBALS['BE_USER'])) {
123 throw new t3lib_error_Exception(
124 'A back-end form protection may only be instantiated if there' .
125 ' is an active back-end session.',
126 1285067843
127 );
128 }
129 parent::__construct();
130 }
131
132 /**
133 * Creates or displayes an error message telling the user that the submitted
134 * form token is invalid.
135 *
136 * @return void
137 */
138 protected function createValidationErrorMessage() {
139 $message = t3lib_div::makeInstance(
140 't3lib_FlashMessage',
141 $GLOBALS['LANG']->sL(
142 'LLL:EXT:lang/locallang_core.xml:error.formProtection.tokenInvalid'
143 ),
144 '',
145 t3lib_FlashMessage::ERROR
146 );
147 t3lib_FlashMessageQueue::addMessage($message);
148 }
149
150 /**
151 * Retrieves all saved tokens.
152 *
153 * @return array<array>
154 * the saved tokens as, will be empty if no tokens have been saved
155 */
156 protected function retrieveTokens() {
157 $tokens = $GLOBALS['BE_USER']->getSessionData('formTokens');
158 if (!is_array($tokens)) {
159 $tokens = array();
160 }
161
162 $this->tokens = $tokens;
163 }
164
165 /**
166 * Saves the tokens so that they can be used by a later incarnation of this
167 * class.
168 *
169 * @return void
170 */
171 public function persistTokens() {
172 $GLOBALS['BE_USER']->setAndSaveSessionData('formTokens', $this->tokens);
173 }
174 }
175
176 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_backendformprotection.php']) {
177 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/formprotection/class.t3lib_formprotection_backendformprotection.php']);
178 }
179 ?>