Rozpoznawanie użytkownika bez plików cookie lub pamięci lokalnej

132

Buduję narzędzie analityczne i obecnie mogę uzyskać adres IP użytkownika, przeglądarkę i system operacyjny od jego agenta użytkownika.

Zastanawiam się, czy istnieje możliwość wykrycia tego samego użytkownika bez korzystania z plików cookie lub lokalnego magazynu? Nie oczekuję tutaj przykładów kodu; tylko prosta wskazówka, gdzie szukać dalej.

Zapomniałem wspomnieć, że musi być kompatybilny z różnymi przeglądarkami, jeśli jest to ten sam komputer / urządzenie. Zasadniczo zależy mi na rozpoznaniu urządzenia, a nie na użytkowniku.

slash197
źródło
5
Niezupełnie - przynajmniej nie w żaden sposób, na którym można by polegać, jeśli chodzi o dokładność. Może mieszanka wszystkich trzech połączonych, jednak jeśli więcej niż jedna osoba w domu korzysta z tej samej przeglądarki i systemu operacyjnego, nadal nie zadziała. Ponadto większość dostawców usług internetowych zapewnia dynamiczne adresy IP, co oznacza, że ​​zmieniają się one co jakiś czas i nie można na nich polegać w celach identyfikacyjnych.
Jon
2
Wtedy nie wiesz, co to są sesje. Twój przypadek użycia jest dokładnie tym, do czego zostały zaprojektowane sesje. Sesje nie mają nic wspólnego z logowaniem lub uwierzytelnianiem. Twój serwer sieciowy powie klientowi, aby wysłał plik cookie z identyfikatorem sesji. Identyfikujesz tego klienta za pomocą identyfikatora sesji, który Ci wysyłają.
Man Vs Code
4
Pliki cookie nadal będą działać? Dlaczego unikasz plików cookie?
Baba
2
To naprawdę proste i używam go cały czas, proszę użytkownika o wpisanie nazwy użytkownika i hasła !!!
Amit Kriplani
2
Oto minimalne rozwiązanie javascript (w tym przypadku nie cross-browser): github.com/carlo/jquery-browser-fingerprint Wspominam o tym, ponieważ doprowadziło mnie do przekonania, że ​​wiele wtyczek jest instalowanych domyślnie w różnych przeglądarkach, bez dowolny wybór ze strony użytkownika. Staranne uporządkowanie tych danych (co nie jest łatwym zadaniem, ale nadal ...) może potencjalnie prowadzić do namacalnej, niezależnej od przeglądarki właściwości większego odcisku palca opartego na urządzeniu.
hexalys

Odpowiedzi:

389

Wprowadzenie

Jeśli dobrze Cię rozumiem, musisz zidentyfikować użytkownika, dla którego nie masz unikalnego identyfikatora, więc chcesz dowiedzieć się, kim on jest, dopasowując dane losowe. Nie możesz niezawodnie przechowywać tożsamości użytkownika, ponieważ:

  • Pliki cookie można usunąć
  • Adres IP może się zmienić
  • Przeglądarka może się zmienić
  • Pamięć podręczna przeglądarki może zostać usunięta

Aplet Java lub obiekt Com byłby łatwym rozwiązaniem wykorzystującym skrót informacji o sprzęcie, ale w dzisiejszych czasach ludzie są tak świadomi bezpieczeństwa, że ​​trudno byłoby skłonić ludzi do zainstalowania tego rodzaju programów w ich systemie. To powoduje, że utkniesz z używaniem plików cookie i innych podobnych narzędzi.

Pliki cookie i inne podobne narzędzia

