Affiliate tracking security, written down plainly.
This page covers AffiliateTracking's affiliate tracking security: what it stores, what it refuses to store, and what it checks before it writes anything. It also lists what we haven't done yet, like an outside audit.
No raw IPsNo card numbersNo tax forms
- affiliate
- 42
- landing page
- /pricing
- utm_source
- newsletter
- subid
- oct-issue
- country
- US
- ip
- 9c1e04…a07b keyed hash
- browser
- 51fd2e…c3e0 keyed hash
AffiliateTracking never writes down the visitor's IP address itself.
The short list
What it stores, and what it never stores
On self-host, all of this sits in your database on your server, and it doesn't send your data to us. On cloud, it sits in your own workspace on our servers at Hetzner in Germany.
Stored
- Affiliate names and emails
- Buyer emails, so the fraud rules can spot self-referrals
- Click details: landing page, referrer, UTM tags, subid, country
- Keyed hashes of IP and browser, for unique counts and fraud rules
- Sales, commissions, refunds and payouts, in the ledger
- API keys as hashes only
Never stored
- Raw IP addresses
- Card numbers or bank details. Your billing tool handles payments
- Tax forms, SSNs or EINs. Only the form's status
- API keys in readable form, after you've seen them once
- Raw clicks older than 90 days, unless you change that
No billing event counts until it passes
A fake webhook is the easiest way to steal commission. So nothing from outside touches the database until it passes these checks.
Signed or password-checked
Stripe, Paddle and Lemon Squeezy events need a valid signature. Chargebee and Recurly need the username and password you set.
Checked on the raw bytes
It checks the signature on exactly what arrived, before parsing anything, with a timing-safe compare.
Old events turned away
Stripe, Paddle and Recurly signatures carry a time. It refuses anything older than 5 minutes, so nobody can replay a captured request later.
Counted once
It keeps each event by its source and ID. A repeat, honest or not, credits nothing.
Size-capped
It refuses anything over 1 MB.
Off until you turn it on
A billing route only exists once you set its secret. Unset means the address answers 404.
Money records nobody can rewrite
The ledger's rules live in the database itself, so a bug or a bad script can't quietly change what's owed.
Read how the ledger keeps every sale for the full picture.
- No edits, no deletesDatabase triggers refuse changes to money rows. A fix is a new entry that reverses the old one.
- An audit log that only growsEach payout and tax form status change adds a row. Nothing overwrites the old ones.
- A Postgres role that can't deleteRun the app as a role with no DELETE or TRUNCATE on money tables. A separate owner role applies database changes.
- Whole cents onlyEvery amount is an integer. It refuses commission math that would overflow instead of rounding it.
Who can see what
There are two kinds of access, and they don't overlap.
You, the admin
- Admin API keys, made from the command line
- Each key is long and random, shown once, stored as a hash
- 600 requests a minute per key
- Failed logins are rate-limited per IP before any database lookup
- On cloud, a password stored hashed, and reset links that work once, for 1 hour
Your affiliates
- A one-time login code that expires after 24 hours
- A session that lasts 30 days, or a key scoped to their own account
- Their own numbers and ledger, never anyone else's
- No access to admin API routes
Privacy tools you control
You decide how much to keep. The defaults already lean private.
IP trimming
Drop the last part of every IP before it's hashed. Counts and fraud rules then work on the network block, not the person.
Click retention
Raw clicks go after 90 days by default. Daily totals stay, so reports still work. Set any number of days.
Export and erase
Export everything about an affiliate, or erase them. Erasing keeps the money records balanced.
How we check it, and what we haven't done yet
Here's what we've done so far, with dates, and what we haven't.
Done
- An internal security review of the code,
- Money rules tested on both SQLite and Postgres on every change
- Property tests that throw random sales at the ledger
- Release files published with checksums
Built in
- Server time limits against slow-connection attacks
- Owner-only files and a refusal to start with a weak secret
- Link redirects only to domains you allow
- All database queries parameterized
Not done yet
- An outside penetration test
- SOC 2 or ISO 27001
- A paid bug bounty
- Off-site copies of cloud backups. Nightly backups stay on the server for now
Found a security issue?
Email us with "Security" in the subject line. Tell us what you found and how to reproduce it. Please don't post it publicly until it's fixed.
- Include the version you tested and the steps to reproduce it
- Test on your own install, never on someone else's
Security FAQ
Where is my data stored?
On self-host, in your own database on your own server, and AffiliateTracking doesn't send it to us. On cloud, in your own workspace on our servers at Hetzner in Germany, backed up every night.
Is AffiliateTracking GDPR compliant?
No software is compliant on its own. You're responsible for how you use data. AffiliateTracking gives you the tools: no raw IPs, IP trimming, click retention, and affiliate export and erase.
Do you have SOC 2?
No, and no outside penetration test yet either. We'd rather say that here than let you find out later.
Does it store card details?
No. Your billing tool takes the payment. The webhooks AffiliateTracking reads carry amounts, IDs and the buyer's email, never full card numbers.
Can an affiliate see another affiliate's data?
No. An affiliate's session or key only reaches their own account, and the security review found no way across.
How do I report a vulnerability?
Email support@affiliatetracking.co with "Security" in the subject line, and please keep it private until it's fixed.