Merge pull request #7962 from eileenmcnaughton/CRM-18213
[civicrm-core.git] / CRM / Core / IDS.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
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
e7112fa7 31 * @copyright CiviCRM LLC (c) 2004-2015
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 /**
100fef9d 45 * The init object
6a488035
TO
46 */
47 private $init = NULL;
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 *
3bdca100 55 * @param object $args cake controller object
6a488035 56 *
3bdca100 57 * @return bool
6a488035
TO
58 */
59 public function check(&$args) {
60 // lets bypass a few civicrm urls from this check
61 static $skip = array('civicrm/admin/setting/updateConfigBackend', 'civicrm/admin/messageTemplates');
62 $path = implode('/', $args);
63 if (in_array($path, $skip)) {
3bdca100 64 return NULL;
6a488035
TO
65 }
66
ea3ddccf 67 // Add request url and user agent.
6a488035
TO
68 $_REQUEST['IDS_request_uri'] = $_SERVER['REQUEST_URI'];
69 if (isset($_SERVER['HTTP_USER_AGENT'])) {
70 $_REQUEST['IDS_user_agent'] = $_SERVER['HTTP_USER_AGENT'];
71 }
72
73 $configFile = self::createConfigFile(FALSE);
74
75 // init the PHPIDS and pass the REQUEST array
76 require_once 'IDS/Init.php';
77 try {
78 $init = IDS_Init::init($configFile);
353ffa53 79 $ids = new IDS_Monitor($_REQUEST, $init);
0db6c3e1
TO
80 }
81 catch (Exception $e) {
6a488035
TO
82 // might be an old stale copy of Config.IDS.ini
83 // lets try to rebuild it again and see if it works
84 $configFile = self::createConfigFile(TRUE);
85 $init = IDS_Init::init($configFile);
353ffa53 86 $ids = new IDS_Monitor($_REQUEST, $init);
6a488035
TO
87 }
88
89 $result = $ids->run();
90 if (!$result->isEmpty()) {
91 $this->react($result);
92 }
93
94 return TRUE;
95 }
96
97 /**
fe482240 98 * Create the default config file for the IDS system.
6a488035 99 *
6a0b768e
TO
100 * @param bool $force
101 * Should we recreate it irrespective if it exists or not.
6a488035 102 *
a6c01b45
CW
103 * @return string
104 * the full path to the config file
6a488035 105 */
00be9182 106 public static function createConfigFile($force = FALSE) {
6a488035
TO
107 $config = CRM_Core_Config::singleton();
108 $configFile = $config->configAndLogDir . 'Config.IDS.ini';
109 if (!$force && file_exists($configFile)) {
110 return $configFile;
111 }
112
113 $tmpDir = empty($config->uploadDir) ? CIVICRM_TEMPLATE_COMPILEDIR : $config->uploadDir;
114
115 // also clear the stat cache in case we are upgrading
116 clearstatcache();
117
118 global $civicrm_root;
119 $contents = "
120[General]
121 filter_type = xml
122 filter_path = {$civicrm_root}/packages/IDS/default_filter.xml
123 tmp_path = $tmpDir
124 HTML_Purifier_Path = IDS/vendors/htmlpurifier/HTMLPurifier.auto.php
125 HTML_Purifier_Cache = $tmpDir
126 scan_keys = false
127 exceptions[] = __utmz
128 exceptions[] = __utmc
129 exceptions[] = widget_code
130 exceptions[] = html_message
131 exceptions[] = text_message
132 exceptions[] = body_html
133 exceptions[] = msg_html
134 exceptions[] = msg_text
135 exceptions[] = msg_subject
136 exceptions[] = description
137 exceptions[] = intro
138 exceptions[] = thankyou_text
139 exceptions[] = intro_text
140 exceptions[] = body_text
141 exceptions[] = footer_text
142 exceptions[] = thankyou_text
143 exceptions[] = tf_thankyou_text
144 exceptions[] = thankyou_footer
145 exceptions[] = thankyou_footer_text
146 exceptions[] = new_text
147 exceptions[] = renewal_text
148 exceptions[] = help_pre
149 exceptions[] = help_post
150 exceptions[] = confirm_title
151 exceptions[] = confirm_text
152 exceptions[] = confirm_footer_text
153 exceptions[] = confirm_email_text
154 exceptions[] = report_header
155 exceptions[] = report_footer
156 exceptions[] = data
bfc7747b 157 exceptions[] = json
6a488035
TO
158 exceptions[] = instructions
159 exceptions[] = suggested_message
160 exceptions[] = page_text
161";
162 if (file_put_contents($configFile, $contents) === FALSE) {
163 CRM_Core_Error::movedSiteError($configFile);
164 }
165
6a488035
TO
166 // also create the .htaccess file so we prevent the reading of the log and ini files
167 // via a browser, CRM-3875
168 CRM_Utils_File::restrictAccess($config->configAndLogDir);
169
170 return $configFile;
171 }
172
173 /**
ea3ddccf 174 * This function reacts on the values in the incoming results array.
6a488035
TO
175 *
176 * Depending on the impact value certain actions are
177 * performed.
178 *
179 * @param IDS_Report $result
180 *
3bdca100 181 * @return bool
6a488035 182 */
d3e86119 183 private function react(IDS_Report $result) {
6a488035
TO
184
185 $impact = $result->getImpact();
186 if ($impact >= $this->threshold['kick']) {
187 $this->log($result, 3, $impact);
188 $this->kick($result);
189 return TRUE;
190 }
191 elseif ($impact >= $this->threshold['warn']) {
192 $this->log($result, 2, $impact);
193 $this->warn($result);
194 return TRUE;
195 }
196 elseif ($impact >= $this->threshold['log']) {
197 $this->log($result, 0, $impact);
198 return TRUE;
199 }
200 else {
201 return TRUE;
202 }
203 }
204
205 /**
ea3ddccf 206 * This function writes an entry about the intrusion to the database.
6a488035 207 *
c490a46a 208 * @param array $result
77b97be7
EM
209 * @param int $reaction
210 *
3bdca100 211 * @return bool
6a488035
TO
212 */
213 private function log($result, $reaction = 0) {
214 $ip = (isset($_SERVER['SERVER_ADDR']) &&
3bdca100 215 $_SERVER['SERVER_ADDR'] != '127.0.0.1') ? $_SERVER['SERVER_ADDR'] : (
216 isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : '127.0.0.1'
006389de 217 );
6a488035
TO
218
219 $data = array();
220 $session = CRM_Core_Session::singleton();
221 foreach ($result as $event) {
222 $data[] = array(
223 'name' => $event->getName(),
224 'value' => stripslashes($event->getValue()),
225 'page' => $_SERVER['REQUEST_URI'],
226 'userid' => $session->get('userID'),
227 'session' => session_id() ? session_id() : '0',
228 'ip' => $ip,
229 'reaction' => $reaction,
230 'impact' => $result->getImpact(),
231 );
232 }
233
234 CRM_Core_Error::debug_var('IDS Detector Details', $data);
235 return TRUE;
236 }
237
238 /**
ea3ddccf 239 * Warn about IDS.
240 *
241 * @param array $result
242 *
243 * @return array
6a488035
TO
244 */
245 private function warn($result) {
246 return $result;
247 }
248
249 /**
ea3ddccf 250 * Kick (whatever that means!).
251 *
252 * @param array $result
253 *
254 * @throws \Exception
6a488035
TO
255 */
256 private function kick($result) {
257 $session = CRM_Core_Session::singleton();
258 $session->reset(2);
259
f3a27f08
DL
260 $msg = ts('There is a validation error with your HTML input. Your activity is a bit suspicious, hence aborting');
261
6a488035 262 $path = implode('/', $args);
f3a27f08 263 if (in_array(
353ffa53
TO
264 $path,
265 array("civicrm/ajax/rest", "civicrm/api/json")
266 )) {
f3a27f08
DL
267 require_once "api/v3/utils.php";
268 $error = civicrm_api3_create_error(
269 $msg,
6a488035
TO
270 array(
271 'IP' => $_SERVER['REMOTE_ADDR'],
272 'error_code' => 'IDS_KICK',
273 'level' => 'security',
274 'referer' => $_SERVER['HTTP_REFERER'],
275 'reason' => 'XSS suspected',
276 )
277 );
ecdef330 278 CRM_Utils_JSON::output($error);
6a488035 279 }
f3a27f08 280 CRM_Core_Error::fatal($msg);
6a488035 281 }
96025800 282
6a488035 283}