3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2019
36 * Define the threshold for the ids reactions.
39 private $threshold = [
53 * This function includes the IDS vendor parts and runs the
54 * detection routines on the request array.
60 public function check($route) {
61 if (CRM_Core_Permission
::check('skip IDS check')) {
65 // lets bypass a few civicrm urls from this check
66 $skip = ['civicrm/admin/setting/updateConfigBackend', 'civicrm/admin/messageTemplates'];
67 CRM_Utils_Hook
::idsException($skip);
68 $this->path
= $route['path'];
69 if (in_array($this->path
, $skip)) {
73 $init = self
::create(self
::createRouteConfig($route));
75 // Add request url and user agent.
76 $_REQUEST['IDS_request_uri'] = $_SERVER['REQUEST_URI'];
77 if (isset($_SERVER['HTTP_USER_AGENT'])) {
78 $_REQUEST['IDS_user_agent'] = $_SERVER['HTTP_USER_AGENT'];
81 require_once 'IDS/Monitor.php';
82 $ids = new \
IDS_Monitor($_REQUEST, $init);
84 $result = $ids->run();
85 if (!$result->isEmpty()) {
86 $this->react($result);
93 * Create a new PHPIDS configuration object.
95 * @param array $config
96 * PHPIDS configuration array (per INI format).
99 protected static function create($config) {
100 require_once 'IDS/Init.php';
101 $init = \IDS_Init
::init(NULL);
102 $init->setConfig($config, TRUE);
105 $reflection = new \
ReflectionProperty('IDS_Init', 'instances');
106 $reflection->setAccessible(TRUE);
107 $value = $reflection->getValue(NULL);
109 $reflection->setValue(NULL, $value);
115 * Create conservative, minimalist IDS configuration.
119 public static function createBaseConfig() {
120 $config = \CRM_Core_Config
::singleton();
121 $tmpDir = empty($config->uploadDir
) ? Civi
::paths()->getVariable('civicrm.compile', 'path') : $config->uploadDir
;
122 global $civicrm_root;
126 'filter_type' => 'xml',
127 'filter_path' => "{$civicrm_root}/packages/IDS/default_filter.xml",
128 'tmp_path' => $tmpDir,
129 'HTML_Purifier_Path' => $civicrm_root . 'packages/IDS/vendors/htmlpurifer/HTMLPurifier.auto.php',
130 'HTML_Purifier_Cache' => $tmpDir,
132 'exceptions' => ['__utmz', '__utmc'],
138 * Create the standard, general-purpose IDS configuration used by many pages.
142 public static function createStandardConfig() {
160 'thankyou_footer_text',
167 'confirm_footer_text',
168 'confirm_email_text',
179 $result = self
::createBaseConfig();
181 $result['General']['exceptions'] = array_merge(
182 $result['General']['exceptions'],
190 * @param array $route
193 public static function createRouteConfig($route) {
194 $config = \CRM_Core_IDS
::createStandardConfig();
195 foreach (['json', 'html', 'exceptions'] as $section) {
196 if (isset($route['ids_arguments'][$section])) {
197 if (!isset($config['General'][$section])) {
198 $config['General'][$section] = [];
200 foreach ($route['ids_arguments'][$section] as $v) {
201 $config['General'][$section][] = $v;
203 $config['General'][$section] = array_unique($config['General'][$section]);
210 * This function reacts on the values in the incoming results array.
212 * Depending on the impact value certain actions are
215 * @param IDS_Report $result
219 public function react(IDS_Report
$result) {
221 $impact = $result->getImpact();
222 if ($impact >= $this->threshold
['kick']) {
223 $this->log($result, 3, $impact);
227 elseif ($impact >= $this->threshold
['warn']) {
228 $this->log($result, 2, $impact);
229 $this->warn($result);
232 elseif ($impact >= $this->threshold
['log']) {
233 $this->log($result, 0, $impact);
242 * This function writes an entry about the intrusion to the database.
244 * @param array $result
245 * @param int $reaction
249 private function log($result, $reaction = 0) {
250 // Include X_FORWARD_FOR ip address if set as per IDS patten.
251 $ip = $_SERVER['REMOTE_ADDR'] . (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
' (' . $_SERVER['HTTP_X_FORWARDED_FOR'] . ')' : '');
254 $session = CRM_Core_Session
::singleton();
255 foreach ($result as $event) {
257 'name' => $event->getName(),
258 'value' => stripslashes($event->getValue()),
259 'page' => $_SERVER['REQUEST_URI'],
260 'userid' => $session->get('userID'),
261 'session' => session_id() ?
session_id() : '0',
263 'reaction' => $reaction,
264 'impact' => $result->getImpact(),
268 CRM_Core_Error
::debug_var('IDS Detector Details', $data);
275 * @param array $result
279 private function warn($result) {
284 * Create an error that prevents the user from continuing.
288 private function kick() {
289 $session = CRM_Core_Session
::singleton();
292 $msg = ts('There is a validation error with your HTML input. Your activity is a bit suspicious, hence aborting');
296 ["civicrm/ajax/rest", "civicrm/api/json"]
298 require_once "api/v3/utils.php";
299 $error = civicrm_api3_create_error(
302 'IP' => $_SERVER['REMOTE_ADDR'],
303 'error_code' => 'IDS_KICK',
304 'level' => 'security',
305 'referer' => $_SERVER['HTTP_REFERER'],
306 'reason' => 'XSS suspected',
309 CRM_Utils_JSON
::output($error);
311 CRM_Core_Error
::fatal($msg);