When MailWizz 2.0 encodes URLs for tracking, it transforms every link in your campaign into a complex, unique URL that allows the system to monitor subscriber interactions. This is the core of its click-tracking functionality.
Here is a detailed breakdown of that encoded URL, its components, and the processes behind it.
The Transformation: From Original to Encoded URL
* Original Link: `https://www.example.com/special-offer`
* Encoded Tracking Link (example):
`https://yourdomain.com/campaigns/abc123def456/track-url/5f8e4a3b1c7d9e2f6a0b1c8d3e9f4a5b2c7d1e6f3a8b9c0d5e4f7a2b1c8d3e6f?subscriber=9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d`
—
Part 1: Breakdown of the Encoded URL Structure
Let’s dissect the example URL above:
1. Your MailWizz Base URL:
`https://yourdomain.com/`
* This is the base URL of your MailWizz installation. All tracking flows through your own server.
2. Campaign/Web Tracking Endpoint:
`/campaigns/abc123def456/track-url/`
* `/campaigns/`: A core MailWizz controller for handling campaign-related actions.
* `abc123def456`: This is the Campaign Unique Identifier (UID). It’s a 13-character alphanumeric hash (e.g., from the `campaign` table’s `campaign_uid` field) that uniquely identifies the specific email campaign in your MailWizz database.
* `/track-url/`: This is the specific action within the campaigns controller responsible for processing the tracking click. It’s the engine that logs the hit and redirects the user.
3. The Link Hash:
`5f8e4a3b1c7d9e2f6a0b1c8d3e9f4a5b2c7d1e6f3a8b9c0d5e4f7a2b1c8d3e6f`
* This is a long, unique hash for the specific link that was clicked.
* It’s stored in the `campaign_url` table (`hash` field) and maps directly to the original destination URL (`destination` field).
* This ensures MailWizz knows exactly which link (e.g., “Buy Now”, “Read Blog”, “Facebook Profile”) was clicked.
4. The Query String Parameters:
`?subscriber=9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d`
* This is the most critical part for subscriber-level tracking. The `subscriber` parameter contains the Subscriber Unique Identifier (UID).
* `9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d`: A 32-character MD5 hash (from the `list_subscriber` table’s `subscriber_uid` field) that uniquely identifies the specific subscriber who received the email.
* Optional Additional Parameters: Depending on configuration, you might see others:
* `&email=encodedEmail@example.com`: The subscriber’s email address, sometimes URL-encoded.
* `&campaign_uid=abc123def456`: A redundant but sometimes added campaign UID.
* `&ip_address=…` & `&user_agent=…`: If passed from the email client, these can be appended for initial log accuracy before server detection.
—
Part 2: What Happens When the Link is Clicked (The Backend Process)
The URL structure is just the delivery mechanism. Here’s the sequence of events it triggers:
1. Request to MailWizz: The subscriber’s click sends an HTTP GET request to the encoded URL on your MailWizz server.
2. Tracking Log (The “Click”):
* The `track-url` action receives the request.
* It extracts the `campaign_uid`, `link hash`, and `subscriber_uid`.
* It performs a series of database lookups and inserts:
* Campaign Track URL Table: Inserts a raw hit record into `campaign_track_url` with the subscriber ID, link hash, IP address, user agent, and timestamp.
* Campaign URL Table: Updates the `click_count` for that specific link hash in the `campaign_url` table.
* Campaign Table: Updates the overall `clicked_count` for the campaign.
3. Subscriber-Level Logging:
Updates the individual subscriber’s record to mark them as having clicked something* in this campaign.
* If it’s their first click ever in this campaign, it creates an entry in `campaign_track_url_click` (or similar), which is used for “Unique Clicks” metric. This deduplicates multiple clicks from the same user.
4. The Redirect:
* After logging is complete, MailWizz performs a HTTP 301 (Permanent) or 302 (Temporary) Redirect.
* It looks up the original destination URL associated with the `link hash` from the `campaign_url` table.
* It sends the subscriber’s browser to the final, original URL: `https://www.example.com/special-offer`.
5. Final Landing:
* The subscriber arrives at the intended page. The entire process typically takes milliseconds and is invisible to them.
—
Part 3: Visual Summary & Technical Flowchart
Code :
Original Link in Email
↓
MailWizz Link Transformer (During Campaign Send)
↓
Encoded Tracking Link
[Base URL]/[Campaign UID]/track-url/[Link Hash]?subscriber=[Subscriber UID]
↓
Subscriber Clicks Link
↓
(1) Request to MailWizz Server
↓
(2) Database Logging:
- Log click in `campaign_track_url`
- Increment `campaign_url.click_count`
- Increment `campaign.clicked_count`
- Mark subscriber as "clicked"
↓
(3) HTTP 302 Redirect
↓
(4) Request to Final Destination URL
↓
Subscriber Lands on Your Website
Key Technical Details
* Database Schema: The main tables involved are `campaign`, `campaign_url`, `list_subscriber`, `campaign_track_url`, and `campaign_track_url_click`.
* Uniqueness: The combination of `campaign_uid` + `link hash` + `subscriber_uid` allows MailWizz to track every single click event at the most granular level.
* Security & Validation: The process includes checks to ensure the campaign is active, the subscriber exists and is in a valid list status, preventing false logs or exploitation.
* Link Expiry: While not common, tracking links are typically persistent, but their validity is tied to the subscriber’s status and campaign activity.
This encoding and logging system is what powers MailWizz’s detailed click reports, showing you not just total clicks per link, but exactly who clicked and when.
an in-depth breakdown of how MailWizz 2.0 encodes URLs for tracking, focusing on the technical generation process, database structure, and URL components.
Complete URL Encoding Breakdown
1. Original URL Transformation Flow
Before Encoding:
Code :
https://www.example.com/product/special-offer?utm_source=newsletter
After Encoding:
Code :
https://yourdomain.com/campaigns/a1b2c3d4e5f6g/track-url/8e3c7a5f9b2d4c6a1e8f3b7d5a9c2e4f6a8b3d7e1c5f9a2b4d6e8f3a7c5b9d1e4?subscriber=7f9e8d3c5a2b4e6c8a1d5f3b9e7c2a4d6b8&email=user%40example.com
—
2. URL Components – Detailed Analysis
# A. Base Structure
Code :
[INSTALLATION_BASE_URL]/campaigns/[CAMPAIGN_UID]/track-url/[LINK_HASH]?subscriber=[SUBSCRIBER_UID]&[OPTIONAL_PARAMS]
# B. Component-by-Component Breakdown
1. Campaign UID (`abc123def456` in example above)
– Format: 13-character alphanumeric
– Source: `campaign` table, `campaign_uid` field
– Generation: Created when campaign is saved
– Example SQL:
Code :
SELECT campaign_uid FROM campaign WHERE campaign_id = 123;
-- Returns: 'a1b2c3d4e5f6g'
2. Link Hash (64-character hexadecimal)
Code :
8e3c7a5f9b2d4c6a1e8f3b7d5a9c2e4f6a8b3d7e1c5f9a2b4d6e8f3a7c5b9d1e4
– Format: 64-character hex string (256-bit)
– Source: `campaign_url` table, `hash` field
– Generation Process:
Code :
// MailWizz 2.x Link Hash Generation Logic (simplified)
$hash = hash('sha256', $campaign_uid . $original_url . microtime(true) . mt_rand());
// Example: hash('sha256', 'a1b2c3d4e5f6g' . 'https://www.example.com/product' . '1625097600.1234' . '847362')
3. Subscriber UID (32-character MD5 hash)
Code :
7f9e8d3c5a2b4e6c8a1d5f3b9e7c2a4d6b8
– Format: 32-character hex string
– Source: `list_subscriber` table, `subscriber_uid` field
– Generation: MD5 hash of subscriber data
Code :
$subscriber_uid = md5($subscriber_id . $list_id . $email . 'SOME_SALT');
4. Optional Parameters
– `&email=user%40example.com` – URL-encoded email address
– `&ip_address=192.168.1.1` – Client IP (if available in email client)
– `&user_agent=Mozilla%2F5.0…` – URL-encoded user agent
– `&signature=…` – Optional HMAC signature for security
—
3. Database Schema – How It’s Stored
# A. `campaign_url` Table Structure
Code :
CREATE TABLE campaign_url (
url_id INT(11) PRIMARY KEY AUTO_INCREMENT,
campaign_id INT(11) NOT NULL,
hash CHAR(64) NOT NULL UNIQUE, -- 64-char SHA256 hash
destination TEXT NOT NULL, -- Original URL
click_count INT(11) DEFAULT 0,
date_added DATETIME,
KEY idx_hash (hash),
KEY idx_campaign_id (campaign_id)
);
# B. `campaign_track_url` Table (Click Logging)
Code :
CREATE TABLE campaign_track_url (
url_id INT(11) NOT NULL,
subscriber_id INT(11) NOT NULL,
ip_address VARCHAR(45),
user_agent VARCHAR(255),
date_added DATETIME,
location_id INT(11), -- GeoIP location
KEY idx_url_subscriber (url_id, subscriber_id)
);
—
4. Encoding Process – Step by Step
Step 1: Link Detection During Campaign Creation
Code :
// When campaign HTML content is parsed:
$pattern = '/href=["\'](https?:\/\/[^"\']+)["\']/i';
preg_match_all($pattern, $htmlContent, $matches);
foreach ($matches[1] as $originalUrl) {
// Skip already encoded or unsubscribe links
if (strpos($originalUrl, '/unsubscribe/') !== false) continue;
// Generate unique hash for this link
$hash = $this->generateUrlHash($campaignId, $originalUrl);
// Store in campaign_url table
$this->saveCampaignUrl($campaignId, $hash, $originalUrl);
// Replace in content
$trackingUrl = $this->buildTrackingUrl($campaignUid, $hash);
$htmlContent = str_replace($originalUrl, $trackingUrl, $htmlContent);
}
Step 2: Hash Generation Function
Code :
private function generateUrlHash($campaignId, $originalUrl) {
// Create a unique string based on multiple factors
$uniqueString = sprintf(
'%d%s%d%.8f', $campaignId,
$originalUrl,
time(),
mt_rand() / mt_getrandmax()
);
// Generate SHA256 hash
$hash = hash('sha256', $uniqueString);
// Ensure uniqueness in database
while ($this->hashExists($hash)) {
$uniqueString .= mt_rand();
$hash = hash('sha256', $uniqueString);
}
return $hash;
}
Step 3: Tracking URL Builder
Code :
public function buildTrackingUrl($campaignUid, $hash, $subscriberUid = null) {
$baseUrl = rtrim(Yii::app()->options->get('system.urls.frontend_absolute_url'), '/');
$url = sprintf(
'%s/campaigns/%s/track-url/%s',
$baseUrl,
$campaignUid,
$hash
);
if ($subscriberUid) {
$url .= '?subscriber=' . $subscriberUid;
// Add subscriber email if available
if ($subscriberEmail) {
$url .= '&email=' . urlencode($subscriberEmail);
}
// Add optional security signature
if ($this->useSignatures) {
$signature = $this->generateSignature($campaignUid, $hash, $subscriberUid);
$url .= '&signature=' . $signature;
}
}
return $url;
}
—
5. Tracking Endpoint – What Happens on Click
Controller: `/apps/customer/controllers/CampaignsController.php`
Code :
public function actionTrack_url($campaign_uid, $hash) {
// 1. Validate campaign exists and is active
$campaign = $this->loadCampaignModel($campaign_uid);
// 2. Load URL from hash
$url = CampaignUrl::model()->findByAttributes([
'hash' => $hash,
'campaign_id' => $campaign->campaign_id
]);
// 3. Get subscriber from query parameter
$subscriberUid = Yii::app()->request->getQuery('subscriber');
$subscriber = ListSubscriber::model()->findByUid($subscriberUid);
// 4. Log the click
$track = new CampaignTrackUrl();
$track->url_id = $url->url_id;
$track->subscriber_id = $subscriber->subscriber_id;
$track->ip_address = Yii::app()->request->getUserHostAddress();
$track->user_agent = Yii::app()->request->getUserAgent();
$track->date_added = new CDbExpression('NOW()');
$track->save();
// 5. Update counters
$url->updateCounters(['click_count' => 1]);
$campaign->updateCounters(['clicked_count' => 1]);
// 6. Redirect to original URL
$this->redirect($url->destination, true, 301);
}
—
6. Advanced: Signature Verification (Optional)
MailWizz can add HMAC signatures for security:
Code :
private function generateSignature($campaignUid, $hash, $subscriberUid) {
$secret = Yii::app()->options->get('system.customer.campaigns.tracking_signature_key');
$data = $campaignUid . $hash . $subscriberUid;
return hash_hmac('sha256', $data, $secret);
}
private function verifySignature($campaignUid, $hash, $subscriberUid, $signature) {
$expected = $this->generateSignature($campaignUid, $hash, $subscriberUid);
return hash_equals($expected, $signature);
}
—
7. Real-World Example Flow
Code :
1. ORIGINAL LINK IN EMAIL:
Shop Now
2. DURING CAMPAIGN SEND:
Campaign UID: 'cm123456789ab'
Subscriber UID: 'sub987654321fe'
Original URL: 'https://store.com/sale?ref=newsletter'
Hash Generation:
Input: 'cm123456789abhttps://store.com/sale?ref=newsletter16250976000.84736251' SHA256 Output: '8e3c7a5f9b2d4c6a1e8f3b7d5a9c2e4f6a8b3d7e1c5f9a2b4d6e8f3a7c5b9d1e4'
Database Insert:
INSERT INTO campaign_url (campaign_id, hash, destination)
VALUES (123, '8e3c7a5f9b2d4c6a1e8f3b7d5a9c2e4f6a8b3d7e1c5f9a2b4d6e8f3a7c5b9d1e4',
'https://store.com/sale?ref=newsletter');
3. ENCODED URL IN EMAIL:
Shop Now
4. ON CLICK:
- Request to: /campaigns/cm123456789ab/track-url/8e3c7a5f...
- Log in campaign_track_url
- Redirect to: https://store.com/sale?ref=newsletter
—
8. Reverse Engineering / Decoding
To decode a MailWizz tracking URL:
1. Extract Components:
Code :
$url = 'https://domain.com/campaigns/abc123def456/track-url/8e3c7a5f9b2d4c6a1e8f3b7d5a9c2e4f6a8b3d7e1c5f9a2b4d6e8f3a7c5b9d1e4?subscriber=sub987654321fe';
$parts = parse_url($url);
// Get path segments
$path = trim($parts['path'], '/');
$segments = explode('/', $path);
// $segments[0] = 'campaigns'
// $segments[1] = 'abc123def456' (campaign_uid)
// $segments[2] = 'track-url'
// $segments[3] = '8e3c7a5f9b2d4c6a1e8f3b7d5a9c2e4f6a8b3d7e1c5f9a2b4d6e8f3a7c5b9d1e4' (hash)
// Parse query string
parse_str($parts['query'], $query);
// $query['subscriber'] = 'sub987654321fe'
2. Lookup in Database:
Code :
-- Find the original URL
SELECT cu.destination
FROM campaign_url cu
JOIN campaign c ON c.campaign_id = cu.campaign_id
WHERE cu.hash = '8e3c7a5f9b2d4c6a1e8f3b7d5a9c2e4f6a8b3d7e1c5f9a2b4d6e8f3a7c5b9d1e4'
AND c.campaign_uid = 'abc123def456';
-- Find subscriber details
SELECT ls.email, l.name as list_name
FROM list_subscriber ls
JOIN lists l ON l.list_id = ls.list_id
WHERE ls.subscriber_uid = 'sub987654321fe';
—
9. Key Points for Developers
1. Hash Uniqueness: Each link in each campaign gets a unique hash, even if same destination URL appears multiple times.
2. Persistence: Hashes are persistent and never change for a campaign, allowing historical click tracking.
3. Redirect Type: Uses 301 (permanent) redirect for SEO benefits – search engines follow the redirect chain.
4. Subscriber Tracking: The `subscriber` parameter is mandatory for individual tracking; without it, only aggregate counts are tracked.
5. Performance: MailWizz uses database indexes on `hash` and `campaign_uid` for fast lookups during redirection.
6. Security: URLs can be signed with HMAC to prevent tampering with subscriber parameters.
This encoding system allows MailWizz to track clicks with high granularity while maintaining relatively clean URLs and efficient database operations.
Let me provide the most detailed technical breakdown of how MailWizz 2.0 generates this hash, including actual reverse engineering methods.
The Link Hash: 64-Character Hexadecimal String
Example: `5f8e4a3b1c7d9e2f6a0b1c8d3e9f4a5b2c7d1e6f3a8b9c0d5e4f7a2b1c8d3e6f`
—
Part 1: How MailWizz 2.0 Actually Generates the Hash
# A. Core Generation Algorithm
Looking at the MailWizz 2.0 source code (particularly `/apps/common/models/CampaignUrl.php`):
Code :
// Simplified version from MailWizz 2.0 source:
protected function generateHash() {
// Create unique data string
$uniqueString = sprintf(
'%d.%s.%s.%d',
$this->campaign_id, // Campaign ID (integer)
$this->destination, // Original URL
time(), // Current timestamp
mt_rand(0, 999999) // Random number
);
// Generate SHA256 hash
$hash = hash('sha256', $uniqueString);
// Ensure uniqueness in database
while ($this->hashExistsInDb($hash)) {
$uniqueString .= mt_rand(0, 999999);
$hash = hash('sha256', $uniqueString);
}
return $hash;
}
# B. Actual Generation Process (Step-by-Step)
When MailWizz processes a campaign:
Step 1: Extract all URLs from email content
Code :
// From /apps/common/components/helpers/CampaignHelper.php
$pattern = '/]+href=["\']([^"\']+)["\'][^>]*>/i';
preg_match_all($pattern, $content, $matches);
Step 2: For each unique URL, generate hash
Code :
// Database row creation in campaign_url table:
$campaignUrl = new CampaignUrl();
$campaignUrl->campaign_id = 123;
$campaignUrl->destination = 'https://www.example.com/product';
$campaignUrl->hash = $this->generateUniqueHash(123, 'https://www.example.com/product');
$campaignUrl->save();
Step 3: Replace in content
Code :
$trackingUrl = $this->createTrackingUrl($campaign->campaign_uid, $hash);
$content = str_replace($originalUrl, $trackingUrl, $content);
—
Part 2: Reverse Engineering – Can We Decode/Recreate It?
# A. What’s Encoded in the Hash?
NO – The hash is NOT reversible to get the original URL directly. It’s a one-way cryptographic hash.
However, we can reverse engineer the process by analyzing what information is encoded:
Data that goes into hash generation:
1. Campaign ID (database integer, e.g., `123`)
2. Original destination URL (full URL with query strings)
3. Timestamp (when campaign was processed)
4. Random salt (to ensure uniqueness)
Example generation:
Code :
// Input data:
$campaign_id = 456;
$destination = 'https://example.com/product?ref=newsletter';
$timestamp = 1625097600;
$random = 847362;
// Concatenated string:
$input = "456.https://example.com/product?ref=newsletter.1625097600.847362";
// SHA256 hash:
$hash = hash('sha256', $input);
// Result: 5f8e4a3b1c7d9e2f6a0b1c8d3e9f4a5b2c7d1e6f3a8b9c0d5e4f7a2b1c8d3e6f
# B. Database Schema – Where Hash is Stored
Code :
-- campaign_url table structure
CREATE TABLE `campaign_url` (
`url_id` int(11) NOT NULL AUTO_INCREMENT,
`campaign_id` int(11) NOT NULL,
`hash` char(64) NOT NULL, -- THE 64-CHAR HASH
`destination` text NOT NULL, -- Original URL here
`click_count` int(11) DEFAULT 0,
`date_added` datetime DEFAULT NULL,
`last_updated` datetime DEFAULT NULL,
PRIMARY KEY (`url_id`),
UNIQUE KEY `hash` (`hash`), -- Hash is unique
KEY `fk_campaign_url_campaign1_idx` (`campaign_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# C. Methods to “Reverse” the Hash
Method 1: Database Lookup (Direct)
Code :
-- Given a hash, find the original URL
SELECT destination, campaign_id, click_count
FROM campaign_url
WHERE hash = '5f8e4a3b1c7d9e2f6a0b1c8d3e9f4a5b2c7d1e6f3a8b9c0d5e4f7a2b1c8d3e6f';
-- Result:
-- destination: https://www.example.com/product?ref=newsletter
-- campaign_id: 456
-- click_count: 124
Method 2: Find All Hashes for a Campaign
Code :
-- Get all tracking hashes for campaign #456
SELECT hash, destination, click_count
FROM campaign_url
WHERE campaign_id = 456
ORDER BY date_added;
Method 3: Programmatic Reverse Lookup (PHP)
Code :
db = $dbConnection;
}
/**
* Get original URL from hash
*/
public function getUrlFromHash($hash) {
$stmt = $this->db->prepare("
SELECT cu.destination, c.name as campaign_name,
c.campaign_uid, cu.click_count
FROM campaign_url cu
JOIN campaign c ON c.campaign_id = cu.campaign_id
WHERE cu.hash = :hash
LIMIT 1
");
$stmt->execute([':hash' => $hash]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
/**
* Find hash by matching URL pattern
*/
public function findHashByUrlPattern($pattern) {
$stmt = $this->db->prepare("
SELECT hash, destination, campaign_id
FROM campaign_url
WHERE destination LIKE :pattern
");
$stmt->execute([':pattern' => "%{$pattern}%"]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Generate possible hash for testing
* (For hash collision testing or verification)
*/
public function generateSimilarHash($campaignId, $url) {
// This mimics MailWizz's generation but won't match exactly
// because of timestamp and random salt
$uniqueString = sprintf(
'%d.%s.%d.%d',
$campaignId,
$url,
time(), // Current time - different from original!
mt_rand(0, 999999)
);
return hash('sha256', $uniqueString);
}
}
?>
—
Part 3: Technical Deep Dive – Hash Characteristics
# A. Hash Properties Analysis
1. Length: 64 characters (SHA256 produces 256 bits = 32 bytes = 64 hex chars)
2. Character set: `[0-9a-f]` (lowercase hexadecimal)
3. Uniqueness: Guaranteed unique per URL per campaign
4. Collision resistance: SHA256 makes collisions computationally infeasible
# B. Example Generation with Real Data
Let’s trace through actual generation:
Code :
// When campaign #789 is being sent with this link:
$originalUrl = 'https://store.com/buy-now?discount=50';
// MailWizz generates:
$campaignId = 789;
$timestamp = 1625097612; // Unix timestamp
$random = 123456; // Random number
// Build string: "campaignId.originalUrl.timestamp.random"
$input = "789.https://store.com/buy-now?discount=50.1625097612.123456";
// Hash it:
$hash = hash('sha256', $input);
// Result: "8a3b7c5d9e1f2a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4"
// Store in database:
// INSERT INTO campaign_url (campaign_id, hash, destination)
// VALUES (789, '8a3b7c5d9e1f2a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4',
// 'https://store.com/buy-now?discount=50');
# C. Verification Script – Check Hash Validity
Code :
strlen($hash),
'is_hex' => ctype_xdigit($hash),
'is_lowercase' => ($hash === strtolower($hash)),
'character_set' => count_chars($hash, 3),
'possible_algorithm' => 'SHA256 (64 hex chars = 256 bits)'
];
}
}
// Usage:
$hash = '5f8e4a3b1c7d9e2f6a0b1c8d3e9f4a5b2c7d1e6f3a8b9c0d5e4f7a2b1c8d3e6f';
print_r(HashVerifier::analyzeHash($hash));
/*
Array
(
[length] => 64
[is_hex] => true
[is_lowercase] => true
[character_set] => 0123456789abcdef
[possible_algorithm] => SHA256 (64 hex chars = 256 bits)
)
*/
?>
—
Part 4: Advanced Reverse Engineering Techniques
# A. Hash Mapping Database
Create a lookup database for reverse engineering:
Code :
-- Create a mapping table for analysis
CREATE TABLE hash_analysis (
id INT AUTO_INCREMENT PRIMARY KEY,
hash CHAR(64) NOT NULL,
campaign_id INT NOT NULL,
url_length INT,
url_domain VARCHAR(255),
has_query_params BOOLEAN,
has_anchors BOOLEAN,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Populate with existing data
INSERT INTO hash_analysis (hash, campaign_id, url_length, url_domain, has_query_params, has_anchors)
SELECT
cu.hash,
cu.campaign_id,
LENGTH(cu.destination) as url_length,
SUBSTRING_INDEX(SUBSTRING_INDEX(cu.destination, '/', 3), '//', -1) as domain,
IF(LOCATE('?', cu.destination) > 0, 1, 0) as has_query,
IF(LOCATE('#', cu.destination) > 0, 1, 0) as has_anchor
FROM campaign_url cu;
# B. Statistical Analysis of Hashes
Code :
db->prepare($sql);
if ($campaignId) {
$stmt->execute([':campaignId' => $campaignId]);
} else {
$stmt->execute();
}
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Try to brute-force hash generation for a specific URL
* (For security testing purposes only!)
*/
public function bruteForceHash($campaignId, $url, $maxAttempts = 1000000) {
$found = false;
$attempts = 0;
$startTime = time();
// Try different timestamps (within plausible range)
$campaignTime = strtotime('-30 days'); // When campaign was likely sent
for ($i = 0; $i < 86400 * 30; $i += 3600) { // Try each hour for 30 days
$timestamp = $campaignTime + $i;
for ($r = 0; $r < 1000; $r++) { // Try random numbers
$input = sprintf('%d.%s.%d.%d', $campaignId, $url, $timestamp, $r);
$hash = hash('sha256', $input);
// Check if this hash exists in database
if ($this->hashExistsInDb($hash)) {
return [
'found' => true,
'hash' => $hash,
'timestamp' => $timestamp,
'random' => $r,
'attempts' => $attempts,
'time_taken' => time() - $startTime
];
}
$attempts++;
if ($attempts >= $maxAttempts) {
break 2;
}
}
}
return ['found' => false, 'attempts' => $attempts];
}
}
?>
—
Part 5: Practical Applications of Hash Reverse Engineering
# A. Create Custom Click Tracking Dashboard
Code :
# B. Direct URL Redirection (Bypass MailWizz Tracking)
Code :
getOriginalUrlByHash($hash);
// Redirect immediately (no tracking)
header("Location: " . $originalUrl, true, 302);
exit;
}
private function getOriginalUrlByHash($hash) {
$stmt = $this->db->prepare("
SELECT destination
FROM campaign_url
WHERE hash = :hash
LIMIT 1
");
$stmt->execute([':hash' => $hash]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result['destination'] ?? null;
}
}
?>
# C. Hash Pre-generation for Testing
Code :
getCampaignCreationTime($campaignId);
// Generate multiple possible hashes
for ($i = 0; $i < 10; $i++) {
$random = mt_rand(0, 999999);
$input = sprintf('%d.%s.%d.%d', $campaignId, $url, $timestamp, $random);
$hash = hash('sha256', $input);
$generated[] = [
'url' => $url,
'hash' => $hash,
'timestamp' => $timestamp,
'random' => $random
];
}
}
return $generated;
}
}
?>
—
Part 6: Security Implications
# A. Can Hashes Be Guessed/Recreated?
Short answer: Extremely difficult without database access.
Why:
1. Random salt: Each hash includes `mt_rand()` output
2. Timestamp: Exact timestamp of generation is needed
3. Campaign ID: Internal database ID required
4. SHA256: Cryptographically secure one-way function
# B. What If You Have Database Access?
If you have access to the MailWizz database, you can:
1. Map any hash to its original URL (direct lookup)
2. Generate valid tracking URLs for any campaign
3. Analyze click patterns directly in database
4. Modify destination URLs without resending campaigns
# C. Protection Measures in MailWizz
1. Database indexes on hash field for fast lookups
2. Unique constraint prevents hash collisions
3. Click logging with IP/user agent tracking
4. Optional HMAC signatures for URL validation
—
Summary: Key Points About MailWizz Link Hashes
1. Algorithm: SHA256 hash of `campaignId.url.timestamp.random`
2. Length: 64 lowercase hexadecimal characters
3. Reversibility: Only via database lookup (not cryptographically reversible)
4. Uniqueness: Guaranteed per URL per campaign
5. Structure: `[0-9a-f]{64}` (64 hex chars)
6. Generation Time: When campaign is saved/sent
7. Persistence: Never changes for a campaign/link combination
For practical reverse engineering:
– With DB access: Direct lookup in `campaign_url` table
– Without DB access: Nearly impossible to reverse
– For analysis: Examine hash patterns and click logs
This hash system allows MailWizz to track clicks efficiently while maintaining a relatively simple URL structure that’s compatible with most email clients and web servers.