Możesz rozważyć utworzenie profilu danych, a następnie użycie testów prawdopodobieństwa do zidentyfikowania prawdopodobnego użytkownika . Profil przydatny do tego celu można wygenerować za pomocą kombinacji następujących elementów:

  1. Adres IP
    • Prawdziwy adres IP
    • Adres IP serwera proxy (użytkownicy często używają tego samego serwera proxy wielokrotnie)
  2. Ciasteczka
  3. Błędy sieciowe (mniej niezawodne, ponieważ błędy są naprawiane, ale nadal przydatne)
    • Błąd PDF
    • Flash Bug
    • Błąd Java
  4. Przeglądarki
    • Śledzenie kliknięć (wielu użytkowników odwiedza tę samą serię stron podczas każdej wizyty)
    • Przeglądarki odcisków palców - zainstalowane wtyczki (ludzie często mają różne, nieco unikalne zestawy wtyczek)
    • Obrazy w pamięci podręcznej (ludzie czasami usuwają pliki cookie, ale pozostawiają obrazy w pamięci podręcznej)
    • Korzystanie z obiektów blob
    • Adresy URL (historia przeglądarki lub pliki cookie mogą zawierać unikalne identyfikatory użytkownika w adresach URL, takie jak https://stackoverflow.com/users/1226894 lub http://www.facebook.com/barackobama?fref=ts )
    • Wykrywanie czcionek systemowych (jest to mało znany, ale często unikalny podpis klucza)
  5. HTML5 i Javascript
    • Magazyn lokalny HTML5
    • Interfejs API geolokalizacji HTML5 i odwrotne geokodowanie
    • Architektura, język systemu operacyjnego, czas systemowy, rozdzielczość ekranu itp.
    • Network Information API
    • Battery Status API

Pozycje, które wymieniłem, to oczywiście tylko kilka możliwych sposobów jednoznacznej identyfikacji użytkownika. Jest ich znacznie więcej.

Z tym zestawem losowych elementów danych, na podstawie których można zbudować profil danych, co dalej?

Następnym krokiem jest opracowanie logiki rozmytej lub, jeszcze lepiej, sztucznej sieci neuronowej (która wykorzystuje logikę rozmytą). W obu przypadkach chodzi o wytrenowanie systemu, a następnie połączenie jego treningu z wnioskiem bayesowskim, aby zwiększyć dokładność wyników.

Sztuczna sieć neuronowa

NeuralMesh biblioteki dla PHP pozwala na generowanie sztucznych sieci neuronowych. Aby zaimplementować wnioskowanie Bayesa, skorzystaj z następujących łączy:

W tym momencie możesz pomyśleć:

Po co tyle matematyki i logiki w pozornie prostym zadaniu?

Zasadniczo dlatego, że nie jest to proste zadanie . To, co próbujesz osiągnąć, to w rzeczywistości czyste prawdopodobieństwo . Na przykład biorąc pod uwagę następujących znanych użytkowników:

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

Kiedy otrzymasz następujące dane:

B + C + E + G + F + K

Pytanie, które zasadniczo zadajesz, brzmi:

Jakie jest prawdopodobieństwo, że otrzymane dane (B + C + E + G + F + K) to w rzeczywistości Użytkownik1 lub Użytkownik2? A który z tych dwóch dopasowań jest najbardziej prawdopodobny?

Aby skutecznie odpowiedzieć na to pytanie, musisz zrozumieć format częstotliwości vs prawdopodobieństwo i dlaczego wspólne prawdopodobieństwo może być lepszym podejściem. Szczegóły są zbyt duże, aby się tutaj zagłębić (dlatego podaję linki), ale dobrym przykładem może być aplikacja Kreatora diagnostyki medycznej , która wykorzystuje kombinację objawów do identyfikacji możliwych chorób.

Pomyśl przez chwilę o serii punktów danych, które składają się na Twój profil danych (B + C + E + G + F + K w powyższym przykładzie) jako o objawach , a nieznani użytkownicy jako o chorobach . Identyfikując chorobę, możesz dodatkowo zidentyfikować odpowiednie leczenie (traktuj tego użytkownika jako Użytkownika1).

Oczywiście choroba, dla której zidentyfikowaliśmy więcej niż 1 objaw, jest łatwiejsza do zidentyfikowania. W rzeczywistości, im więcej Objawów możemy zidentyfikować, tym łatwiejsza i dokładniejsza jest nasza diagnoza.

Czy są jakieś inne alternatywy?

Oczywiście. Alternatywą może być stworzenie własnego prostego algorytmu punktacji i oparcie go na dokładnych dopasowaniach. Nie jest to tak wydajne jak prawdopodobieństwo, ale może być prostsze do wdrożenia.

Jako przykład rozważ ten prosty wykres wyników:

+ ------------------------- + -------- + ------------ +
| Nieruchomość | Waga | Znaczenie |
+ ------------------------- + -------- + ------------ +
| Prawdziwy adres IP | 60 | 5 |
| Używany adres IP proxy | 40 | 4 |
| Pliki cookie HTTP | 80 | 8 |
| Sesyjne pliki cookie | 80 | 6 |
| Pliki cookie innych firm | 60 | 4 |
| Pliki cookie Flash | 90 | 7 |
| Błąd PDF | 20 | 1 |
| Błąd Flash | 20 | 1 |
| Błąd Java | 20 | 1 |
| Częste strony | 40 | 1 |
| Przeglądarki odcisków palców | 35 | 2 |
| Zainstalowane wtyczki | 25 | 1 |
| Obrazy w pamięci podręcznej | 40 | 3 |
| URL | 60 | 4 |
| Wykrywanie czcionek systemowych | 70 | 4 |
| Localstorage | 90 | 8 |
| Geolokalizacja | 70 | 6 |
| AOLTR | 70 | 4 |
| Network Information API | 40 | 3 |
| Interfejs API stanu baterii | 20 | 1 |
+ ------------------------- + -------- + ------------ +

Za każdą informację, którą możesz zebrać w ramach danego żądania, przyznaj odpowiednią ocenę, a następnie użyj opcji Ważność, aby rozwiązać konflikty, gdy wyniki są takie same.

Dowód koncepcji

Aby uzyskać prosty dowód słuszności koncepcji, spójrz na Perceptron . Perceptron to model RNA, który jest powszechnie używany w zastosowaniach do rozpoznawania wzorców. Istnieje nawet stara klasa PHP, która doskonale ją implementuje, ale prawdopodobnie będziesz musiał ją zmodyfikować do swoich celów.

Pomimo tego, że jest świetnym narzędziem, Perceptron nadal może zwracać wiele wyników (możliwych dopasowań), więc użycie porównania wyniku i różnicy jest nadal przydatne do określenia najlepszego z tych dopasowań.

Założenia

  • Przechowuj wszystkie możliwe informacje o każdym użytkowniku (adres IP, pliki cookie itp.)
  • Jeśli wynik jest dokładnym dopasowaniem, zwiększ wynik o 1
  • Jeśli wynik nie jest dokładnym dopasowaniem, zmniejsz wynik o 1

Oczekiwanie

  1. Generuj etykiety RNA
  2. Generuj losowych użytkowników emulujących bazę danych
  3. Wygeneruj jednego nieznanego użytkownika
  4. Generuj RNA i wartości nieznanego użytkownika
  5. System połączy informacje RNA i nauczy Perceptron
  6. Po wytrenowaniu Perceptronu system będzie posiadał zestaw ważeń
  7. Możesz teraz przetestować wzorzec Nieznanego użytkownika, a Perceptron wygeneruje zestaw wyników.
  8. Przechowuj wszystkie pozytywne dopasowania
  9. Sortuj mecze najpierw według wyniku, a następnie według różnicy (jak opisano powyżej)
  10. Wypisz dwa najbliższe dopasowania lub, jeśli nie zostaną znalezione żadne dopasowania, wypisz puste wyniki

Kod dowodu koncepcji

$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 ";
}

