Reconnaissance de l'utilisateur sans cookies ni stockage local


132

Je suis en train de créer un outil d'analyse et je peux actuellement obtenir l'adresse IP, le navigateur et le système d'exploitation de l'utilisateur auprès de son agent utilisateur.

Je me demande s'il est possible de détecter le même utilisateur sans utiliser de cookies ou de stockage local? Je ne m'attends pas à des exemples de code ici; juste un simple indice pour savoir où chercher plus loin.

J'ai oublié de mentionner qu'il devrait être compatible avec tous les navigateurs s'il s'agit du même ordinateur / appareil. Fondamentalement, je suis après la reconnaissance de l'appareil, pas vraiment l'utilisateur.


5
Pas vraiment - du moins pas d'une manière sur laquelle vous pourriez vous fier pour être précis. Peut-être un hachage des trois combinés, mais si plus d'une personne dans une maison utilise le même navigateur et le même système d'exploitation, cela ne fonctionnera toujours pas. En outre, la plupart des FAI fournissent des adresses IP dynamiques, ce qui signifie qu'elles changent de temps en temps et ne pourront pas non plus être invoquées à des fins d'identification.
Jon

2
Alors vous ne savez pas ce que sont les sessions. Votre cas d'utilisation est exactement ce pour quoi les sessions ont été conçues. Les sessions n'ont rien à voir avec la connexion ou l'authentification. Votre serveur Web demandera à un client d'envoyer un cookie avec un identifiant de session. Vous identifiez ce client à l'aide de l'ID de session qu'il vous envoie.
Man Vs Code

4
Les cookies fonctionneraient toujours? Pourquoi évitez-vous d'utiliser des cookies?
Baba

2
C'est vraiment simple et je l'utilise tout le temps, demandez à l'utilisateur de saisir un nom d'utilisateur et un mot de passe !!!
Amit Kriplani

2
Voici une solution javascript minimale (non cross-browser dans ce cas): github.com/carlo/jquery-browser-fingerprint Je le mentionne, car cela m'a amené à l'idée que de nombreux plugins sont installés cross-browser par défaut, sans tout choix de la part de l'utilisateur. Les trier soigneusement (ce qui n'est pas une petite tâche, mais quand même ...) pourrait potentiellement conduire à une propriété tangible indépendante du navigateur d'une empreinte digitale plus grande basée sur un appareil.
hexalys

Réponses:


389

introduction

Si je vous comprends bien, vous devez identifier un utilisateur pour lequel vous n'avez pas d'identifiant unique, vous voulez donc savoir qui il est en faisant correspondre des données aléatoires. Vous ne pouvez pas stocker l'identité de l'utilisateur de manière fiable car:

  • Les cookies peuvent être supprimés
  • L'adresse IP peut changer
  • Le navigateur peut changer
  • Le cache du navigateur peut être supprimé

Une applet Java ou un objet Com aurait été une solution simple en utilisant un hachage d'informations matérielles, mais de nos jours, les gens sont tellement conscients de la sécurité qu'il serait difficile d'amener les gens à installer ce type de programmes sur leur système. Cela vous laisse coincé avec l'utilisation de cookies et d'autres outils similaires.

Cookies et autres outils similaires

Vous pouvez envisager de créer un profil de données, puis d'utiliser des tests de probabilité pour identifier un utilisateur probable . Un profil utile pour cela peut être généré par une combinaison des éléments suivants:

  1. Adresse IP
    • Adresse IP réelle
    • Adresse IP du proxy (les utilisateurs utilisent souvent le même proxy à plusieurs reprises)
  2. Biscuits
  3. Bogues Web (moins fiables car les bogues sont corrigés, mais toujours utiles)
    • Bug PDF
    • Bug Flash
    • Bogue Java
  4. Navigateurs
    • Suivi des clics (de nombreux utilisateurs visitent la même série de pages à chaque visite)
    • Browsers Finger Print - Plugins installés (les gens ont souvent des ensembles de plugins variés et quelque peu uniques)
    • Images en cache (les gens suppriment parfois leurs cookies mais laissent des images en cache)
    • Utilisation de blobs
    • URL (s) (l'historique du navigateur ou les cookies peuvent contenir des identifiants d'utilisateur uniques dans les URL, telles que https://stackoverflow.com/users/1226894 ou http://www.facebook.com/barackobama?fref=ts )
    • Détection des polices système (il s'agit d'une signature de clé peu connue mais souvent unique)
  5. HTML5 et Javascript
    • HTML5 LocalStorage
    • API de géolocalisation HTML5 et géocodage inversé
    • Architecture, langue du système d'exploitation, heure du système, résolution de l'écran, etc.
    • API d'informations sur le réseau
    • API d'état de la batterie

Les éléments que j'ai énumérés ne sont, bien sûr, que quelques façons possibles d'identifier un utilisateur de manière unique. Il y en a beaucoup plus.

Avec cet ensemble d'éléments de données aléatoires à partir desquels créer un profil de données, quelle est la prochaine étape?

L'étape suivante consiste à développer une logique floue , ou, mieux encore, un réseau neuronal artificiel (qui utilise la logique floue). Dans les deux cas, l'idée est de former votre système, puis de combiner son entraînement avec l'inférence bayésienne pour augmenter la précision de vos résultats.

Réseau neuronal artificiel

La bibliothèque NeuralMesh pour PHP vous permet de générer des réseaux de neurones artificiels. Pour implémenter l'inférence bayésienne, consultez les liens suivants:

À ce stade, vous pensez peut-être:

Pourquoi tant de mathématiques et de logique pour une tâche apparemment simple?

Fondamentalement, parce que ce n'est pas une tâche simple . Ce que vous essayez d'atteindre, c'est en fait la pure probabilité . Par exemple, compte tenu des utilisateurs connus suivants:

User1 = A + B + C + D + G + K
User2 = C + D + I + J + K + F

Lorsque vous recevez les données suivantes:

B + C + E + G + F + K

La question que vous vous posez essentiellement est:

Quelle est la probabilité que les données reçues (B + C + E + G + F + K) soient réellement User1 ou User2? Et laquelle de ces deux correspondances est la plus probable?

Afin de répondre efficacement à cette question, vous devez comprendre le format Fréquence vs Probabilité et pourquoi la probabilité conjointe pourrait être une meilleure approche. Les détails sont trop difficiles à entrer ici (c'est pourquoi je vous donne des liens), mais un bon exemple serait une application d'assistant de diagnostic médical , qui utilise une combinaison de symptômes pour identifier les maladies possibles.

Pensez un instant à la série de points de données qui composent votre profil de données (B + C + E + G + F + K dans l'exemple ci-dessus) en tant que symptômes et Utilisateurs inconnus en tant que maladies . En identifiant la maladie, vous pouvez en outre identifier un traitement approprié (traiter cet utilisateur comme Utilisateur1).

De toute évidence, une maladie pour laquelle nous avons identifié plus d'un symptôme est plus facile à identifier. En fait, plus nous pouvons identifier de symptômes , plus notre diagnostic est presque certain et précis.

Y a-t-il d'autres alternatives?

Bien sûr. Comme mesure alternative, vous pouvez créer votre propre algorithme de notation simple et le baser sur des correspondances exactes. Ce n'est pas aussi efficace que la probabilité, mais peut être plus simple à mettre en œuvre.

À titre d'exemple, considérons ce graphique de score simple:

+ ------------------------- + -------- + ------------ +
| Propriété | Poids | Importance |
+ ------------------------- + -------- + ------------ +
| Adresse IP réelle | 60 | 5 |
| Adresse IP proxy utilisée | 40 | 4 |
| Cookies HTTP | 80 | 8 |
| Cookies de session | 80 | 6 |
| Cookies tiers | 60 | 4 |
| Cookies Flash | 90 | 7 |
| Bug PDF | 20 | 1 |
| Flash Bug | 20 | 1 |
| Bug Java | 20 | 1 |
| Pages fréquentes | 40 | 1 |
| Navigateurs Empreinte digitale | 35 | 2 |
| Plugins installés | 25 | 1 |
| Images en cache | 40 | 3 |
| URL | 60 | 4 |
| Détection des polices système | 70 | 4 |
| Localstorage | 90 | 8 |
| Géolocalisation | 70 | 6 |
| AOLTR | 70 | 4 |
| API d'informations réseau | 40 | 3 |
| API d'état de la batterie | 20 | 1 |
+ ------------------------- + -------- + ------------ +

Pour chaque information que vous pouvez collecter sur une demande donnée, attribuez le score associé, puis utilisez Importance pour résoudre les conflits lorsque les scores sont identiques.

Preuve de concept

Pour une simple preuve de concept, jetez un œil à Perceptron . Perceptron est un modèle d'ARN qui est généralement utilisé dans les applications de reconnaissance de formes. Il existe même une ancienne classe PHP qui l'implémente parfaitement, mais vous devrez probablement la modifier pour vos besoins.

Bien qu'il s'agisse d'un excellent outil, Perceptron peut toujours renvoyer plusieurs résultats (correspondances possibles), il est donc toujours utile d'utiliser une comparaison de score et de différence pour identifier le meilleur de ces correspondances.

Hypothèses

  • Stocker toutes les informations possibles sur chaque utilisateur (IP, cookies, etc.)
  • Lorsque le résultat est une correspondance exacte, augmentez le score de 1
  • Lorsque le résultat n'est pas une correspondance exacte, diminuez le score de 1

Attente

  1. Générer des étiquettes ARN
  2. Générer des utilisateurs aléatoires émulant une base de données
  3. Générer un seul utilisateur inconnu
  4. Générer un ARN et des valeurs utilisateur inconnus
  5. Le système fusionnera les informations d'ARN et apprendra au Perceptron
  6. Après avoir entraîné le Perceptron, le système aura un ensemble de pondérations
  7. Vous pouvez maintenant tester le modèle de l'utilisateur inconnu et le Perceptron produira un jeu de résultats.
  8. Stocker toutes les correspondances positives
  9. Trier les correspondances d'abord par score, puis par différence (comme décrit ci-dessus)
  10. Affiche les deux correspondances les plus proches ou, si aucune correspondance n'est trouvée, affiche des résultats vides

Code de preuve de concept

$features = array(
    'Real IP address' => .5,
    'Used proxy IP address' => .4,
    'HTTP Cookies' => .9,
    'Session Cookies' => .6,
    '3rd Party Cookies' => .6,
    'Flash Cookies' => .7,
    'PDF Bug' => .2,
    'Flash Bug' => .2,
    'Java Bug' => .2,
    'Frequent Pages' => .3,
    'Browsers Finger Print' => .3,
    'Installed Plugins' => .2,
    'URL' => .5,
    'Cached PNG' => .4,
    'System Fonts Detection' => .6,
    'Localstorage' => .8,
    'Geolocation' => .6,
    'AOLTR' => .4,
    'Network Information API' => .3,
    'Battery Status API' => .2
);

// Get RNA Lables
$labels = array();
$n = 1;
foreach ($features as $k => $v) {
    $labels[$k] = "x" . $n;
    $n ++;
}

// Create Users
$users = array();
for($i = 0, $name = "A"; $i < 5; $i ++, $name ++) {
    $users[] = new Profile($name, $features);
}

// Generate Unknown User
$unknown = new Profile("Unknown", $features);

// Generate Unknown RNA
$unknownRNA = array(
    0 => array("o" => 1),
    1 => array("o" => - 1)
);

// Create RNA Values
foreach ($unknown->data as $item => $point) {
    $unknownRNA[0][$labels[$item]] = $point;
    $unknownRNA[1][$labels[$item]] = (- 1 * $point);
}

// Start Perception Class
$perceptron = new Perceptron();

// Train Results
$trainResult = $perceptron->train($unknownRNA, 1, 1);

// Find matches
foreach ($users as $name => &$profile) {
    // Use shorter labels
    $data = array_combine($labels, $profile->data);
    if ($perceptron->testCase($data, $trainResult) == true) {
        $score = $diff = 0;

        // Determing the score and diffrennce
        foreach ($unknown->data as $item => $found) {
            if ($unknown->data[$item] === $profile->data[$item]) {
                if ($profile->data[$item] > 0) {
                    $score += $features[$item];
                } else {
                    $diff += $features[$item];
                }
            }
        }
        // Ser score and diff
        $profile->setScore($score, $diff);
        $matchs[] = $profile;
    }
}

// Sort bases on score and Output
if (count($matchs) > 1) {
    usort($matchs, function ($a, $b) {
        // If score is the same use diffrence
        if ($a->score == $b->score) {
            // Lower the diffrence the better
            return $a->diff == $b->diff ? 0 : ($a->diff > $b->diff ? 1 : - 1);
        }
        // The higher the score the better
        return $a->score > $b->score ? - 1 : 1;
    });

    echo "<br />Possible Match ", implode(",", array_slice(array_map(function ($v) {
        return sprintf(" %s (%0.4f|%0.4f) ", $v->name, $v->score,$v->diff);
    }, $matchs), 0, 2));
} else {
    echo "<br />No match Found ";
}

Production:

Possible Match D (0.7416|0.16853),C (0.5393|0.2809)

Print_r de "D":

echo "<pre>";
print_r($matchs[0]);


Profile Object(
    [name] => D
    [data] => Array (
        [Real IP address] => -1
        [Used proxy IP address] => -1
        [HTTP Cookies] => 1
        [Session Cookies] => 1
        [3rd Party Cookies] => 1
        [Flash Cookies] => 1
        [PDF Bug] => 1
        [Flash Bug] => 1
        [Java Bug] => -1
        [Frequent Pages] => 1
        [Browsers Finger Print] => -1
        [Installed Plugins] => 1
        [URL] => -1
        [Cached PNG] => 1
        [System Fonts Detection] => 1
        [Localstorage] => -1
        [Geolocation] => -1
        [AOLTR] => 1
        [Network Information API] => -1
        [Battery Status API] => -1
    )
    [score] => 0.74157303370787
    [diff] => 0.1685393258427
    [base] => 8.9
)

Si Debug = true, vous pourrez voir l' entrée (capteur et souhaité), les poids initiaux, la sortie (capteur, somme, réseau), l'erreur, la correction et les poids finaux .

+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| o  | x1 | x2 | x3 | x4 | x5 | x6 | x7 | x8 | x9 | x10 | x11 | x12 | x13 | x14 | x15 | x16 | x17 | x18 | x19 | x20 | Bias | Yin | Y  | deltaW1 | deltaW2 | deltaW3 | deltaW4 | deltaW5 | deltaW6 | deltaW7 | deltaW8 | deltaW9 | deltaW10 | deltaW11 | deltaW12 | deltaW13 | deltaW14 | deltaW15 | deltaW16 | deltaW17 | deltaW18 | deltaW19 | deltaW20 | W1 | W2 | W3 | W4 | W5 | W6 | W7 | W8 | W9 | W10 | W11 | W12 | W13 | W14 | W15 | W16 | W17 | W18 | W19 | W20 | deltaBias |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 1  | 1  | -1 | -1 | -1 | -1 | -1 | -1 | 1  | 1  | 1   | 1   | 1   | 1   | 1   | -1  | -1  | -1  | -1  | 1   | 1   | 1    | 0   | -1 | 0       | -1      | -1      | -1      | -1      | -1      | -1      | 1       | 1       | 1        | 1        | 1        | 1        | 1        | -1       | -1       | -1       | -1       | 1        | 1        | 0  | -1 | -1 | -1 | -1 | -1 | -1 | 1  | 1  | 1   | 1   | 1   | 1   | 1   | -1  | -1  | -1  | -1  | 1   | 1   | 1         |
| -1 | -1 | 1  | 1  | 1  | 1  | 1  | 1  | -1 | -1 | -1  | -1  | -1  | -1  | -1  | 1   | 1   | 1   | 1   | -1  | -1  | 1    | -19 | -1 | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0  | -1 | -1 | -1 | -1 | -1 | -1 | 1  | 1  | 1   | 1   | 1   | 1   | 1   | -1  | -1  | -1  | -1  | 1   | 1   | 1         |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --   | --  | -- | --      | --      | --      | --      | --      | --      | --      | --      | --      | --       | --       | --       | --       | --       | --       | --       | --       | --       | --       | --       | -- | -- | -- | -- | -- | -- | -- | -- | -- | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --        |
| 1  | 1  | -1 | -1 | -1 | -1 | -1 | -1 | 1  | 1  | 1   | 1   | 1   | 1   | 1   | -1  | -1  | -1  | -1  | 1   | 1   | 1    | 19  | 1  | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0  | -1 | -1 | -1 | -1 | -1 | -1 | 1  | 1  | 1   | 1   | 1   | 1   | 1   | -1  | -1  | -1  | -1  | 1   | 1   | 1         |
| -1 | -1 | 1  | 1  | 1  | 1  | 1  | 1  | -1 | -1 | -1  | -1  | -1  | -1  | -1  | 1   | 1   | 1   | 1   | -1  | -1  | 1    | -19 | -1 | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0       | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0        | 0  | -1 | -1 | -1 | -1 | -1 | -1 | 1  | 1  | 1   | 1   | 1   | 1   | 1   | -1  | -1  | -1  | -1  | 1   | 1   | 1         |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --   | --  | -- | --      | --      | --      | --      | --      | --      | --      | --      | --      | --       | --       | --       | --       | --       | --       | --       | --       | --       | --       | --       | -- | -- | -- | -- | -- | -- | -- | -- | -- | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --  | --        |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+

x1 à x20 représentent les caractéristiques converties par le code.

// Get RNA Labels
$labels = array();
$n = 1;
foreach ( $features as $k => $v ) {
    $labels[$k] = "x" . $n;
    $n ++;
}

Voici une démo en ligne

Classe utilisée:

class Profile {
    public $name, $data = array(), $score, $diff, $base;

    function __construct($name, array $importance) {
        $values = array(-1, 1); // Perception values
        $this->name = $name;
        foreach ($importance as $item => $point) {
            // Generate Random true/false for real Items
            $this->data[$item] = $values[mt_rand(0, 1)];
        }
        $this->base = array_sum($importance);
    }

    public function setScore($score, $diff) {
        $this->score = $score / $this->base;
        $this->diff = $diff / $this->base;
    }
}

Classe Perceptron modifiée

class Perceptron {
    private $w = array();
    private $dw = array();
    public $debug = false;

    private function initialize($colums) {
        // Initialize perceptron vars
        for($i = 1; $i <= $colums; $i ++) {
            // weighting vars
            $this->w[$i] = 0;
            $this->dw[$i] = 0;
        }
    }

    function train($input, $alpha, $teta) {
        $colums = count($input[0]) - 1;
        $weightCache = array_fill(1, $colums, 0);
        $checkpoints = array();
        $keepTrainning = true;

        // Initialize RNA vars
        $this->initialize(count($input[0]) - 1);
        $just_started = true;
        $totalRun = 0;
        $yin = 0;

        // Trains RNA until it gets stable
        while ($keepTrainning == true) {
            // Sweeps each row of the input subject
            foreach ($input as $row_counter => $row_data) {
                // Finds out the number of columns the input has
                $n_columns = count($row_data) - 1;

                // Calculates Yin
                $yin = 0;
                for($i = 1; $i <= $n_columns; $i ++) {
                    $yin += $row_data["x" . $i] * $weightCache[$i];
                }

                // Calculates Real Output
                $Y = ($yin <= 1) ? - 1 : 1;

                // Sweeps columns ...
                $checkpoints[$row_counter] = 0;
                for($i = 1; $i <= $n_columns; $i ++) {
                    /** DELTAS **/
                    // Is it the first row?
                    if ($just_started == true) {
                        $this->dw[$i] = $weightCache[$i];
                        $just_started = false;
                        // Found desired output?
                    } elseif ($Y == $row_data["o"]) {
                        $this->dw[$i] = 0;
                        // Calculates Delta Ws
                    } else {
                        $this->dw[$i] = $row_data["x" . $i] * $row_data["o"];
                    }

                    /** WEIGHTS **/
                    // Calculate Weights
                    $this->w[$i] = $this->dw[$i] + $weightCache[$i];
                    $weightCache[$i] = $this->w[$i];

                    /** CHECK-POINT **/
                    $checkpoints[$row_counter] += $this->w[$i];
                } // END - for

                foreach ($this->w as $index => $w_item) {
                    $debug_w["W" . $index] = $w_item;
                    $debug_dw["deltaW" . $index] = $this->dw[$index];
                }

                // Special for script debugging
                $debug_vars[] = array_merge($row_data, array(
                    "Bias" => 1,
                    "Yin" => $yin,
                    "Y" => $Y
                ), $debug_dw, $debug_w, array(
                    "deltaBias" => 1
                ));
            } // END - foreach

            // Special for script debugging
             $empty_data_row = array();
            for($i = 1; $i <= $n_columns; $i ++) {
                $empty_data_row["x" . $i] = "--";
                $empty_data_row["W" . $i] = "--";
                $empty_data_row["deltaW" . $i] = "--";
            }
            $debug_vars[] = array_merge($empty_data_row, array(
                "o" => "--",
                "Bias" => "--",
                "Yin" => "--",
                "Y" => "--",
                "deltaBias" => "--"
            ));

            // Counts training times
            $totalRun ++;

            // Now checks if the RNA is stable already
            $referer_value = end($checkpoints);
            // if all rows match the desired output ...
            $sum = array_sum($checkpoints);
            $n_rows = count($checkpoints);
            if ($totalRun > 1 && ($sum / $n_rows) == $referer_value) {
                $keepTrainning = false;
            }
        } // END - while

        // Prepares the final result
        $result = array();
        for($i = 1; $i <= $n_columns; $i ++) {
            $result["w" . $i] = $this->w[$i];
        }

        $this->debug($this->print_html_table($debug_vars));

        return $result;
    } // END - train
    function testCase($input, $results) {
        // Sweeps input columns
        $result = 0;
        $i = 1;
        foreach ($input as $column_value) {
            // Calculates teste Y
            $result += $results["w" . $i] * $column_value;
            $i ++;
        }
        // Checks in each class the test fits
        return ($result > 0) ? true : false;
    } // END - test_class

    // Returns the html code of a html table base on a hash array
    function print_html_table($array) {
        $html = "";
        $inner_html = "";
        $table_header_composed = false;
        $table_header = array();

        // Builds table contents
        foreach ($array as $array_item) {
            $inner_html .= "<tr>\n";
            foreach ( $array_item as $array_col_label => $array_col ) {
                $inner_html .= "<td>\n";
                $inner_html .= $array_col;
                $inner_html .= "</td>\n";

                if ($table_header_composed == false) {
                    $table_header[] = $array_col_label;
                }
            }
            $table_header_composed = true;
            $inner_html .= "</tr>\n";
        }

        // Builds full table
        $html = "<table border=1>\n";
        $html .= "<tr>\n";
        foreach ($table_header as $table_header_item) {
            $html .= "<td>\n";
            $html .= "<b>" . $table_header_item . "</b>";
            $html .= "</td>\n";
        }
        $html .= "</tr>\n";

        $html .= $inner_html . "</table>";

        return $html;
    } // END - print_html_table

    // Debug function
    function debug($message) {
        if ($this->debug == true) {
            echo "<b>DEBUG:</b> $message";
        }
    } // END - debug
} // END - class

Conclusion

Identifier un utilisateur sans identifiant unique n'est pas une tâche simple ou directe. il dépend de la collecte d'une quantité suffisante de données aléatoires que vous pouvez collecter auprès de l'utilisateur par diverses méthodes.

Même si vous choisissez de ne pas utiliser de réseau neuronal artificiel, je suggère au moins d'utiliser une matrice de probabilité simple avec des priorités et des probabilités - et j'espère que le code et les exemples fournis ci-dessus vous en donneront assez pour continuer.


@Baba Qu'entendez-vous par "Utilisation de Blobs" pour empreinte d'un navigateur?
billmalarky


1
@Baba Comment utiliserait-on cela pour empreinte d'un navigateur? Il suffit de vérifier ce qu'il contient actuellement à un moment donné?
billmalarky

@Baba excellent travail, j'ai toujours essayé d'avoir une stratégie à plusieurs niveaux pour identifier un utilisateur, mais comme vous l'avez dit, le cache peut être effacé, les adresses IP modifiées, les utilisateurs derrière des proxys ou NAT - en particulier ces personnes -, les cookies supprimés, etc. Mais même avec tous ces efforts, c'est une question de probabilité, même si le mauvais utilisateur utilise le navigateur Tor par exemple, la plupart sinon toutes les stratégies de détection mentionnées ne fonctionneront pas. J'ai aimé browserleaks.com mais avec Tor, tout est revenu indéfini ou inconnu
Mi-Creativity

Juste une note destinée uniquement à "dépoussiérer" ce bijou d'une publication: Liste des liens cassés au 07.09.17: - Implement Bayesian inference using PHP, les 3 parties. - Frequency vs Probability - Joint Probability -Input (Sensor & Desired), Initial Weights, Output (Sensor, Sum, Network), Error, Correction and Final Weights
Ziezi

28

Cette technique (pour détecter les mêmes utilisateurs sans cookies - ou même sans adresse IP) s'appelle l' empreinte du navigateur . En gros, vous explorez autant d'informations que vous le pouvez sur le navigateur - de meilleurs résultats peuvent être obtenus avec javascript, flash ou java (par exemple, les extensions installées, les polices, etc.). Après cela, vous pouvez stocker les résultats hachés, si vous le souhaitez.

Ce n'est pas infaillible, mais:

83,6% des navigateurs vus avaient une empreinte digitale unique; parmi ceux avec Flash ou Java activé, 94,2%. Cela n'inclut pas les cookies!

Plus d'informations:


je pense que c'est toujours la réponse. si vous avez besoin d'identifier un appareil, il vous suffit d'obtenir ces données - par ex. OS, extensions génériques (et ses 'versions), polices installées, etc ...
pozs

Cela ne fonctionnera pas bien. Chaque navigateur prend en charge les sessions et les cookies. Utilisez le bon outil pour le travail.
Man Vs Code

1
@ slash197 qu'en est-il du cache de fichiers? je veux dire en utilisant un support flash transparent 1px x 1px avec un fichier xml contenant un identifiant unique généré à l'intérieur (le xml doit être créé une fois sur le serveur avant qu'il ne soit téléchargé sur l'utilisateur HD local) de cette façon, même si l'utilisateur supprime les cookies ou se déconnecte, vous pouvez toujours avoir un pont en utilisant la méthode sendAndLoad du script d'action.
Mbarry

Le minimum de changement affectera le résultat du hachage. par exemple la version du lecteur à ondes de choc. solution possible avec un fichier de cache xml stocké localement avec une clé unique générée + un support flash caché 1px x 1px (script d'action) sur le navigateur, de cette façon vous vous débarrassez des cookies, problème d'expiration de session si c'était le problème principal. vous pouvez toujours avoir le pont entre votre base de données SQL et la clé sur la machine locale de l'utilisateur.
Mbarry

@Mbarry Je ne suis pas vraiment un fan de flash, mais si dans le navigateur il y a un module complémentaire de blocage de flash comme je l'ai fait, le support flash 1x1 pixel serait désactivé, est-ce que j'ai raison?
slash197

7

L'impression de pouce mentionnée ci-dessus fonctionne, mais peut encore souffrir de colisions.

Une façon consiste à ajouter l'UID à l'url de chaque interaction avec l'utilisateur.

http://someplace.com/12899823/user/profile

Où chaque lien du site est adapté avec ce modificateur. Il est similaire à la façon dont ASP.Net fonctionnait en utilisant les données FORM entre les pages.


J'y ai pensé mais c'est le moyen le plus simple pour un utilisateur de le modifier
slash197

1
pas de l'id est un hachage d'auto-référencement. Le rend cryptographiquement sécurisé.
Justin Alexander

De plus, cette méthode est correcte lorsque quelqu'un navigue sur le site, mais comment proposez-vous de gérer le cas lorsqu'un utilisateur revenant revient après une semaine et tape simplement l'adresse du site Web, sans identifiant?
slash197

@ slash197 dans ce cas, pourquoi ne dites-vous pas à l'utilisateur de se connecter, que ce qui se passe même lorsque l'utilisateur supprime les cookies.
Akash Kava

6

Avez-vous examiné Evercookie ? Cela peut ou non fonctionner sur tous les navigateurs. Un extrait de leur site.

"Si un utilisateur obtient le cookie sur un navigateur et passe à un autre navigateur, tant qu'il a toujours le cookie Local Shared Object, le cookie se reproduira dans les deux navigateurs."


Je me demande si cela fonctionne avec JavaScript désactivé. Avez-vous une expérience?
Voitcus

Cela s'appelle evercookie pour une raison, cela fonctionnera quoi qu'il arrive. Il leur est presque impossible de supprimer le cookie.
Alexis Tyler

Cela ne fonctionnera pas quoi qu'il arrive. Dès la première ligne de la description: 'evercookie est une API javascript ...'. Cela ne fonctionnera pas si javascript est désactivé.
gdw2

N'a même pas besoin d'être désactivé. Ghostery et uBlock abandonnent Evercookie
OpenGrid

3

Vous pouvez le faire avec un png mis en cache, ce serait peu fiable (différents navigateurs se comportent différemment, et cela échouera si l'utilisateur efface son cache), mais c'est une option.

1: configurer une base de données qui stocke un identifiant d'utilisateur unique sous forme de chaîne hexadécimale

2: créez un fichier genUser.php (ou n'importe quelle langue) qui génère un identifiant d'utilisateur, le stocke dans la base de données puis crée une vraie couleur .png à partir des valeurs de cette chaîne hexadécimale (chaque pixel sera de 4 octets) et retourne cela au navigateur. Assurez-vous de définir le type de contenu et les en-têtes de cache.

3: dans le HTML ou JS créez une image comme <img id='user_id' src='genUser.php' />

4: dessinez cette image sur une toile ctx.drawImage(document.getElementById('user_id'), 0, 0);

5: lire les octets de cette image en utilisant ctx.getImageData , et convertissez les entiers en une chaîne hexadécimale.

6: C'est votre identifiant d'utilisateur unique qui est maintenant mis en cache sur l'ordinateur de vos utilisateurs.


Il veut quelque chose qui puisse suivre l'utilisateur "à travers les navigateurs" qui ne fonctionnera pas ici (chaque navigateur a sa propre base de données de cache).
EricLaw

Où voyez-vous cela, sa question demande seulement "J'ai oublié de mentionner que cela devrait être compatible avec tous les navigateurs", c'est-à-dire fonctionner dans n'importe quel navigateur.
hobberwickey

Sa question est mal écrite. I'm after device recognitionest le cadeau pour ce qu'il veut, et il élabore ici: stackoverflow.com/questions/15966812/…
EricLaw

2

D'après ce que vous avez dit:

Fondamentalement, je suis après la reconnaissance de l'appareil, pas vraiment l'utilisateur

La meilleure façon de le faire est d'envoyer l'adresse mac qui est l'ID NIC.

Vous pouvez jeter un œil à cet article: Comment puis-je obtenir le MAC et l'adresse IP d'un client connecté en PHP?


Désolé, mais l'identifiant NIC est facilement usurpable. Ce n'est certainement pas le meilleur moyen.
asgs

La prise d'empreintes digitales du navigateur @asgs serait peut-être meilleure, ou quel serait le meilleur moyen à votre avis?
Mehdi Karamosly

Il n'y a pas de meilleur moyen, c'est la partie triste à ce sujet. Cependant, cela et le Browser FingerPrinting en combinaison avec l'étude de probabilité que Baba a présentée ci-dessus seraient les meilleurs à mon avis.
asgs

1

Vous pouvez le faire avec etags. Bien que je ne sois pas sûr si cela juridique comme un tas de poursuites ont été intentées.

Si vous prévenez correctement vos utilisateurs ou si vous avez quelque chose comme un site Web intranet, cela peut être bon.


Les Etags ne sont pas compatibles avec tous les navigateurs.
slash197

1
Les Etags font partie de la spécification HTTP / 1.1. Tous les navigateurs courants prennent en charge etags, vous auriez à peu près besoin d'écrire votre propre navigateur pour ne pas prendre en charge les en-têtes ETag / If-None-Match.
Brian McGinity

Je n'ai pas dit qu'il ne le supportait pas, j'ai dit qu'il n'était pas compatible avec tous les navigateurs. Si une balise est enregistrée dans Firefox, elle n'est pas disponible dans Chrome, donc le contenu sera à nouveau téléchargé car il n'y a pas de cache.
slash197

Maintenant je comprends ce que vous disiez. Vous avez raison. Chaque navigateur a son propre magasin de cache, donc différents etags.
Brian McGinity


0

Inefficace, mais peut vous donner les résultats escomptés, serait d'interroger une API de votre côté. Avoir un processus d'arrière-plan côté client qui envoie les données utilisateur à un intervalle. Vous aurez besoin d'un identifiant utilisateur à envoyer à votre API. Une fois que vous avez cela, vous pouvez envoyer toutes les informations associées à cet identifiant unique.

Cela supprime le besoin de cookies et de stockage local.


0

Je ne peux pas croire, http://browserspy.dk n'a toujours pas été mentionné ici! Le site décrit de nombreuses fonctionnalités (en termes de reconnaissance de formes), qui pourraient être utilisées pour construire un classificateur.

Et bien sûr , pour évaluer les fonctionnalités, je suggérerais Support Vector Machines et libsvm en particulier.


0

Les suivre pendant une session ou entre les sessions?

Si votre site est HTTPS Everywhere, vous pouvez utiliser l'ID de session TLS pour suivre la session de l'utilisateur


1
la question ici est de savoir comment?
user455318

-2
  1. créer un plugin factice multiplateforme (nsapi) et générer un nom unique pour le nom ou la version du plugin lorsque l'utilisateur le télécharge (par exemple après la connexion).
  2. fournir un installateur pour le plugin / l'installer par politique

cela obligera l'utilisateur à installer volontairement l'identifiant.

une fois le plugin installé, l' empreinte digitale de n'importe quel navigateur (plugin activé) contiendra ce plugin spécifique. Pour renvoyer les informations à un serveur, un algorithme pour détecter efficacement le plugin côté client est nécessaire, sinon IE et Firefox> = 28 utilisateurs auront besoin d'un tableau des identifiants valides possibles.

Cela nécessite un investissement relativement élevé dans une technologie qui sera probablement fermée par les fournisseurs de navigateurs. Lorsque vous êtes en mesure de convaincre vos utilisateurs d'installer un plugin, il peut également y avoir des options comme installer un proxy local , utiliser vpn ou patcher les pilotes réseau.

Les utilisateurs qui ne souhaitent pas être identifiés (ou leurs machines) trouveront toujours un moyen de l'empêcher.


Salut bienvenue à débordement de pile. Notez s'il vous plaît; this will require the user to willingly install the identifier.n'est probablement pas ce que signifiait l'affiche originale (OP).
Stefan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.