CRM-20926 - CRM_Core_Menu - Parse XML element <ids_arguments>
[civicrm-core.git] / CRM / Core / IDS.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
0f03f337 6 | Copyright CiviCRM LLC (c) 2004-2017 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
0f03f337 31 * @copyright CiviCRM LLC (c) 2004-2017
6a488035
TO
32 */
33class CRM_Core_IDS {
34
35 /**
fe482240 36 * Define the threshold for the ids reactions.
6a488035
TO
37 */
38 private $threshold = array(
39 'log' => 25,
40 'warn' => 50,
41 'kick' => 75,
42 );
43
44 /**
7c877abd 45 * @var string
6a488035 46 */
7c877abd 47 private $path;
6a488035
TO
48
49 /**
ea3ddccf 50 * Check function.
51 *
6a488035
TO
52 * This function includes the IDS vendor parts and runs the
53 * detection routines on the request array.
54 *
76adcecc 55 * @param array $route
6a488035 56 *
3bdca100 57 * @return bool
6a488035 58 */
76adcecc
TO
59 public function check($route) {
60 if (CRM_Core_Permission::check('skip IDS check')) {
61 return NULL;
62 }
63
6a488035 64 // lets bypass a few civicrm urls from this check
aa00da9b 65 $skip = array('civicrm/admin/setting/updateConfigBackend', 'civicrm/admin/messageTemplates');
66 CRM_Utils_Hook::idsException($skip);
76adcecc 67 $this->path = $route['path'];
7c877abd 68 if (in_array($this->path, $skip)) {
3bdca100 69 return NULL;
6a488035
TO
70 }
71
76adcecc
TO
72 $config = \CRM_Core_IDS::createStandardConfig();
73 foreach (array('json', 'html', 'exception') as $section) {
74 if (isset($route['ids_arguments'][$section])) {
75 foreach ($route['ids_arguments'][$section] as $v) {
76 $config['General'][$section][] = $v;
77 }
78 $config['General'][$section] = array_unique($config['General'][$section]);
79 }
80 }
81
82 $init = self::create($config);
83
ea3ddccf 84 // Add request url and user agent.
6a488035
TO
85 $_REQUEST['IDS_request_uri'] = $_SERVER['REQUEST_URI'];
86 if (isset($_SERVER['HTTP_USER_AGENT'])) {
87 $_REQUEST['IDS_user_agent'] = $_SERVER['HTTP_USER_AGENT'];
88 }
89
76adcecc
TO
90 require_once 'IDS/Monitor.php';
91 $ids = new \IDS_Monitor($_REQUEST, $init);
6a488035
TO
92
93 $result = $ids->run();
94 if (!$result->isEmpty()) {
95 $this->react($result);
96 }
97
98 return TRUE;
99 }
100
101 /**
76adcecc 102 * Create a new PHPIDS configuration object.
6a488035 103 *
76adcecc
TO
104 * @param array $config
105 * PHPIDS configuration array (per INI format).
106 * @return \IDS_Init
6a488035 107 */
76adcecc
TO
108 protected static function create($config) {
109 require_once 'IDS/Init.php';
110 $init = \IDS_Init::init(NULL);
111 $init->setConfig($config, TRUE);
6a488035 112
76adcecc
TO
113 // Cleanup
114 $reflection = new \ReflectionProperty('IDS_Init', 'instances');
115 $reflection->setAccessible(TRUE);
116 $value = $reflection->getValue(NULL);
117 unset($value[NULL]);
118 $reflection->setValue(NULL, $value);
6a488035 119
76adcecc 120 return $init;
6a488035
TO
121 }
122
b74c8634
TO
123 /**
124 * Create conservative, minimalist IDS configuration.
125 *
126 * @return array
127 */
128 public static function createBaseConfig() {
129 $config = \CRM_Core_Config::singleton();
130 $tmpDir = empty($config->uploadDir) ? CIVICRM_TEMPLATE_COMPILEDIR : $config->uploadDir;
131 global $civicrm_root;
132
133 return array(
134 'General' => array(
135 'filter_type' => 'xml',
136 'filter_path' => "{$civicrm_root}/packages/IDS/default_filter.xml",
137 'tmp_path' => $tmpDir,
138 'HTML_Purifier_Path' => 'IDS/vendors/htmlpurifier/HTMLPurifier.auto.php',
139 'HTML_Purifier_Cache' => $tmpDir,
140 'scan_keys' => '',
141 'exceptions' => array('__utmz', '__utmc'),
142 ),
143 );
144 }
145
146 /**
147 * Create the standard, general-purpose IDS configuration used by many pages.
148 *
149 * @return array
150 */
151 public static function createStandardConfig() {
152 $excs = array(
153 'widget_code',
154 'html_message',
155 'text_message',
156 'body_html',
157 'msg_html',
158 'msg_text',
159 'msg_subject',
160 'description',
161 'intro',
162 'thankyou_text',
163 'intro_text',
164 'body_text',
165 'footer_text',
166 'thankyou_text',
167 'tf_thankyou_text',
168 'thankyou_footer',
169 'thankyou_footer_text',
170 'new_text',
171 'renewal_text',
172 'help_pre',
173 'help_post',
174 'confirm_title',
175 'confirm_text',
176 'confirm_footer_text',
177 'confirm_email_text',
178 'report_header',
179 'report_footer',
180 'data',
181 'json',
182 'instructions',
183 'suggested_message',
184 'page_text',
185 'details',
186 );
187
188 $result = self::createBaseConfig();
189
190 $result['General']['exceptions'] = array_merge(
191 $result['General']['exceptions'],
192 $excs
193 );
194
195 return $result;
196 }
197
6a488035 198 /**
ea3ddccf 199 * This function reacts on the values in the incoming results array.
6a488035
TO
200 *
201 * Depending on the impact value certain actions are
202 * performed.
203 *
204 * @param IDS_Report $result
205 *
3bdca100 206 * @return bool
6a488035 207 */
76adcecc 208 public function react(IDS_Report $result) {
6a488035
TO
209
210 $impact = $result->getImpact();
211 if ($impact >= $this->threshold['kick']) {
212 $this->log($result, 3, $impact);
7c877abd 213 $this->kick();
6a488035
TO
214 return TRUE;
215 }
216 elseif ($impact >= $this->threshold['warn']) {
217 $this->log($result, 2, $impact);
218 $this->warn($result);
219 return TRUE;
220 }
221 elseif ($impact >= $this->threshold['log']) {
222 $this->log($result, 0, $impact);
223 return TRUE;
224 }
225 else {
226 return TRUE;
227 }
228 }
229
230 /**
ea3ddccf 231 * This function writes an entry about the intrusion to the database.
6a488035 232 *
c490a46a 233 * @param array $result
77b97be7
EM
234 * @param int $reaction
235 *
3bdca100 236 * @return bool
6a488035
TO
237 */
238 private function log($result, $reaction = 0) {
239 $ip = (isset($_SERVER['SERVER_ADDR']) &&
3bdca100 240 $_SERVER['SERVER_ADDR'] != '127.0.0.1') ? $_SERVER['SERVER_ADDR'] : (
241 isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '127.0.0.1'
006389de 242 );
6a488035
TO
243
244 $data = array();
245 $session = CRM_Core_Session::singleton();
246 foreach ($result as $event) {
247 $data[] = array(
248 'name' => $event->getName(),
249 'value' => stripslashes($event->getValue()),
250 'page' => $_SERVER['REQUEST_URI'],
251 'userid' => $session->get('userID'),
252 'session' => session_id() ? session_id() : '0',
253 'ip' => $ip,
254 'reaction' => $reaction,
255 'impact' => $result->getImpact(),
256 );
257 }
258
259 CRM_Core_Error::debug_var('IDS Detector Details', $data);
260 return TRUE;
261 }
262
263 /**
ea3ddccf 264 * Warn about IDS.
265 *
266 * @param array $result
267 *
268 * @return array
6a488035
TO
269 */
270 private function warn($result) {
271 return $result;
272 }
273
274 /**
7c877abd 275 * Create an error that prevents the user from continuing.
ea3ddccf 276 *
277 * @throws \Exception
6a488035 278 */
7c877abd 279 private function kick() {
6a488035
TO
280 $session = CRM_Core_Session::singleton();
281 $session->reset(2);
282
f3a27f08
DL
283 $msg = ts('There is a validation error with your HTML input. Your activity is a bit suspicious, hence aborting');
284
f3a27f08 285 if (in_array(
7c877abd 286 $this->path,
353ffa53
TO
287 array("civicrm/ajax/rest", "civicrm/api/json")
288 )) {
f3a27f08
DL
289 require_once "api/v3/utils.php";
290 $error = civicrm_api3_create_error(
291 $msg,
6a488035
TO
292 array(
293 'IP' => $_SERVER['REMOTE_ADDR'],
294 'error_code' => 'IDS_KICK',
295 'level' => 'security',
296 'referer' => $_SERVER['HTTP_REFERER'],
297 'reason' => 'XSS suspected',
298 )
299 );
ecdef330 300 CRM_Utils_JSON::output($error);
6a488035 301 }
f3a27f08 302 CRM_Core_Error::fatal($msg);
6a488035 303 }
96025800 304
6a488035 305}