Wynik:

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

Print_r z "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
)

Jeśli debugowanie = true, będzie można zobaczyć dane wejściowe (czujnik i żądane), masy początkowe, dane wyjściowe (czujnik, suma, sieć), błąd, korektę i masy końcowe .

+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 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 do x20 reprezentują funkcje konwertowane przez kod.

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

Oto demo online

Użyta klasa:

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;
    }
}

Zmodyfikowana klasa perceptronu

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

Wniosek

Identyfikacja użytkownika bez unikalnego identyfikatora nie jest prostym ani prostym zadaniem. zależy to od zebrania wystarczającej ilości losowych danych, które możesz zebrać od użytkownika różnymi metodami.

Nawet jeśli zdecydujesz się nie używać sztucznej sieci neuronowej, proponuję przynajmniej użyć prostej macierzy prawdopodobieństwa z priorytetami i prawdopodobieństwami - i mam nadzieję, że kod i przykłady podane powyżej wystarczą, aby kontynuować.

Baba
źródło
@Baba Co masz na myśli mówiąc „Używanie Blobów” do pobierania odcisków palców przeglądarki?
billmalarky
1
@Baba Jak można to wykorzystać do odcisków palców przeglądarki? Wystarczy sprawdzić, co aktualnie w nim jest w dowolnym momencie?
billmalarky
@Baba świetna robota, zawsze starałem się mieć jakąś wielopoziomową strategię identyfikacji użytkownika, ale, jak powiedziałeś, można wyczyścić pamięć podręczną, zmienić adresy IP, użytkowników za serwerami proxy lub NAT - zwłaszcza te osoby - usunąć pliki cookie itp. ., ale nawet przy tak dużym wysiłku jest to kwestia prawdopodobieństwa, także jeśli zły użytkownik używa na przykład przeglądarki Tor , większość, jeśli nie wszystkie wymienione strategie wykrywania nie zadziałają. Podobało mi się browserleaks.com, ale w przypadku Tora wszystko wróciło niezdefiniowane lub nieznane
Mi-Creativity
Tylko uwaga przeznaczona tylko do " odkurzenia " z tej perełki publikacji: Lista niedziałających linków na dzień 07.09.17: - Implement Bayesian inference using PHP, wszystkie 3 części. - Frequency vs Probability - Joint Probability -Input (Sensor & Desired), Initial Weights, Output (Sensor, Sum, Network), Error, Correction and Final Weights
Ziezi
28

