Skip to content

Instantly share code, notes, and snippets.

@rawmain
Last active July 28, 2022 01:55
Show Gist options
  • Save rawmain/30b8504b04a98d4dae4bcf053bbdac4c to your computer and use it in GitHub Desktop.
Save rawmain/30b8504b04a98d4dae4bcf053bbdac4c to your computer and use it in GitHub Desktop.
GDPR-compliant UA/GA3 single-host proxy implementation
<?php
include_once('gaproxy.class.php');
$ga = new GaProxy();
$ga->sendHit();
?>
<?php
class GaProxy {
// Configuration Start
// Set your real Property Nmber where you want to redirect the data
private $property_id = 'UA-XXXXXXXXX-Y'; // Devi mettere il tuo tag UA
// This will be attached to the hostname value, so we can then filter any hit not coming from this script
private $filterHash = 'GDPR-TEST-';
// set this to true, if you want to remove the last Ip's Octet
private $anonymizeIp2 = true;
// Configuration End
public $payload;
// We are setting the jail for the visitors, a PHP is started and the session_id is saved into a session variable
// When collect.php is loaded we will check the current session_id against this value, and if it doesn't match we'll abort sending the hit to Google Analytics
function setupProxy()
{
session_start();
$_SESSION["gajail_session_id"] = session_id();
$_SESSION["gajail_current_url"] = $_SERVER['REQUEST_URI'];
}
// We'll build the hit on there, adding the current user IP address to keep the Geolocation reports, and the user agent
// Again, we'll setup out real filtered UA property in here. An attacker will not be able to guess our real UA account.
function setupHit()
{
$this->payload = $_GET;
$this->payload["uip"] = $this->getIpAddress();
$this->payload["ua"] = $this->getUserAgent();
$this->payload["tid"] = $this->property_id;
$this->payload["dh"] = $this->filterHash.'-'.$this->getRequestHostName();
}
// This will be used to add more antispam mechanism in a future.
function checkRequestHeaders()
{
// TODO
// Check User Agent Format : bots, malformed user agents, etc
// Check Againts spammers blacklist IP's
// Check throttling
}
// Gets the current loading user agent
function getUserAgent()
{
$user_agent = $_SERVER["HTTP_USER_AGENT"];
if (strpos($user_agent, 'Opera') || strpos($user_agent, 'OPR/')) return 'Opera';
elseif (strpos($user_agent, 'Edg/')
|| strpos($user_agent, 'EdgA/')
|| strpos($user_agent, 'EdgiOS/')
|| strpos($user_agent, 'Edge/')) return 'Edge';
elseif (strpos($user_agent, 'Chrome')) return 'Chrome';
elseif (strpos($user_agent, 'Safari')) return 'Safari';
elseif (strpos($user_agent, 'Firefox')) return 'Firefox';
elseif (strpos($user_agent, 'MSIE') || strpos($user_agent, 'Trident')) return 'Internet Explorer';
return 'Other';
}
// Gets the current hostnamea
function getRequestHostName()
{
return $_SERVER["SERVER_NAME"];
}
// Gets the current loading Ip Address
function getIpAddress()
{
$ipAddress = $_SERVER['REMOTE_ADDR']?:($_SERVER['HTTP_X_FORWARDED_FOR']?:$_SERVER['HTTP_CLIENT_IP']);
if (strpos($ipAddress, ':'))
{
$replace_num = strrpos($ipAddress, ':') - strlen($ipAddress) + 1;
$ipAddress6 = substr_replace($ipAddress, '0000', $replace_num);
return $ipAddress6;
}
elseif (strpos($ipAddress, '.'))
{
$octets = explode('.', $ipAddress);
$second_to_last = array_slice($octets, -2, 1, true);
$second_to_last_key = array_key_first($second_to_last);
$second_to_last[$second_to_last_key] = '0';
$last = array_slice($octets, -1, 1, true);
$last_key = array_key_first($last);
$last[$last_key] = '0';
$octets = array_replace($octets, $second_to_last, $last);
return implode('.', $octets);
}
return '185.27.0.0';
}
// This function will care of building the hit payload and sending it to Universal Analytics endpoint
function sendHit()
{
session_start();
$this->setupHit();
if($_SESSION["gajail_session_id"] == session_id())
{
$context = stream_context_create(array('http' => array(
'method' => 'GET',
'header'=>"Accept-language: ".$this->payload["ul"]."\r\n" .
"User-Agent: ".$this->payload["ua"]."\r\n"
)));
$url = 'https://www.google-analytics.com/collect?'.http_build_query($this->payload,'','&');
$fp = fopen($url, 'r', false, $context);
}
header('Content-Type: image/gif');
echo base64_decode("R0lGODdhAQABAIAAAPxqbAAAACwAAAAAAQABAAACAkQBADs=");
die();
}
}
?>
<script src='https://YOURWEBSITE/mazinger.js'></script>
// Include it in every webpage you want to collect pageview hits
// P.S.: Replace YOURWEBSITE with your own site address
(function (context, trackingId, options) {
const history = context.history;
const doc = document;
const nav = navigator || {};
const storage = localStorage;
const encode = encodeURIComponent;
const pushState = history.pushState;
const typeException = 'exception';
const generateZ = () => Math.floor(1000000000 + (Math.random() * 8999999999)).toString();
if (!Math.imul) Math.imul = function(opA, opB) {
opB |= 0;
var result = (opA & 0x003fffff) * opB;
if (opA & 0xffc00000) result += (opA & 0xffc00000) * opB|0;
return result|0;
};
const cyrb53 = function(str, seed = 0) {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
const generateId = () => {
let creationTime = new Date().getTime();
let expiryTime = creationTime + (30 * 24 * 3600 * 1000); // Change 30 to any number of days you want the CID to be valid.
let CIDSource = window.location.host + ";" + navigator.userAgent + ";" + navigator.language + ";" + creationTime;
if (window.localStorage) {
CIDhashed = localStorage.getItem('CID_HASHED');
CIDexpiry = localStorage.getItem('CID_EXPIRY');
if ((CIDhashed === null || CIDexpiry === null)
|| (CIDhashed !== null && CIDexpiry !== null && CIDexpiry >= expiryTime)) {
localStorage.setItem('CID_HASHED', cyrb53(CIDSource).toString(16));
localStorage.setItem('CID_EXPIRY', expiryTime);
}
return storage.CID_HASHED;
} else {
return undefined;
}
};
const getId = () => {
if (!storage.CID_HASHED) {
storage.CID_HASHED = generateId();
}
return storage.CID_HASHED;
};
const serialize = (obj) => {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
if(obj[p] !== undefined) {
str.push(encode(p) + "=" + encode(obj[p]));
}
}
}
return str.join("&");
};
const track = (
type,
eventCategory,
eventAction,
eventLabel,
eventValue,
exceptionDescription,
exceptionFatal
) => {
const url = '/collect.php';
const data = serialize({
v: '1',
ds: undefined,
aip: options.anonymizeIp ? 1 : undefined,
tid: trackingId,
cid: getId(),
t: type || 'pageview',
sd: options.colorDepth && screen.colorDepth ? `${screen.colorDepth}-bits` : undefined,
dr: undefined,
dt: doc.title,
dl: doc.location.origin + doc.location.pathname,
ul: options.language ? (nav.language || "").toLowerCase() : undefined,
de: options.characterSet ? doc.characterSet : undefined,
sr: options.screenSize ? `${(context.screen || {}).width}x${(context.screen || {}).height}` : undefined,
vp: options.screenSize && context.visualViewport ? `${(context.visualViewport || {}).width}x${(context.visualViewport || {}).height}` : undefined,
ec: eventCategory || undefined,
ea: eventAction || undefined,
el: eventLabel || undefined,
ev: eventValue || undefined,
exd: exceptionDescription || undefined,
exf: typeof exceptionFatal !== 'undefined' && !!exceptionFatal === false ? 0 : undefined,
z: generateZ(),
});
var xhr = new XMLHttpRequest();
xhr.open("GET", url+"?"+data);
xhr.send();
};
const trackEvent = (category, action, label, value) => track('event', category, action, label, value);
const trackException = (description, fatal) => track(typeException, null, null, null, null, description, fatal);
history.pushState = function (state) {
if (typeof history.onpushstate == "function") {
history.onpushstate({ state: state });
}
setTimeout(track, options.delay || 10);
return pushState.apply(history, arguments);
}
track();
context.ma = {
trackEvent,
trackException
}
})(window, "MAZINGER", {
anonymizeIp: true,
colorDepth: true,
characterSet: true,
screenSize: true,
language: true
});
<?php
include_once('gaproxy.class.php');
$ga = new GaProxy();
$ga->setupProxy();
/**
* Include this code before HTML tag
* for every webpage you want to
* collect pageview hits
*/
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment