Skip to content

Commit

Permalink
Issue #39: Hash IP addresses when tracking project usage.
Browse files Browse the repository at this point in the history
By @quicksketch, @BWPanda, and @jenlampton.
  • Loading branch information
quicksketch authored Aug 28, 2021
1 parent d8055fc commit a0b5b11
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 6 deletions.
35 changes: 31 additions & 4 deletions project_release/project-release-serve-history.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,19 @@
* location ^= /release-history {
* rewrite ^/(.*)$ /modules/project/project_release/project-release-serve-history.php?q=$1;
* }
* @endcode
*
* To call this file directly (without the rewrite):
*
* @code
* http://example.com/modules/project/project_release/project-release-serve-history.php?q=example_project/1.x&version=1.x-1.0.0&site_key=JF7UsBYsZDXiUL3j97rdUAp49X1WNvsEW-Y4uheoRHw
* @endcode
*
* Or with the rewrite in place:
*
* @code
* http://example.com/release-history/example_project/1.x?version=1.x-1.0.0&site_key=JF7UsBYsZDXiUL3j97rdUAp49X1WNvsEW-Y4uheoRHw
* @endcode
*
* Configuration within this file is usually unnecessary and settings should be
* automatically determined. If manual setting of the BACKDROP_ROOT or
Expand Down Expand Up @@ -90,7 +100,7 @@
$version_api = $args[1];
}

// Sanitize the user-supplied input for use in filenames.
// Sanitize the user-supplied input for use in file names.
$whitelist_regexp = '@[^a-zA-Z0-9_.-]@';
$safe_project_name = preg_replace($whitelist_regexp, '#', $project_name);
$safe_api_vers = preg_replace($whitelist_regexp, '#', $version_api);
Expand Down Expand Up @@ -129,19 +139,36 @@
// We can't call module_exists without bootstrapping to a higher level so
// we'll settle for checking that the table exists.
if (db_table_exists('project_usage_raw')) {
// Have to include password.inc for user_hash_password().
require_once BACKDROP_ROOT . '/' . settings_get('password_inc', 'core/includes/password.inc');

$site_key = $_GET['site_key'];
$project_version = isset($_GET['version']) ? $_GET['version'] : '';
$ip_address = ip_address();
$ip_address_hashed = user_hash_password(ip_address());

// Compute a GMT timestamp for beginning of the day. getdate() is
// affected by the server's timezone so we need to cancel it out.
$now = time();
$time_parts = getdate($now - date('Z', $now));
$timestamp = gmmktime(0, 0, 0, $time_parts['mon'], $time_parts['mday'], $time_parts['year']);

$result = db_query("UPDATE {project_usage_raw} SET version_api = :version_api, version = :version, hostname = :hostname WHERE name = :name AND timestamp = :timestamp AND site_key = :site_key", array(':version_api' => $version_api, ':version' => $project_version, ':hostname' => $ip_address, ':name' => $project_name, ':timestamp' => $timestamp, ':site_key' => $site_key));
$result = db_query("UPDATE {project_usage_raw} SET version_api = :version_api, version = :version, hostname = :hostname WHERE name = :name AND timestamp = :timestamp AND site_key = :site_key", array(
':version_api' => $version_api,
':version' => $project_version,
':hostname' => $ip_address_hashed,
':name' => $project_name,
':timestamp' => $timestamp,
':site_key' => $site_key,
));
if ($result->rowCount() === 0) {
db_query("INSERT INTO {project_usage_raw} (name, timestamp, site_key, version_api, version, hostname) VALUES (:name, :timestamp, :site_key, :version_api, :version, :hostname)", array(':name' => $project_name, ':timestamp' => $timestamp, ':site_key' => $site_key, ':version_api' => $version_api, ':version' => $project_version, ':hostname' => $ip_address));
db_query("INSERT INTO {project_usage_raw} (name, timestamp, site_key, version_api, version, hostname) VALUES (:name, :timestamp, :site_key, :version_api, :version, :hostname)", array(
':name' => $project_name,
':timestamp' => $timestamp,
':site_key' => $site_key,
':version_api' => $version_api,
':version' => $project_version,
':hostname' => $ip_address_hashed,
));
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions project_usage/project-usage-process.php
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,19 @@
/**
* @file
* Processes the project_usage statistics.
*
* This script should be run from the command line, with a similar format:
*
* @code
* ./project-usage-process.php --url=http://example.com --root=/var/www/html
* @endcode
*
* Running this on a cron job could look like this:
*
* @code
* # Run once per hour:
* 0 * * * * /var/www/html/modules/contrib/project/project_usage/project-usage-process.php --url=http://example.com --root=/var/html/www
* @endcode
*/

// Define the root directory of the Backdrop installation.
Expand Down
35 changes: 33 additions & 2 deletions project_usage/project_usage.install
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ function project_usage_schema() {
'default' => 0,
),
'hostname' => array(
'description' => 'The IP address of the incoming request (to detect possible abuse).',
'description' => 'The hashed IP address of the incoming request. Actual IP address is not logged for privacy.',
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
Expand Down Expand Up @@ -133,7 +133,7 @@ function project_usage_schema() {
'length' => 128,
'not null' => TRUE,
'default' => '',
'description' => "The IP address of the incoming request (to detect possible abuse).",
'description' => 'The hashed IP address of the incoming request. Actual IP address is not logged for privacy.',
),
),
'primary key' => array('timestamp', 'site_key', 'project_nid'),
Expand Down Expand Up @@ -250,3 +250,34 @@ function project_usage_schema() {
return $schema;
}

/**
* Hash IP addresses for privacy.
*/
function project_usage_update_1000() {
// Include password.inc for user_hash_password().
require_once BACKDROP_ROOT . '/' . settings_get('password_inc', 'core/includes/password.inc');

// Update raw values. Hashed IPs have a longer value (55 characters) than an
// IP address (16 characters max).
$raw_rows = db_query("SELECT hostname FROM {project_usage_raw} WHERE LENGTH(hostname) < :hash_length GROUP BY hostname", array(
'hash_length' => BACKDROP_HASH_LENGTH,
));

foreach ($raw_rows as $raw_row) {
db_query("UPDATE {project_usage_raw} SET hostname = :hostname_hashed WHERE :hostname = :hostname", array(
'hostname_hashed' => $raw_row->hostname,
'hostname' => user_hash_password($raw_row->hostname),
));
}

// Update daily values with a hashed IP address.
$day_rows = db_query("SELECT hostname FROM {project_usage_day} WHERE LENGTH(hostname) < :hash_length GROUP BY hostname", array(
'hash_length' => BACKDROP_HASH_LENGTH,
));
foreach ($day_rows as $day_row) {
db_query("UPDATE {project_usage_day} SET hostname = :hostname_hashed WHERE hostname = :hostname", array(
':hostname_hashed' => user_hash_password($day_row->hostname),
':hostname' => $day_row->hostname,
));
}
}

0 comments on commit a0b5b11

Please sign in to comment.