Ta technika (wykrywania tych samych użytkowników bez plików cookie - lub nawet bez adresu IP) nazywana jest odciskiem palca przeglądarki . Zasadniczo indeksujesz jako informacje o przeglądarce, jak tylko możesz - lepsze wyniki można osiągnąć za pomocą javascript, flash lub java (np. Zainstalowane rozszerzenia, czcionki itp.). Następnie możesz zapisać zaszyfrowane wyniki, jeśli chcesz.

Nie jest nieomylne, ale:

83,6% przeglądarek miało unikalny odcisk palca; wśród tych z włączoną obsługą Flash lub Java 94,2%. Nie dotyczy to plików cookie!

Więcej informacji:

pozs
źródło
Myślę, że to wciąż odpowiedź. jeśli potrzebujesz zidentyfikować urządzenie, potrzebujesz tylko tych danych - np. System operacyjny, ogólne rozszerzenia (i jego wersje), zainstalowane czcionki itp.
pozs
To nie zadziała dobrze. Każda przeglądarka obsługuje sesje i pliki cookie. Użyj odpowiedniego narzędzia do pracy.
Man Vs Code
1
@ slash197 a co z pamięcią podręczną plików? mam na myśli użycie przezroczystego nośnika flash o wymiarach 1px x 1px wraz z plikiem xml zawierającym unikalny wygenerowany identyfikator (plik xml powinien być utworzony raz na serwerze przed pobraniem na lokalny dysk HD użytkownika) w ten sposób, nawet jeśli użytkownik usunie ciasteczka lub wyloguje się, nadal możesz mieć mostek używając metody sendAndLoad skryptu akcji.
Mbarry
Minimalna zmiana wpłynie na wynik skrótu. na przykład wersja odtwarzacza fali uderzeniowej. możliwe rozwiązanie z lokalnie przechowywanym plikiem pamięci podręcznej xml z wygenerowanym unikalnym kluczem + ukrytym nośnikiem flash 1px x 1px (skryptem akcji) w przeglądarce, w ten sposób pozbywasz się plików cookie, problem z wygasaniem sesji, jeśli to był główny problem. nadal możesz mieć mostek między bazą danych sql a kluczem na komputerze lokalnym użytkownika.
Mbarry
@Mbarry Nie przepadam za fleszem, ale jeśli w przeglądarce jest dodatek blokujący flash, tak jak mam, to flashowe media 1x1 piksel byłyby wyłączone, czy mam rację?
slash197
7

Wspomniany powyżej odcisk palca działa, ale nadal może wystąpić kolizja.

Jednym ze sposobów jest dodanie UID do adresu URL każdej interakcji z użytkownikiem.

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

Gdzie każdy link w witrynie jest dostosowany za pomocą tego modyfikatora. Jest to podobne do sposobu, w jaki ASP.Net pracował przy użyciu danych FORM między stronami.

