Автар dex35
dex35
Разработка, 3d печать, игры

Fingerprint как средство повторной идентификации

Обложка публикации

Каждая система аналитики или рекламы должна достаточно точно идентифицировать своих пользователей для более корректного сбора данных или показа релевантной рекламы, именно для этого используют fingerprint (далее отпечаток).

У каждого сервиса реализованы свои решения формирования отпечатка, а для большей точности генерируют клиентский и серверный.

Клиентский отпечаток в вебе, формируется используя javascript на основе информации о браузере, а пересечения сокращают, например, за счет использования canvas api - браузеры немного по разному рендерят рисуемое изображение и это добавляет уникальности.

Как пример, покажу упрощенный вариант генерации отпечатка в браузере:

async function generateFingerprint() {
    const data = [
        navigator.userAgent,
        navigator.language,
        navigator.platform,
        screen.width,
        screen.height
    ].join('#');
    
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    ctx.textBaseline = 'top';
    ctx.font = '13px Arial';
    ctx.textBaseline = 'alphabetic';
    ctx.fillStyle = '#A480DD';
    ctx.fillText('fingerprint', 2, 15);
    ctx.fillStyle = '#8957CA';
    ctx.fillText('fingerprint', 1, 16);
    const canvasData = canvas.toDataURL();
    
    const raw = `${data}#${canvasData}`;
    
    const buffer = (new TextEncoder()).encode(raw);
    const hash = await crypto.subtle.digest('SHA-256', buffer);
    const hashArray = Array.from(new Uint8Array(hash));

    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

Серверный отпечаток формируется из параметров доступных на сервере, например самый простой отпечаток, можно сформировать из ip адреса и http заголовков.

Упрощенный пример генерации отпечатка в middleware для koa:

import * as crypto from 'node:crypto';

async function fingerprintMiddleware(ctx: Context, next: Next) {  
    const forwardedIp = ctx.request.headers['x-forwarded-for'];  
    const userIp = forwardedIp || ctx.request.ip;  
    const userAgent = ctx.request.headers['user-agent'];  
    const acceptLanguage = ctx.request.headers['accept-language'];
    
    const raw = [ userIp, userAgent, acceptLanguage ].join('#');
    
    ctx.state.fingerprint = crypto  
        .createHash('sha256')  
        .update(raw)  
        .digest('hex');
    
    await next();  
}

Естественно, отпечатком все не ограничивается и после первого обращения к целевому сервису формируется уникальный идентификатор и записывается в cookie клиента на продолжительный срок, ожидая все последующие запросы к сервису с этой cookie. Если cookie для клиента не существует, снова создается fingerprint по тем же правилам, именно так системы определяют возвраты пользователей, если cookie протухла или была стерта.

p.s.: И для клиентского и для серверного отпечатка существуют готовые решения, можете поискать самостоятельно, если интересно.

p.s.s.: Существуют и готовые решения позволяющие обманывать системы, подкидывая рандомные данные для генерации отпечатка.