introduzione
Se ti capisco correttamente, devi identificare un utente per il quale non hai un identificatore univoco, quindi vuoi capire chi sono abbinando i dati casuali. Non è possibile memorizzare l'identità dell'utente in modo affidabile perché:
- I cookie possono essere cancellati
- L'indirizzo IP può cambiare
- Il browser può cambiare
- La cache del browser potrebbe essere eliminata
Un'applet Java o un oggetto Com sarebbe stata una soluzione semplice utilizzando un hash di informazioni sull'hardware, ma oggigiorno le persone sono così consapevoli della sicurezza che sarebbe difficile indurre le persone a installare questo tipo di programmi sul proprio sistema. Questo ti lascia bloccato con l'utilizzo di cookie e altri strumenti simili.
Cookie e altri strumenti simili
Potresti prendere in considerazione la creazione di un profilo dati, quindi utilizzare i test di probabilità per identificare un probabile utente . Un profilo utile per questo può essere generato da una combinazione dei seguenti elementi:
- Indirizzo IP
- Indirizzo IP reale
- Indirizzo IP proxy (gli utenti usano spesso ripetutamente lo stesso proxy)
- Biscotti
- Bug Web (meno affidabili perché i bug vengono corretti, ma comunque utili)
- Bug PDF
- Bug flash
- Bug Java
- browser
- Tracciamento dei clic (molti utenti visitano la stessa serie di pagine per ogni visita)
- Browser Finger Print - Plugin installati (le persone hanno spesso set di plug-in vari, piuttosto unici)
- Immagini memorizzate nella cache (le persone a volte eliminano i cookie ma lasciano immagini memorizzate nella cache)
- Usando BLOB
- URL (la cronologia del browser o i cookie possono contenere ID utente univoci negli URL, come https://stackoverflow.com/users/1226894 o http://www.facebook.com/barackobama?fref=ts )
- Rilevamento caratteri di sistema (si tratta di una firma chiave poco conosciuta ma spesso unica)
- HTML5 e Javascript
- HTML5 LocalStorage
- API di geolocalizzazione HTML5 e geocodifica inversa
- Architettura, lingua del sistema operativo, ora del sistema, risoluzione dello schermo, ecc.
- API delle informazioni di rete
- API di stato della batteria
Gli elementi che ho elencato sono, ovviamente, solo alcuni dei modi in cui un utente può essere identificato in modo univoco. Ce ne sono molti altri.
Con questo set di elementi di dati casuali da cui creare un profilo dati, quali sono le prospettive?
Il prossimo passo è sviluppare una Fuzzy Logic , o, meglio ancora, una rete neurale artificiale (che usa la logica fuzzy). In entrambi i casi, l'idea è quella di addestrare il sistema e quindi combinare la sua formazione con l'inferenza bayesiana per aumentare l'accuratezza dei risultati.
La libreria NeuralMesh per PHP ti consente di generare reti neurali artificiali. Per implementare l'inferenza bayesiana, controlla i seguenti collegamenti:
A questo punto, potresti pensare:
Perché così tanta matematica e logica per un compito apparentemente semplice?
Fondamentalmente, perché non è un compito semplice . Ciò che stai cercando di ottenere è, in effetti, Pura Probabilità . Ad esempio, dati i seguenti utenti noti:
User1 = A + B + C + D + G + K
User2 = C + D + I + J + K + F
Quando ricevi i seguenti dati:
B + C + E + G + F + K
La domanda che si pone essenzialmente è:
Qual è la probabilità che i dati ricevuti (B + C + E + G + F + K) siano effettivamente Utente1 o Utente2? E quale di queste due partite è più probabile?
Per rispondere efficacemente a questa domanda, è necessario comprendere la frequenza rispetto al formato di probabilità e perché la probabilità congiunta potrebbe essere un approccio migliore. I dettagli sono troppi per entrare qui (motivo per cui ti sto dando dei link), ma un buon esempio potrebbe essere un'applicazione guidata per la diagnosi medica , che utilizza una combinazione di sintomi per identificare possibili malattie.
Pensa per un momento alla serie di punti dati che comprendono il tuo profilo dati (B + C + E + G + F + K nell'esempio sopra) come Sintomi e Utenti sconosciuti come Malattie . Identificando la malattia, è possibile identificare ulteriormente un trattamento appropriato (trattare questo utente come Utente1).
Ovviamente, una malattia per la quale abbiamo identificato più di 1 Sintomo è più facile da identificare. In effetti, più sintomi siamo in grado di identificare, più facile e accurata sarà la nostra diagnosi.
Ci sono altre alternative?
Ovviamente. In alternativa, potresti creare il tuo semplice algoritmo di punteggio e basarlo su corrispondenze esatte. Questo non è efficiente quanto la probabilità, ma potrebbe essere più semplice da implementare.
Ad esempio, considera questo semplice diagramma dei punteggi:
+ ------------------------- + -------- + ------------ +
| Proprietà | Peso | Importanza |
+ ------------------------- + -------- + ------------ +
| Indirizzo IP reale | 60 | 5 |
| Indirizzo IP proxy utilizzato 40 | 4 |
| Cookie HTTP | 80 | 8 |
| Cookie di sessione | 80 | 6 |
| Cookie di terze parti | 60 | 4 |
| Cookie Flash | 90 | 7 |
| Bug PDF | 20 | 1 |
| Bug Flash | 20 | 1 |
| Bug Java | 20 | 1 |
| Pagine frequenti | 40 | 1 |
| Impronta digitale dei browser | 35 | 2 |
| Plugin installati | 25 | 1 |
| Immagini memorizzate nella cache 40 | 3 |
| URL | 60 | 4 |
| Rilevamento caratteri di sistema | 70 | 4 |
| Deposito locale | 90 | 8 |
| Geolocalizzazione | 70 | 6 |
| AOLTR | 70 | 4 |
| API informazioni di rete | 40 | 3 |
| API di stato della batteria | 20 | 1 |
+ ------------------------- + -------- + ------------ +
Per ogni informazione che puoi raccogliere su una determinata richiesta, assegna il punteggio associato, quindi usa Importanza per risolvere i conflitti quando i punteggi sono uguali.
Verifica teorica
Per una semplice dimostrazione del concetto, dai un'occhiata a Perceptron . Perceptron è un modello di RNA che viene generalmente utilizzato in applicazioni di riconoscimento di schemi. Esiste persino una vecchia classe PHP che la implementa perfettamente, ma probabilmente dovrai modificarla per i tuoi scopi.
Nonostante sia un ottimo strumento, Perceptron può comunque restituire più risultati (possibili corrispondenze), quindi utilizzare un confronto di Punteggio e Differenza è ancora utile per identificare il migliore di tali corrispondenze.
ipotesi
- Memorizza tutte le informazioni possibili su ciascun utente (IP, cookie, ecc.)
- Se il risultato è una corrispondenza esatta, aumentare il punteggio di 1
- Se il risultato non corrisponde esattamente, ridurre il punteggio di 1
aspettativa
- Genera etichette RNA
- Genera utenti casuali che emulano un database
- Genera un singolo utente sconosciuto
- Genera RNA e valori dell'utente sconosciuto
- Il sistema unirà le informazioni RNA e insegnerà il Perceptron
- Dopo l'allenamento di Perceptron, il sistema avrà una serie di ponderazioni
- Ora puoi testare il modello dell'utente sconosciuto e Perceptron produrrà un set di risultati.
- Memorizza tutte le partite positive
- Ordina le partite prima per punteggio, quindi per differenza (come descritto sopra)
- Emette le due corrispondenze più vicine o, se non viene trovata alcuna corrispondenza, restituisce risultati vuoti
Codice per Proof of 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 ";
}
Produzione:
Possible Match D (0.7416|0.16853),C (0.5393|0.2809)
Stampa_r di "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
)
Se Debug = true si sarebbe in grado di vedere Input (Sensore e desiderato), Pesi iniziali, Output (Sensore, Somma, Rete), Errore, Correzione e Pesi finali .
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 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 |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
Da x1 a x20 rappresentano le funzioni convertite dal codice.
// Get RNA Labels
$labels = array();
$n = 1;
foreach ( $features as $k => $v ) {
$labels[$k] = "x" . $n;
$n ++;
}
Ecco una demo online
Classe utilizzata:
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 di Perceptron modificata
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
Conclusione
Identificare un utente senza un identificatore univoco non è un'attività semplice o diretta. dipende dalla raccolta di una quantità sufficiente di dati casuali che è possibile raccogliere dall'utente con una varietà di metodi.
Anche se scegli di non utilizzare una rete neurale artificiale, ti suggerisco almeno di usare una matrice di probabilità semplice con priorità e probabilità - e spero che il codice e gli esempi forniti sopra ti diano abbastanza per andare avanti.