From 3c825a8f55484602f7610b022158ad61f4ae1166 Mon Sep 17 00:00:00 2001 From: systopia Date: Mon, 31 Jul 2017 16:32:58 +0200 Subject: [PATCH] implementing #16 --- proxy/config.php | 6 ++- proxy/proxy.php | 128 ++++++++++++++++++++++++++++------------------- proxy/rest.php | 2 +- 3 files changed, 82 insertions(+), 54 deletions(-) diff --git a/proxy/config.php b/proxy/config.php index 1d90e38..73f081a 100644 --- a/proxy/config.php +++ b/proxy/config.php @@ -38,7 +38,7 @@ $mail_subscription_user_key = NULL; // CAREFUL: only enable temporarily on debug systems. Will log all queries to given PUBLIC file $debug = NULL; //'debug.log'; -// Local network interface or IP to be used for the relayed query +// Local network interface or IP to be used for the relayed query // This is usefull in some VPN configurations (see CURLOPT_INTERFACE) $target_interface = NULL; @@ -91,6 +91,10 @@ $rest_allowed_actions = array( 'getsingle' => array( 'first_name' => 'string', 'last_name' => 'string', + // the following means *all* remaining parameters will be + // added and sanitised as 'string'. Better leave it out + // if you know which parameters you expect + '*' => 'string', ), ), ), diff --git a/proxy/proxy.php b/proxy/proxy.php index e6bbee9..ba8a0f5 100644 --- a/proxy/proxy.php +++ b/proxy/proxy.php @@ -14,10 +14,10 @@ $civiproxy_logo = "/** => '' * where type can be 'int', 'string' (unchecked), */ function civiproxy_get_parameters($valid_parameters) { $result = array(); + $default_sanitation = NULL; foreach ($valid_parameters as $name => $type) { + if ($name == '*') { + // this sets default_sanitation + $default_sanitation = $type; + continue; + } + if (isset($_REQUEST[$name])) { - $value = $_REQUEST[$name]; - if ($type=='int') { - $value = (int) $value; - } elseif ($type == 'string') { - // TODO: sanitize? SQL? - $value = $value; - } elseif ($type == 'float2') { - // TODO: check if safe wrt l10n. rather use sprintf - $value = number_format($value, 2, '.', ''); - } elseif ($type == 'hex') { - // hex code - if (!preg_match("#^[0-9a-f]*$#i", $value)) { - error_log("CiviProxy: removed invalid hex parameter: " . $value); - $value = ''; - } - } elseif ($type == 'email') { - // valid email - if (!preg_match("#^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$#i", $value)) { - error_log("CiviProxy: removed invalid email parameter: " . $value); - $value = ''; - } - } elseif (is_array($type)) { - // this is a list of valid options - $requested_value = $value; - $value = ''; - foreach ($type as $allowed_value) { - if ($requested_value === $allowed_value) { - $value = $requested_value; - break; - } - } - } else { - error_log("CiviProxy: unknown type '$type'. Ignored."); - $value = ''; + $result[$name] = civiproxy_sanitise($_REQUEST[$name], $type); + } + } + + // process wildcard elements + if ($default_sanitation !== NULL) { + // i.e. we want the others too + $remove_parameters = array('key', 'api_key', 'version', 'entity', 'action'); + foreach ($_REQUEST as $name => $value) { + if (!in_array($name, $remove_parameters) && !isset($valid_parameters[$name])) { + $result[$name] = civiproxy_sanitise($value, $default_sanitation); } - $result[$name] = $value; } } return $result; } +/** + * sanitise the given value with the given sanitiation type + */ +function civiproxy_sanitise($value, $type) { + if ($type=='int') { + $value = (int) $value; + } elseif ($type == 'string') { + // TODO: sanitize? SQL? + $value = $value; + } elseif ($type == 'float2') { + // TODO: check if safe wrt l10n. rather use sprintf + $value = number_format($value, 2, '.', ''); + } elseif ($type == 'hex') { + // hex code + if (!preg_match("#^[0-9a-f]*$#i", $value)) { + error_log("CiviProxy: removed invalid hex parameter: " . $value); + $value = ''; + } + } elseif ($type == 'email') { + // valid email + if (!preg_match("#^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$#i", $value)) { + error_log("CiviProxy: removed invalid email parameter: " . $value); + $value = ''; + } + } elseif (is_array($type)) { + // this is a list of valid options + $requested_value = $value; + $value = ''; + foreach ($type as $allowed_value) { + if ($requested_value === $allowed_value) { + $value = $requested_value; + break; + } + } + } else { + error_log("CiviProxy: unknown type '$type'. Ignored."); + $value = ''; + } +} + + /** * generates a CiviCRM REST API compliant error * and ends processing @@ -224,7 +248,7 @@ function civicrm_api3($entity, $action, $data) { // extract site key $site_keys = array_values($sys_key_map); if (empty($site_keys)) civiproxy_http_error('No site key set.'); - + $query = $data; // array copy(!) $query['key'] = $site_keys[0]; $query['json'] = 1; @@ -247,7 +271,7 @@ function civicrm_api3($entity, $action, $data) { } $response = curl_exec($curlSession); - + if (curl_error($curlSession)){ civiproxy_http_error(curl_error($curlSession)); } else { diff --git a/proxy/rest.php b/proxy/rest.php index e5b587b..d97efb7 100644 --- a/proxy/rest.php +++ b/proxy/rest.php @@ -44,7 +44,7 @@ if (empty($credentials['api_key'])) { // check if the call itself is allowed $action = civiproxy_get_parameters(array('entity' => 'string', 'action' => 'string', 'version' => 'int', 'json' => 'int', 'sequential' => 'int')); if (!isset($action['version']) || $action['version'] != 3) { - civiproxy_rest_error("Invalid entity/action."); + civiproxy_rest_error("API 'version' information missing."); } // in release 0.4, allowed entity/actions per IP were introduced. To introduce backward compatibility,