PHPackages                             centamiv/advanced-fingerprint - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. centamiv/advanced-fingerprint

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

centamiv/advanced-fingerprint
=============================

Advanced Server-Side and Client-Side Fingerprinting for Laravel

20PHP

Since Dec 30Pushed 4mo ago1 watchersCompare

[ Source](https://github.com/centamiv/advanced-fingerprint)[ Packagist](https://packagist.org/packages/centamiv/advanced-fingerprint)[ RSS](/packages/centamiv-advanced-fingerprint/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

Advanced Fingerprint for Laravel
================================

[](#advanced-fingerprint-for-laravel)

**Advanced Server-Side and Client-Side Fingerprinting for Laravel Applications.**

Introduction
------------

[](#introduction)

`centamiv/advanced-fingerprint` is a powerful Laravel package designed to identify and track devices interacting with your application. Unlike simple IP-based tracking, this library combines server-side analysis (HTTP headers, Client Hints) with optional client-side techniques (Canvas Fingerprinting) to create a robust "Device Fingerprint".

This fingerprint allows you to:

- Detect account sharing.
- Prevent session hijacking.
- Implement more granular rate limiting.
- Track unique devices for analytics.
- Block bots that rotate IPs but keep the same browser configuration.

Why "Advanced"?
---------------

[](#why-advanced)

Standard fingerprinting usually relies on just the User-Agent and IP address. This is easily spoofed and often inaccurate (e.g., all users behind a corporate proxy might share an IP).

This package goes further by:

1. **Utilizing Client Hints**: Leveraging modern `Sec-CH-UA` headers that replace the User-Agent string.
2. **Hybrid Approach**: Merging server-side data with a client-side calculated hash (Canvas + Environment) for maximum entropy.
3. **Anomaly Detection**: Automatically detecting if a logged-in user's device signature changes mid-session (a sign of token theft).
4. **Developer Friendly**: Providing simple helpers, facades, and events to integrate deeply into your business logic.

Key Features
------------

[](#key-features)

- **Zero-Config Start**: Works out of the box with sensible defaults.
- **Configurable Entropy**: Choose which signals (IP, UA, Accept-Language, Platform) to include.
- **Dual-Layer Identification**: Server Hash (fast, immediate) + Client Hash (accurate, delayed).
- **Session Protection**: Built-in event firing when a signature mismatch occurs.
- **Rate Limit Helper**: Limit actions per *device*, not just per IP.
- **Blade Directive**: One-line integration for client-side fingerprinting script.

Requirements
------------

[](#requirements)

- **PHP**: 8.1 or higher
- **Laravel**: 10.0 or higher

Installation
------------

[](#installation)

Install the package via Composer:

```
composer require centamiv/advanced-fingerprint
```

The package will automatically register its service provider.

Next, you should publish the configuration file. This is crucial for customizing the fingerprinting logic:

```
php artisan vendor:publish --tag=fingerprint-config
```

This will create a `config/fingerprint.php` file in your application.

Configuration
-------------

[](#configuration)

The configuration file is the heart of the library. It determines how "sensitive" or "unique" your fingerprints are.

Open `config/fingerprint.php`:

```
return [
    /*
    |--------------------------------------------------------------------------
    | Fingerprint Algorithm
    |--------------------------------------------------------------------------
    | The hashing algorithm to use for generating the signature.
    | 'sha256' is recommended for a balance of speed and collision resistance.
    */
    'algo' => 'sha256',

    /*
    |--------------------------------------------------------------------------
    | Components
    |--------------------------------------------------------------------------
    | List of components to consider for fingerprint calculation.
    | The more components you add, the more unique (but volatile) the fingerprint will be.
    */
    'components' => [
        // The IP Address. High entropy, but changes on mobile networks.
        // Set to false if you want to track devices across different networks.
        'ip' => true,

        // The User-Agent string. Standard, but legacy.
        'user_agent' => true,

        // The Accept-Language header. Helps distinguish users in the same region.
        'accept_language' => true,

        // Modern Client Hints (Sec-CH-UA). Highly recommended for Chrome/Edge.
        'sec_ch_ua' => true,
        'sec_ch_ua_platform' => true,
        'sec_ch_ua_mobile' => true,
    ],

    /*
    |--------------------------------------------------------------------------
    | Ignore Routes
    |--------------------------------------------------------------------------
    | Routes to exclude from fingerprint injection (optional).
    */
    'ignore_routes' => [
        'api/health-check',
    ],

    /*
    |--------------------------------------------------------------------------
    | Cache & Rate Limiting
    |--------------------------------------------------------------------------
    */
    'cache' => [
        'prefix' => 'device_sig',
        'ttl' => 60, // Cache duration in minutes
    ],

    /*
    |--------------------------------------------------------------------------
    | Anomaly Detection
    |--------------------------------------------------------------------------
    | Features to detect session hijacking or spoofing.
    */
    'anomaly_detection' => [
        'enabled' => true,
        // Fires Centamiv\AdvancedFingerprint\Events\DeviceSignatureChanged
        'fire_event' => true,
    ],
];
```

### Note on configuration

[](#note-on-configuration)

If you set `'ip' => false`, the fingerprint will be identical for the same browser on any network. This is useful for "Remember Device" features but bad for rate limiting (collisions). Choose wisely based on your use case.

Core Concepts
-------------

[](#core-concepts)

Understanding the architecture helps you use the library effectively.

### Server-Side Fingerprint

[](#server-side-fingerprint)

This is generated **immediately** on every request. It is calculated from HTTP headers and IP. **Pros**: Available instantly, cannot be blocked by ad-blockers. **Cons**: Lower entropy (uniqueness).

### Client-Side Fingerprint

[](#client-side-fingerprint)

This is generated by JavaScript relative to the browser (Canvas, Screen properties, Timezone). It is automatically sent asynchronously to the library. **Pros**: Very high unique entropy (can distinguish identical laptops). **Cons**: Requires JS, slightly delayed (exists in session after 2nd request).

Basic Usage
-----------

[](#basic-usage)

### Accessing the Fingerprint

[](#accessing-the-fingerprint)

The easiest way is to access the fingerprint via the `deviceSignature()` method in the Request object:

```
public function index(Request $request)
{
    $signature = $request->deviceSignature();

    // The server-side fingerprint (always available)
    echo $signature->serverHash;

    // The client-side fingerprint (available after JS sync)
    if ($signature->clientHash) {
        echo $signature->clientHash;
    }
}
```

### Using the Facade

[](#using-the-facade)

```
use Centamiv\AdvancedFingerprint\Facades\Fingerprint;

// Get the current fingerprint object
$signature = Fingerprint::current();
```

### Checks

[](#checks)

You can check if a fingerprint matches the current request:

```
if ($request->deviceSignature()->matches('some-stored-hash')) {
    // It's the same device
}
```

Frontend Integration
--------------------

[](#frontend-integration)

To enable the powerful client-side fingerprinting, you must include the script in your layout file.

### The Blade Directive

[](#the-blade-directive)

Add the **`@deviceFingerprintScript`** directive to your main layout, preferably directly before the closing `` tag.

```
>

    My App

    @yield('content')

    @deviceFingerprintScript

```

It automatically generates the client-side fingerprint and sends it to the server via a POST request automatically handled by the library.

### Customizing the JS

[](#customizing-the-js)

The default client JS logic is at `resources/views/vendor/advanced-fingerprint/script.blade.php`. If you want to modyfy the client fingerprint logic itself you need to publish the views:

```
php artisan vendor:publish --tag=advanced-fingerprint-views
```

Middleware Usage
----------------

[](#middleware-usage)

The package registers a middleware alias: `device.signature`.

You can apply it to specific routes to enforce device signature checks:

```
Route::middleware(['auth', 'device.signature'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
});
```

Or add it globally to your `web` middleware group in `app/Http/Kernel.php` to run on every request:

```
protected $middlewareGroups = [
    'web' => [
        // ...
        \Centamiv\AdvancedFingerprint\Middleware\CheckDeviceSignature::class,
    ],
];
```

Events &amp; Listeners
----------------------

[](#events--listeners)

The library fires `Centamiv\AdvancedFingerprint\Events\DeviceSignatureChanged` whenever a fingerprint mismatch is detected.

**Payload:**

- `$user` (Authenticatable): The logged-in user.
- `$oldSignature` (string): The signature stored in cache/session.
- `$newSignature` (string): The signature detected in the current request.

This is your hook for "Security Alerts".

---

Cookbook: Real-World Use Cases
------------------------------

[](#cookbook-real-world-use-cases)

Here are extensive examples of how to use this library in production.

### 1. Intelligent Rate Limiting (Beyond IP)

[](#1-intelligent-rate-limiting-beyond-ip)

Standard Laravel throttling limits by IP or User ID. A smart attacker can rotate IPs. With this library, you can limit by **Device Fingerprint**. Even if they switch IPs (VPN/Proxy), if the browser signals remain the same, they stay blocked.

**Implementation:**

Create a custom middleware or modify `App\Http\Kernel.php`.

```
namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Centamiv\AdvancedFingerprint\DeviceSignatureService;
use Symfony\Component\HttpFoundation\Response;

class RateLimitByDevice
{
    protected $service;

    public function __construct(DeviceSignatureService $service)
    {
        $this->service = $service;
    }

    public function handle(Request $request, Closure $next, int $maxAttempts = 5)
    {
        // Generate the fingerprint
        $fingerprint = $request->deviceSignature();

        // Use the service helper to check limits
        // This checks cache key: device_sig:{hash}
        if ($this->service->isRateLimited($fingerprint->serverHash, $maxAttempts)) {
            return response()->json([
                'message' => 'Too many requests from this device.',
                'device_id' => $fingerprint->serverHash
            ], 429);
        }

        return $next($request);
    }
}
```

Now apply this middleware to your sensitive routes (e.g., Login, Registration, Content Scraping targets).

### 2. Session Hijacking Protection

[](#2-session-hijacking-protection)

If a user logs in on their laptop, and suddenly a request comes with the same Session Cookie but from a different Browser or OS, it's highly likely their session was stolen (XSS or simplified Cookie theft).

We can listen for the `DeviceSignatureChanged` event to invalidate the session immediately.

**Step 1: Create the Listener**

```
php artisan make:listener RevokeSessionOnHijack --event=\Centamiv\AdvancedFingerprint\Events\DeviceSignatureChanged
```

**Step 2: Implement the logic**

```
// app/Listeners/RevokeSessionOnHijack.php

namespace App\Listeners;

use Centamiv\AdvancedFingerprint\Events\DeviceSignatureChanged;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;

class RevokeSessionOnHijack
{
    public function handle(DeviceSignatureChanged $event): void
    {
        // Log the security incident
        Log::warning('Session Hijacking Detected', [
            'user_id' => $event->user->id,
            'old_sig' => $event->oldSignature,
            'new_sig' => $event->newSignature,
            'ip' => request()->ip(),
        ]);

        // Logout the user to force re-authentication
        Auth::guard('web')->logout();

        // Invalidate the session
        Session::invalidate();
        Session::regenerateToken(); // Protection against CSRF

        // (Optional) Notify the user via Email
        // Mail::to($event->user)->send(new SecurityAlertMail(...));

        abort(403, 'Security violation: Device fingerprint mismatch. Please login again.');
    }
}
```

**Step 3: Register in EventServiceProvider**

In `app/Providers/EventServiceProvider.php`:

```
protected $listen = [
    \Centamiv\AdvancedFingerprint\Events\DeviceSignatureChanged::class => [
        \App\Listeners\RevokeSessionOnHijack::class,
    ],
];
```

Ensure `anomaly_detection.enabled` is `true` in `config/fingerprint.php`.

### 3. Device Content Locking (Paywalls)

[](#3-device-content-locking-paywalls)

Imagine a news site or streaming service that allows "3 free articles per device per month". Using IP is bad (shared Wi-Fi or NAT networking blocks users because they share the same IP). Cookies are cleared by users. Device fingerprinting is persistent.

```
// app/Http/Controllers/ArticleController.php

public function show($id)
{
    $request = request();
    $fingerprint = $request->deviceSignature()->serverHash;

    // Check database for usage
    $usage = \DB::table('device_usage')
                ->where('device_hash', $fingerprint)
                ->where('month', now()->format('Y-m'))
                ->first();

    if ($usage && $usage->view_count >= 3) {
        // If not subscribed, block
        if (! $request->user()?->isSubscribed()) {
            return view('paywall.limit_reached');
        }
    }

    // Increment usage
    \DB::table('device_usage')->updateOrInsert(
        ['device_hash' => $fingerprint, 'month' => now()->format('Y-m')],
        ['view_count' => \DB::raw('view_count + 1'), 'updated_at' => now()]
    );

    return view('articles.show', ['article' => Article::find($id)]);
}
```

### 4. Tracking User Devices History

[](#4-tracking-user-devices-history)

Show users a list of "Devices where you are logged in", similar to Google or Facebook security dashboards.

**Step 1: Create a table**

```
php artisan make:migration create_user_devices_table
```

```
Schema::create('user_devices', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained();
    $table->string('device_fingerprint'); // The hash
    $table->string('ip_address');
    $table->string('user_agent');
    $table->timestamp('last_active_at');
    $table->timestamps();
});
```

**Step 2: Update the table on login/activity**

You can use a middleware for this, or hook into the `Login` event.

```
// app/Listeners/LogDeviceOnLogin.php

public function handle(Login $event)
{
    $fingerprint = request()->deviceSignature()->serverHash;

    $device = \App\Models\UserDevice::updateOrCreate(
        [
            'user_id' => $event->user->id,
            'device_fingerprint' => $fingerprint
        ],
        [
            'ip_address' => request()->ip(),
            'user_agent' => request()->header('User-Agent'),
            'last_active_at' => now(),
        ]
    );
}
```

**Step 3: Show to User**

In your User Profile controller:

```
public function devices()
{
    $devices = Auth::user()->devices()->orderByDesc('last_active_at')->get();

    return view('profile.devices', compact('devices'));
}
```

Deep Dive: Inner Workings
-------------------------

[](#deep-dive-inner-workings)

This section details how the fingerprint generation actually works, which is useful for security auditing.

### Entropy Sources

[](#entropy-sources)

Entropy is the measure of randomness or uniqueness. A higher entropy means a lower chance of collision (two different devices having the same fingerprint).

**1. Server-Side Entropy (Low - Medium Reliability)**

- `IP Address`: Very specific, but changes with networks (Wi-Fi vs 4G).
- `User-Agent`: Describes the browser version and OS. Chrome freezes this string to improve privacy, making it less useful.
- `Accept-Language`: Can reveal the user's preferred language and region (e.g., `en-US,en;q=0.9,it;q=0.8`).
- `Sec-CH-UA-*`: Modern replacements for User-Agent. They provide the platform (Windows, Android), architecture, and mobile status.

**2. Client-Side Entropy (High Reliability)**

- `Canvas`: The script draws a hidden text with specific emoji/styles on a HTML5 Canvas. Different Graphics Cards (GPU) and Drivers render fonts and anti-aliasing slightly differently. By exporting the image data (base64) and hashing it, we get a signature unique to the hardware stack.
- `Hardware Concurrency`: Number of logical processors.
- `Color Depth`: e.g., 24-bit.
- `Timezone`: The user's local timezone.

All these values are hashed using SHA-256 by default.

### Canvas Fingerprinting Explained

[](#canvas-fingerprinting-explained)

Canvas fingerprinting is one of the most reliable entropy sources because it exploits the hardware-level differences in how graphics are rendered.

1. **Instruction**: The library instructs the browser to draw a hidden 2D image containing specific text, emojis, and gradients using the HTML5 `` API.
2. **Rendering**: The browser uses the operating system's font-rendering engine and the local GPU's anti-aliasing algorithms to draw the image.
3. **Variation**: Subtle differences in graphics drivers, hardware architecture, and OS-level sub-pixel rendering mean that the exact pixel data of the resulting image is unique to that specific hardware/software stack.
4. **Hashing**: This raw pixel data is converted into a hash. Unlike cookies, this identifier is persistent and remains the same even if the user clears their browsing data or uses "Incognito" mode, as it is tied to the machine's physical capabilities.

Testing Your Application
------------------------

[](#testing-your-application)

When writing Feature tests for your Laravel application, you might encounter issues if your tests expect real fingerprinting data (especially headers that aren't present in testing environments).

### Mocking the Signature

[](#mocking-the-signature)

You should mock the `DeviceSignatureService` in your tests to return a predictable hash.

```
namespace Tests\Feature;

use Tests\TestCase;
use Centamiv\AdvancedFingerprint\DeviceSignatureService;

class LoginTest extends TestCase
{
    public function test_user_login_with_fingerprint()
    {
        // Mock the service
        $this->mock(DeviceSignatureService::class, function ($mock) {
            $mock->shouldReceive('generate')
                 ->andReturn('dummy-test-hash-123');

             // If you use checkAnomaly, mock that too
             $mock->shouldReceive('checkAnomaly')->andReturn(null);
        });

        $response = $this->post('/login', [
            'email' => 'test@example.com',
            'password' => 'password',
        ]);

        $response->assertRedirect('/home');
    }
}
```

### Testing Middleware

[](#testing-middleware)

If you use the `device.signature` middleware, you can disable it in tests if it gets in the way:

```
$this->withoutMiddleware([\Centamiv\AdvancedFingerprint\Middleware\CheckDeviceSignature::class]);
```

However, it is better to ensure your request headers in tests mimic a real browser to verify the integration properly.

API Reference
-------------

[](#api-reference)

### DeviceSignatureService

[](#devicesignatureservice)

The core singleton class responsible for logic.

**`generate(Request $request): string`**Generates the server-side SHA-256 hash based on the current configuration.

**`checkAnomaly(Authenticatable $user, string $currentSignature): void`**Compares the current signature with the one cached for the user. Fires `DeviceSignatureChanged` if different.

**`isRateLimited(string $signature, int $maxAttempts = 60): bool`**Atomic rate limiter using Cache. Returns `true` if limit exceeded.

### DeviceSignature Class

[](#devicesignature-class)

A simple value object holding the state.

**`public string $serverHash`**The hash calculated from HTTP headers.

**`public ?string $clientHash`**The hash calculated from JS (Canvas). Null if not yet synced.

**`matches(string $hash): bool`**Returns `true` if `$serverHash` equals the provided string.

Troubleshooting
---------------

[](#troubleshooting)

### "The Client Hash is always null"

[](#the-client-hash-is-always-null)

- Ensure you included `@deviceFingerprintScript` in your layout.
- The client hash is sent via AJAX *after* the page loads. It will not be available in the initial `GET` request of the page load. It will be available in subsequent requests (AJAX or navigation).
- Check your browser console for "Fingerprint Sync Failed" errors.
- Ensure your `route('fingerprint.sync')` is accessible and not blocked by other middleware. Only the `web` middleware group is applied by default.

### "My Rate Limiting is blocking everyone on the same Wi-Fi"

[](#my-rate-limiting-is-blocking-everyone-on-the-same-wi-fi)

- You have `'ip' => true` in your config.
- If you want to distinguish devices behind a NAT (e.g., Corporate Office), set `'ip' => false` and rely on Header/Canvas entropy.
- **Warning**: Removing IP reduces security against attackers who can simulate headers. It's a trade-off.

### Privacy &amp; GDPR

[](#privacy--gdpr)

- Device Fingerprinting can be considered "Tracking" under GDPR/ePrivacy.
- You **must** disclose this in your Privacy Policy.
- If used strictly for "Security" (Fraud detection, Account protection), it is generally a "Legitimate Interest".
- If used for "Analytics" or "Marketing", you likely need Consent (Cookie Banner).

Extending the Library
---------------------

[](#extending-the-library)

### Custom Generators

[](#custom-generators)

Currently, the generator logic is inside `DeviceSignatureService`. If you need to add custom logic (e.g., checking specific custom headers passed by your mobile app), you can extend the service and re-bind it.

```
// App\Services\MyCustomSignatureService.php
namespace App\Services;

use Centamiv\AdvancedFingerprint\DeviceSignatureService;
use Illuminate\Http\Request;

class MyCustomSignatureService extends DeviceSignatureService
{
    public function generate(Request $request): string
    {
        // Call parent or write complete custom logic
        $hash = parent::generate($request);

        // Add a custom header to the mix
        if ($request->hasHeader('X-My-App-ID')) {
            $hash = hash('sha256', $hash . $request->header('X-My-App-ID'));
        }

        return $hash;
    }
}
```

In your `AppServiceProvider`:

```
$this->app->singleton(\Centamiv\AdvancedFingerprint\DeviceSignatureService::class, \App\Services\MyCustomSignatureService::class);
```

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

---

*Built with ❤️ by Ivan Centamori*

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance52

Moderate activity, may be stable

Popularity3

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/4bb6e94628f1de489fb3f7114991eef0c4d84f8b42d507cadc2bd54fefc75091?d=identicon)[centamiv](/maintainers/centamiv)

---

Top Contributors

[![centamiv](https://avatars.githubusercontent.com/u/4162703?v=4)](https://github.com/centamiv "centamiv (1 commits)")

### Embed Badge

![Health badge](/badges/centamiv-advanced-fingerprint/health.svg)

```
[![Health](https://phpackages.com/badges/centamiv-advanced-fingerprint/health.svg)](https://phpackages.com/packages/centamiv-advanced-fingerprint)
```

###  Alternatives

[namshi/jose

JSON Object Signing and Encryption library for PHP.

1.8k99.6M101](/packages/namshi-jose)[league/oauth1-client

OAuth 1.0 Client Library

99698.8M106](/packages/league-oauth1-client)[bezhansalleh/filament-shield

Filament support for `spatie/laravel-permission`.

2.8k2.9M88](/packages/bezhansalleh-filament-shield)[gesdinet/jwt-refresh-token-bundle

Implements a refresh token system over Json Web Tokens in Symfony

70516.4M35](/packages/gesdinet-jwt-refresh-token-bundle)[league/oauth2-google

Google OAuth 2.0 Client Provider for The PHP League OAuth2-Client

41721.2M118](/packages/league-oauth2-google)[illuminate/auth

The Illuminate Auth package.

9327.3M1.0k](/packages/illuminate-auth)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