Justin Alexander
źródło
Myślałem o tym, ale to najłatwiejszy sposób dla użytkownika, aby to zmodyfikować
slash197
1
nie z id jest hashem odwołującym się do siebie. Zapewnia bezpieczeństwo kryptograficzne.
Justin Alexander
Również ta metoda jest w porządku, gdy ktoś przegląda witrynę, ale jak proponujesz załatwić sprawę, gdy powracający użytkownik wraca po tygodniu i po prostu wpisuje adres strony internetowej bez identyfikatora?
slash197
@ slash197 w takim przypadku dlaczego nie powiesz użytkownikowi, aby się zalogował, co się dzieje nawet wtedy, gdy użytkownik usunie pliki cookie.
Akash Kava,
6

Czy zajrzałeś do Evercookie ? Może, ale nie musi, działać w różnych przeglądarkach. Wyciąg z ich strony.

„Jeśli użytkownik zostanie zapisany w jednej przeglądarce i przełączy się na inną, o ile nadal będzie miał plik cookie Local Shared Object, plik cookie zostanie odtworzony w obu przeglądarkach”.

Alexis Tyler
źródło
Zastanawiam się, czy działa z wyłączoną obsługą JavaScript. Czy masz jakieś doświadczenie?
Voitcus
Nie bez powodu nazywa się to evercookie, będzie działać bez względu na wszystko. Usunięcie pliku cookie jest prawie niemożliwe.
Alexis Tyler,
To nie zadziała bez względu na wszystko. Z pierwszego wiersza opisu: „evercookie to javascript API ...”. Nie będzie działać, jeśli JavaScript jest wyłączony.
gdw2
Nie trzeba nawet wyłączać js. Ghostery i uBlock upuszczają evercookie
opengrid
3

Możesz to zrobić za pomocą buforowanego pliku png, byłoby to nieco zawodne (różne przeglądarki zachowują się inaczej i nie powiedzie się, jeśli użytkownik wyczyści pamięć podręczną), ale jest to opcja.

1: skonfiguruj bazę danych, która przechowuje unikalny identyfikator użytkownika jako ciąg szesnastkowy

2: utwórz plik genUser.php (lub w jakimkolwiek innym języku), który generuje identyfikator użytkownika, przechowuje go w bazie danych, a następnie tworzy prawdziwy kolor .png z wartości tego ciągu szesnastkowego (każdy piksel będzie miał 4 bajty) i zwróć to do przeglądarki. Pamiętaj, aby ustawić typ zawartości i nagłówki pamięci podręcznej.

3: w HTML lub JS utwórz obraz podobny do <img id='user_id' src='genUser.php' />

4: narysuj ten obraz na płótnie ctx.drawImage(document.getElementById('user_id'), 0, 0);

5: odczytaj bajty tego obrazu za pomocą ctx.getImageDatai zamień liczby całkowite na ciąg szesnastkowy.

6: To jest twój unikalny identyfikator użytkownika, który jest teraz przechowywany w pamięci podręcznej na komputerze użytkownika.

hobberwickey
źródło
Chce czegoś, co może śledzić użytkownika „w różnych przeglądarkach”, co tutaj nie zadziała (każda przeglądarka ma własną bazę danych pamięci podręcznej).
EricLaw
Gdzie to widzisz, jego pytanie dotyczy tylko „Zapomniałem wspomnieć, że musi być kompatybilne z różnymi przeglądarkami”, czyli działać w dowolnej przeglądarce.
hobberwickey
Jego pytanie jest słabo napisane. I'm after device recognitionjest gratisem za to, czego chce, i rozwija tutaj: stackoverflow.com/questions/15966812/…
EricLaw
2

Na podstawie tego, co powiedziałeś:

Zasadniczo zależy mi na rozpoznaniu urządzenia, a nie na użytkowniku

Najlepszym sposobem na to jest przesłanie adresu mac, który jest identyfikatorem karty sieciowej.

Możesz rzucić okiem na ten post: Jak mogę uzyskać adres MAC i adres IP podłączonego klienta w PHP?

Mehdi Karamosly
źródło
Przepraszamy, ale identyfikator karty sieciowej można łatwo sfałszować. To zdecydowanie nie jest najlepszy sposób.
asgs
Być może lepsze byłoby pobieranie odcisków palców przeglądarki @asgs, a może według Ciebie najlepszy sposób?
Mehdi Karamosly
Nie ma najlepszego sposobu, to smutna część tego. Jednak to i odciski palców w przeglądarce w połączeniu z badaniem prawdopodobieństwa, które Baba przedstawił powyżej, byłyby moim zdaniem najlepsze.
asgs
1

