Skip to content

Commit

Permalink
Adapt Martin's implemetation :
Browse files Browse the repository at this point in the history
- IP stripping option moved to tracking namespace rather then logging logging, and able to configure the amount of bits.
- Anonymize() doesn't throw anymore but returns null on invalid ip addresses. Also guarantees the IP string to be valid.
- Add default option for IP stripping + document in readme

Update notes:
- Enables IP address stripping for anonymization thanks to @S1SYPHOS
- IPs provided by your server to PHP now have to be valid ones (new IP checking meccanism) [rare-breaks]
- Breaking change: By default, simplestats now truely anonimizes user IPs (by 1 bit by default, set to 0 to preserve initial behaviour)
- Various whitespace syntax fixes
  • Loading branch information
Daandelange committed Apr 7, 2022
1 parent 61e8ad0 commit 953c8c2
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 19 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,12 @@ Here's a list with options that have been tested. (the `daandelange.simplestats`
| `tracking.enableVisitLanguages` | Bool | true | Enables a counter per language per page. | Only effective in multi-language Kirby installations and `enableVisits` enabled. |
| `tracking.ignore.roles` | Array | `['admin']` | Ignore any tracking for connected users with these roles. | |
| `tracking.ignore.pages` | Array | `[]` | Ignore tracking for these page ids. | Make sure to use the full id, not the slug. |
| `tracking.ignore.templates` | Array | `['error']` | Ignore tracking for pages with these templates. | Check against `template()` and `intendedTemplate()` |
| `tracking.ignore.templates` | Array | `['error']` | Ignore tracking for pages with these templates. | Checked against `template()` and `intendedTemplate()` |
| `tracking.ignore.bots` | Bool | false | Ignore tracking any bots. | |
| `tracking.ignore.botVisits` | Bool | true | Ignore counting bot page views. | |
| `tracking.ignore.botReferers` | Bool | true | Ignore tracking referrers sent by bots. | |
| `tracking.salt` | String | `'CHANGEME'` | A unique hash, used to generate a unique user id from visitor data. | Recommended to change, ensures that user identifying information is hard to retrieve if your database leaks. |
| `tracking.anonimizeIpBits` | Number | `1` | Anonymise the IP address of X bits. | Use `0` for no anonymisation, `4` for full anonymisation. |
| `tracking.method` | SimplestatsTrackingMode | `onLoad` | Tracking mode. See `SimplestatsTrackingMode` for more information. | `onLoad` is the only fully automatic mode, others need manual attention. |
| **PANEL** | | | | |
| `panel.dismissDisclaimer` | Bool | false | Dismisses the panel disclaimer message. | |
Expand Down
3 changes: 2 additions & 1 deletion src/config/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
'enableVisits' => true, // Enables tracking of page visits
'enableVisitLanguages' => true, // In multilanguage setups, separately count language hits with page counts
'salt' => 'CHANGEME', // Salt used to obfuscate unique user string.
'uniqueSeconds' => 1*24*60*60, // Anonimised user data is deleted after this delay to become obfuscated
'uniqueSeconds' => 1*24*60*60, // Anonimised user data is deleted after this delay to become void
'imageStyle' => 'position: absolute; right: 0; pointer-events: none; height: 1px; width: 1px; opacity: 0;', // The style applied to the tracking image. Only for tracking.method=OnImage on $page->simpleStatsImage();
'anonimizeIpBits' => 1, // how many bits to strip from the end of the IP ? (for IP anonimization)

// Tracking blacklist
'ignore' => [
Expand Down
46 changes: 29 additions & 17 deletions src/models/SimpleStats.php
Original file line number Diff line number Diff line change
Expand Up @@ -310,37 +310,49 @@ public static function getPageIDWithLang($page_uri, string $forceLang = null): s
* See https://github.com/geertw/php-ip-anonymizer
*
* @param string $address IP address
* @return string Anonymized IP address
* @throws Exception
* @return string Anonymized IP address or null if no valid IP.
*/
public static function anonymize(string $address): string
{
public static function anonymize(string $address): ?string {
$addressPacked = inet_pton($address);
if(!$addressPacked) return null; // return early when IP address has wrong format
// Note: sometimes, on localhost, ip can be `::1` which is still valid and gets stripped to `::`

if (strlen($addressPacked) == 4) {
return inet_ntop(inet_pton($address) & inet_pton('255.255.0.0'));
}
$bitsToAnonymize = option('daandelange.simplestats.tracking.anonimizeIpBits', 1);
if($bitsToAnonymize < 1) return inet_ntop($addressPacked); // Return early when no-anonymization

$ipBits = strlen($addressPacked);

if ($ipBits == 4 || $ipBits == 16) {
$isIpv6 = ($ipBits == 16);
if($isIpv6){
$bitsToAnonymize *= 2; // note: ipv6 has double anonimized bits
$ipBits = 8;
}

if (strlen($addressPacked) == 16) {
return inet_ntop(inet_pton($address) & inet_pton('ffff:ffff:ffff:ffff:0000:0000:0000:0000'));
$maskMax = array_fill(0, $ipBits, '');
$maskMax = array_map(function($k, $v) use($ipBits, $bitsToAnonymize, $isIpv6) {
return ($k >= $ipBits-$bitsToAnonymize )?(($isIpv6?'0000':'0')):($isIpv6?'ffff':'255');
}, array_keys($maskMax), $maskMax);
implode($isIpv6?':':'.', $maskMax);

return inet_ntop($addressPacked & inet_pton(implode($isIpv6?':':'.', $maskMax)));
}

throw new Exception(sprintf('Invalid IP address: "%s"', $address));
return null;
}


// Combines the ip + user_agent to get a unique user string
public static function getUserUniqueString(string $ua = ''): string {
// Anonymize IP beforehand (if enabled)
$ip = option('daandelange.simplestats.log.anonymizeFirst', true) === true
? static::anonymize($_SERVER['REMOTE_ADDR'])
: $_SERVER['REMOTE_ADDR']
;
$ip = static::anonymize($_SERVER['REMOTE_ADDR']); // $kirby->visitor()->ip()

$ip = preg_replace("/[\.\:]+/", '_', preg_replace("/[^a-zA-Z0-9\.\:]+/", '', substr($ip,0,128))); // $kirby->visitor()->ip()
// Replace `.:` by `_`
$ip = preg_replace("/[\.\:]+/", '_', $ip);
$ua = preg_replace("/[^a-zA-Z0-9]+/", '', substr(isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:'UserAgentNotSet', 0, 256) ); // $kirby->visitor()->ip()->userAgent()
$salt = option('daandelange.simplestats.tracking.salt');
// Compute final string

// Compute final string mixing the 3 previous ones
$final = '';
$iplen=strlen($ip);
$ualen=strlen($ua);
Expand All @@ -352,7 +364,7 @@ public static function getUserUniqueString(string $ua = ''): string {
if( $i < $saltlen ) $final.=$salt[$i];
}
//echo '----'.($ip.$salt.$ua).'----';
return hash("sha1", base64_encode($final));
return hash('sha1', base64_encode($final));
}

// Returns an array with detected user hardware setup
Expand Down

0 comments on commit 953c8c2

Please sign in to comment.