Вступ
Якщо я вас правильно зрозумів, вам потрібно визначити користувача, для якого у вас немає унікального ідентифікатора, тому ви хочете з’ясувати, хто вони, шляхом зіставлення випадкових даних. Ви не можете надійно зберігати особу користувача, оскільки:
- Файли cookie можна видалити
- IP-адреса може змінюватися
- Веб-переглядач може змінюватися
- Кеш браузера може бути видалений
Яблучний аплет або Com Object було б простим рішенням, використовуючи хеш апаратної інформації, але в наші дні люди настільки обізнані в безпеці, що людям буде важко встановити подібні програми в їхній системі. Це не дасть вам застрягти з використанням файлів cookie та інших подібних інструментів.
Файли cookie та інші подібні інструменти
Ви можете розглянути можливість створення профілю даних та використання ймовірних тестів для ідентифікації ймовірного користувача . Корисний для цього профіль може бути сформований за допомогою комбінації з наступного:
- IP-адреса
- Реальна IP-адреса
- IP-адреса проксі (користувачі часто використовують один і той же проксі)
- Печиво
- Веб-помилки (менш надійні, оскільки помилки виправляються, але все ж корисні)
- Файл PDF
- Flash Bug
- Java-помилка
- Браузери
- Відстеження натискань (багато користувачів відвідують одну і ту ж серію сторінок під час кожного відвідування)
- Відбиток пальців браузера - встановлені плагіни (люди часто мають різноманітні, дещо унікальні набори плагінів)
- Кешовані зображення (люди іноді видаляють файли cookie, але залишають кешовані зображення)
- Використання крапель
- URL-адреси (історія веб-переглядача чи файли cookie можуть містити унікальні ідентифікатори користувача в URL-адресах, наприклад https://stackoverflow.com/users/1226894 або http://www.facebook.com/barackobama?fref=ts )
- Виявлення системних шрифтів (це маловідома, але часто унікальна підпис ключа)
- HTML5 та Javascript
- HTML5 LocalStorage
- API геолокації HTML5 та зворотне геокодування
- Архітектура, мова ОС, системний час, роздільна здатність екрана тощо.
- API мережевої інформації
- API стану акумулятора
Перераховані мною пункти - це, звичайно, лише декілька можливих способів, коли користувач може бути визначений однозначно. Є ще багато.
Що з цього набору елементів випадкових даних для побудови профілю даних, що далі?
Наступним кроком є розробка якоїсь нечіткої логіки , а ще краще, штучної нейронної мережі (яка використовує нечітку логіку). У будь-якому випадку ідея полягає в тому, щоб навчити свою систему, а потім поєднати її навчання з байєсівським висновком, щоб підвищити точність результатів.
Бібліотека NeuralMesh для PHP дозволяє генерувати Штучні нейронні мережі. Для реалізації байєсівського висновку перегляньте такі посилання:
У цей момент ви можете думати:
Чому так багато математики та логіки для, здавалося б, простого завдання?
В основному, тому що це непросте завдання . Те, що ви намагаєтесь досягти, - це насправді Чиста ймовірність . Наприклад, з урахуванням таких відомих користувачів:
User1 = A + B + C + D + G + K
User2 = C + D + I + J + K + F
Коли ви отримуєте такі дані:
B + C + E + G + F + K
Питання, яке ви, по суті, задаєте:
Яка ймовірність того, що отримані дані (B + C + E + G + F + K) насправді є User1 або User2? І який із цих двох матчів найбільш вірогідний?
Щоб ефективно відповісти на це запитання, вам потрібно зрозуміти формат частоти проти ймовірності та чому спільна ймовірність може бути кращим підходом. Деталей занадто багато, щоб потрапити сюди (саме тому я даю вам посилання), але хорошим прикладом може бути додаток Майстра медичного діагностування , який використовує поєднання симптомів для виявлення можливих захворювань.
Подумайте на хвилину із серії точок даних, які містять Ваш профіль даних (B + C + E + G + F + K у прикладі вище) як симптоми , а невідомі користувачі - як захворювання . Визначивши захворювання, ви можете додатково визначити відповідне лікування (ставитися до цього користувача як до User1).
Очевидно, що хворобу, для якої ми виявили більше 1 симптому , легше визначити. Насправді, чим більше симптомів ми можемо виявити, тим легше і точніше наш діагноз майже певний.
Чи є інші альтернативи?
Звичайно. Як альтернативний захід, ви можете створити власний простий алгоритм оцінювання та базувати його на точних відповідностях. Це не настільки ефективно, як ймовірність, але може бути простішим у здійсненні.
Як приклад, розглянемо цю просту таблицю показників:
+ ------------------------- + -------- + ------------ +
| Майно | Вага | Важливість |
+ ------------------------- + -------- + ------------ +
| Реальна IP-адреса | 60 | 5 |
| Використовувана IP-адреса проксі | 40 | 4 |
| HTTP cookies | 80 | 8 |
| Сесійні файли cookie | 80 | 6 |
| Файли cookie сторонніх | 60 | 4 |
| Flash Cookies | 90 | 7 |
| PDF-помилка | 20 | 1 |
| Flash Bug | 20 | 1 |
| Java Bug | 20 | 1 |
| Часті сторінки | 40 | 1 |
| Відбитки пальців браузера | 35 | 2 |
| Встановлені плагіни | 25 | 1 |
| Кешовані зображення | 40 | 3 |
| URL | 60 | 4 |
| Виявлення системних шрифтів | 70 | 4 |
| Місцеве зберігання | 90 | 8 |
| Геолокація | 70 | 6 |
| АОЛТР | 70 | 4 |
| API інформації про мережу | 40 | 3 |
| API стану акумулятора | 20 | 1 |
+ ------------------------- + -------- + ------------ +
За кожну інформацію, яку ви можете зібрати за певним запитом, присвойте відповідний бал, а потім використовуйте Важливість для вирішення конфліктів, коли бали однакові.
Доказ концепції
Для простого доказу концепції, будь ласка, подивіться на Perceptron . Perceptron - це модель РНК, яка зазвичай використовується у програмах розпізнавання образів. Існує навіть старий клас PHP, який ідеально реалізує його, але вам, ймовірно, доведеться змінити його для своїх цілей.
Незважаючи на те, що він є чудовим інструментом, Perceptron може все-таки повернути декілька результатів (можливих матчів), тому використання порівняння «Оцінка та Різниця» все ще корисно для визначення найкращого з цих матчів.
Припущення
- Зберігати всю можливу інформацію про кожного користувача (IP, файли cookie тощо)
- Якщо результат відповідає точній відповідності, збільште бал на 1
- Якщо результат не відповідає точності, зменшіть бал на 1
Очікування
- Створюйте мітки РНК
- Створення випадкових користувачів, що емулюють базу даних
- Створити одного невідомого користувача
- Створення РНК та цінностей невідомого користувача
- Система об'єднає інформацію РНК і навчить Перцептрону
- Після тренування Perceptron система матиме набір зважувань
- Тепер ви можете протестувати шаблон Невідомого користувача, і Perceptron створить набір результатів.
- Зберігайте всі позитивні сірники
- Сортуйте збіги спочатку за Оцінками, потім за різницею (як описано вище)
- Виведіть два найближчих збіги або, якщо не знайдено відповідностей, виведіть порожні результати
Код для підтвердження концепції
$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 ";
}
Вихід:
Possible Match D (0.7416|0.16853),C (0.5393|0.2809)
Print_r з "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
)
Якщо Debug = true, ви зможете побачити вхідні дані (датчик та бажані), початкові ваги, вихідний сигнал (датчик, сума, мережа), помилка, корекція та кінцеві ваги .
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 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 представляють функції, перетворені кодом.
// Get RNA Labels
$labels = array();
$n = 1;
foreach ( $features as $k => $v ) {
$labels[$k] = "x" . $n;
$n ++;
}
Ось онлайн демонстрація
Клас:
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;
}
}
Модифікований клас рецепторів
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
Висновок
Ідентифікація користувача без унікального ідентифікатора - це не пряма задача чи проста задача. це залежить від збору достатньої кількості випадкових даних, які ви можете зібрати у користувача різними методами.
Навіть якщо ви вирішите не використовувати Штучну нейронну мережу, я пропоную хоча б використовувати Просту матрицю ймовірностей з пріоритетами та ймовірностями - і сподіваюся, що наведений вище код та приклади дають вам достатньо для продовження.