Możesz to zrobić za pomocą etagów. Chociaż nie jestem pewien, czy to legalne, ponieważ złożono kilka pozwów.

Jeśli odpowiednio ostrzegasz swoich użytkowników lub masz coś w rodzaju witryny intranetowej, może być w porządku.

Brian McGinity
źródło
Etykiety nie są kompatybilne z różnymi przeglądarkami.
slash197
1
Etykiety są częścią specyfikacji HTTP / 1.1. Wszystkie popularne przeglądarki obsługują etagi, prawie musiałbyś napisać własną przeglądarkę, aby nie obsługiwała nagłówków ETag / If-None-Match.
Brian McGinity
Nie powiedziałem, że go nie obsługuje, powiedziałem, że nie jest kompatybilny z różnymi przeglądarkami. Jeśli tag jest zapisany w przeglądarce Firefox, nie jest dostępny w przeglądarce Chrome, więc zawartość zostanie pobrana ponownie, ponieważ nie ma pamięci podręcznej.
slash197
Teraz rozumiem, co mówiłeś. Masz rację. Każda przeglądarka ma swój własny magazyn pamięci podręcznej, stąd różne etagi.
Brian McGinity
0

Nieefektywne, ale może dać pożądane rezultaty, byłoby sondowanie API po twojej stronie. Mieć proces w tle po stronie klienta, który wysyła dane użytkownika w określonych odstępach czasu. Będziesz potrzebować identyfikatora użytkownika, aby wysłać go do swojego interfejsu API. Gdy już to zrobisz, możesz przesłać wszelkie informacje związane z tym unikalnym identyfikatorem.

Eliminuje to potrzebę stosowania plików cookie i lokalnego przechowywania.

rexposadas
źródło
0

Nie mogę uwierzyć, że http://browserspy.dk nadal nie zostało tu wymienione! Witryna opisuje wiele funkcji (w zakresie rozpoznawania wzorców), które można wykorzystać do zbudowania klasyfikatora.

I nie bez powodu, do oceny funkcji sugerowałbym w szczególności Support Vector Machines i libsvm .

Valentin Heinitz
źródło
0

Śledzić je podczas sesji lub między sesjami?

Jeśli Twoja witryna korzysta z protokołu HTTPS Everywhere, możesz użyć identyfikatora sesji TLS do śledzenia sesji użytkownika

Neil McGuigan
źródło
1
pytanie tutaj brzmi jak?
user455318,
-2
  1. utwórz wtyczkę wieloplatformową (nsapi) i wygeneruj unikalną nazwę dla nazwy lub wersji wtyczki, gdy użytkownik ją pobierze (np. po zalogowaniu).
  2. Zapewnij instalator wtyczki / zainstaluj ją zgodnie z zasadami

będzie to wymagało od użytkownika dobrowolnego zainstalowania identyfikatora.

po zainstalowaniu wtyczki odcisk palca dowolnej przeglądarki (z włączoną wtyczką) będzie zawierał tę konkretną wtyczkę. Aby zwrócić informacje na serwer, potrzebny jest algorytm do skutecznego wykrywania wtyczki po stronie klienta, w przeciwnym razie użytkownicy przeglądarek IE i Firefox> = 28 będą potrzebować tabeli możliwych prawidłowych identyfikatorów.

Wymaga to stosunkowo dużych inwestycji w technologię, która prawdopodobnie zostanie wyłączona przez dostawców przeglądarek. Kiedy jesteś w stanie przekonać użytkowników do zainstalowania wtyczki, mogą również istnieć opcje, takie jak instalacja lokalnego serwera proxy , użycie VPN lub załatanie sterowników sieciowych.

Użytkownicy, którzy nie chcą być identyfikowani (lub ich komputery), zawsze znajdą sposób, aby temu zapobiec.

aec8adaikaeNg6hey2oof8otai9quo
źródło
Witam, zapraszamy do przepełnienia stosu. Proszę zanotować; this will require the user to willingly install the identifier.nie jest prawdopodobnie tym, co oznaczał oryginalny plakat (OP).
Stefan