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:
- Adres IP
- Prawdziwy adres IP
- Adres IP serwera proxy (użytkownicy często używają tego samego serwera proxy wielokrotnie)
- Ciasteczka
- Błędy sieciowe (mniej niezawodne, ponieważ błędy są naprawiane, ale nadal przydatne)
- Błąd PDF
- Flash Bug
- Błąd Java
- 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)
- 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.
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
- Generuj etykiety RNA
- Generuj losowych użytkowników emulujących bazę danych
- Wygeneruj jednego nieznanego użytkownika
- Generuj RNA i wartości nieznanego użytkownika
- System połączy informacje RNA i nauczy Perceptron
- Po wytrenowaniu Perceptronu system będzie posiadał zestaw ważeń
- Możesz teraz przetestować wzorzec Nieznanego użytkownika, a Perceptron wygeneruje zestaw wyników.
- Przechowuj wszystkie pozytywne dopasowania
- Sortuj mecze najpierw według wyniku, a następnie według różnicy (jak opisano powyżej)
- 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ć.