
Learn more about Fingerprint
- Streamline user experiences for trusted traffic
- The highest accuracy device identification for mobile and web
- Improve visitor analytics on mobile and web
Almost every web developer has to combat fraud at some point. Attacks might include malicious users trying to brute force passwords, place fraudulent orders, initiate bot attacks, or bypass your site’s paywall.
Traditional methods of tracking users in PHP often fall short when preventing fraud. This is where browser fingerprinting comes in. Browser fingerprinting is a technique that generates a highly accurate identifier that can uniquely identify someone based on their browser and device settings.
Why Browser Fingerprinting?
Browser fingerprinting has many practical applications — from helping block malicious users to fighting bank fraud:
- Combating Malicious Users - Browser fingerprints can help identify when a user trying to register is a bot rather than a real person. Fingerprints can also help detect when a malicious user is using a legitimate account.
- Preventing Financial Fraud - You can use fingerprints to detect and stop malicious users from testing stolen credit cards by making many small purchases on your site from different credit cards.
- Enforcing paywalls - Sometimes, tech-savvy readers use incognito mode or delete their cookies to bypass paywalls and access restricted content. Using browser fingerprints, you can catch users bypassing your paywall to ensure your business isn’t losing revenue.
Browser Fingerprinting in PHP
Suppose you are running a streaming site that offers a 14-day free trial to new users. However, a malicious user can repeatedly register with different email addresses to take advantage of your trial offer.
Unfortunately, PHP is exclusively a server-side programming language, so you can’t implement browser fingerprinting in PHP alone. But, by implementing Fingerprint on your frontend, you can easily add fingerprinting to prevent the same user from registering in your application with multiple email addresses.
In this tutorial, I’ll show you how to use Fingerprint to generate browser fingerprints for your PHP application. I’ll contrast fingerprinting with several traditional PHP-only ways to track users (session tracking, HTTP cookies, and IP address tracking) so you understand why fingerprinting is a more reliable solution in most cases.
Project Setup
Following this tutorial must have PHP and SQLite3 installed on your system. You can find the finished code for this tutorial on Github with the files for the initial project setup in the ./original
directory.
Once you have the dependencies ready, create a file called register.php
:
<?php
$db = new SQLite3("data.db");
if($_SERVER["REQUEST_METHOD"] == "POST"){
if(empty($_POST['email'])) {
die("Email is required");
} else if(empty($_POST['password'])) {
die("Password is required");
} else {
$stmt = $db->prepare("SELECT * FROM users WHERE email = ?");
$stmt -> bindValue(1, $_POST["email"], SQLITE3_TEXT);
$res = $stmt->execute();
if(($res->fetchArray())[0]) {
die("Email already exists");
} else {
$insert_stmt = $db->prepare("INSERT INTO users(email, password, visitorId) VALUES(?, ?, ?)");
$insert_stmt -> bindValue(1, $_POST["email"], SQLITE3_TEXT);
$insert_stmt -> bindValue(2, password_hash($_POST["password"], PASSWORD_BCRYPT), SQLITE3_TEXT);
$insert_stmt -> bindValue(3, $_POST["visitorId"], SQLITE3_TEXT);
$res = $insert_stmt->execute();
if($res) {
header('Location: dashboard.html');
} else {
die("An error occurred");
}
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div class="flex h-screen bg-blue-700">
<div class="max-w-lg m-auto bg-blue-100 rounded p-5">
<h2 class="text-xl">Sign Up</h2>
<p class="text-sm">Please fill this form to create an account.</p>
<form class="p-3" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post">
<div class="form-group">
<label class="block mb-2 text-blue-500">Email</label>
<input
class="w-full p-2 mb-6 text-blue-700 border-b-2 border-blue-500 outline-none focus:bg-gray-300"
type="text" name="email">
</div>
<div class="form-group">
<label class="block mb-2 text-blue-500">Password</label>
<input class="w-full p-2 mb-6 text-blue-700 border-b-2 border-blue-500 outline-none focus:bg-gray-300" type="password" name="password">
</div>
<div class="form-group">
<input class="w-full bg-blue-700 hover:bg-pink-700 text-white font-bold py-2 px-4 mb-6 rounded" type="submit" value="Submit">
</div>
</form>
<footer>
<a class="text-blue-700 hover:text-pink-700 text-sm float-left" href="#">Log In</a>
</footer>
</div>
</div>
</body>
</html>
This simple registration form uses SQLite as its database and TailWind CSS to look presentable.
Next, create a file called dashboard.html
in the same folder:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Dashboard</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div class="flex h-screen items-center justify-center bg-blue-700">
<h1 class="text-6xl text-white">Welcome, user</h1>
</div>
</body>
</html>
After successful registration, a simple HTML page appears.
Finally, create the database using SQLite:
sqlite3 data.db
This action will create a file called data.db
and provide you with a sqlite
prompt. Next, run the following query in the prompt to create a users
table with an id, email, and password field:
CREATE TABLE users(
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE,
password TEXT
);
Finally, press CTRL-D
to exit the sqlite
prompt.
You are now ready to test user registration. Start the PHP server on port 8000.
php -S localhost:8000
Now, open your browser and navigate to http://localhost:8000/register.php
. You should see a screen like this:
Try registering a user with some email and password. Once successfully registered, you should be redirected to the dashboard.
Come back to the register.php
page again and try registering again with a different email. You will see that the app allows you to re-register.
In the next section, I’ll show you how to prevent re-registration using browser fingerprinting integrated with your PHP application. Before you continue, wipe your database clean to avoid any conflicts.
Using Fingerprint
Fingerprint is a device identification and browser fingerprinting service that uses a combination of fingerprinting, cookies, server-side techniques, and machine learning to generate a browser fingerprint that is up to 99.5% accurate.
To get started with Fingerprint, you will need a Fingerprint Pro account. If you do not have an account, you can start a 14-day free trial with no credit card required.
Once you have an account, visit your dashboard and select the subscription that you created while registering. Head to the API Keys
section from the left sidebar and copy the active public API key.
After you have the API key, install the JavaScript agent. This JavaScript agent will run in the browser, so you do not need a backend PHP component to generate the fingerprint. The required snippet can either be downloaded from a CDN (content delivery network) or installed through NPM. In this example, I’ll use a CDN.
Inside your PHP application’s HTML, add the following to your <head>
tag:
<script
async
src="https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs-pro@3/dist/fp.min.js"
onload="initFingerprintJS()"
></script>
This snippet downloads the required JavaScript file and runs the initFingerprintJS()
function. Let's create the function (before the previous <script>
tag):
<script>
function initFingerprintJS() {
FingerprintJS.load({apiKey: 'your-public-api-key'})
.then(fp => fp.get())
.then(result => console.log(result.visitorId));
}
</script>
Note that you need to replace your-public-api-key
with the actual Public API key you copied from your dashboard.
This function calls the Fingerprint service to create a visitorID
and print it to the console. You can reload the registration page to see an alphanumeric hash in your JavaScript console.
Now that you have a unique visitor ID, you can send this with every request and store it in your database after registration.
First, create a new column called visitorId
on the users
table:
ALTER TABLE users ADD COLUMN visitorId TEXT;
Add the generated visitorId
to the form as a hidden
field:
<input name="visitorId" id="visitorId" value="" hidden>
And change the initFingerprintJS()
function to set the value of the field:
<script>
function initFingerprintJS() {
FingerprintJS.load({apiKey: 'your-public-api-key'})
.then(fp => fp.get())
.then(result => {
document.getElementById('visitorId').value = result.visitorId
});
}
</script>
Now the visitorId
needs to be saved in the database. Just like you did in the IP tracking part, modify the code so that the visitorId
is saved:
$insert_stmt = $db->prepare("INSERT INTO users(email, password, visitorId) VALUES(?, ?, ?)");
$insert_stmt -> bindValue(1, $_POST["email"], SQLITE3_TEXT);
$insert_stmt -> bindV