Введение
Если я правильно вас понял, вам нужно идентифицировать пользователя, для которого у вас нет уникального идентификатора, поэтому вы хотите выяснить, кто они, сопоставив случайные данные. Вы не можете надежно сохранить личность пользователя, потому что:
- Файлы cookie могут быть удалены
- IP-адрес может измениться
- Браузер может измениться
- Кэш браузера может быть удален
Java-апплет или Com-объект был бы простым решением, использующим хэш аппаратной информации, но в наши дни люди настолько осведомлены о безопасности, что было бы трудно заставить людей устанавливать такие программы в их систему. Это оставляет вас зацикленными на использовании файлов cookie и других подобных инструментов.
Файлы cookie и другие подобные инструменты
Вы можете подумать о создании профиля данных, а затем использовать вероятностные тесты для определения вероятного пользователя . Профиль, полезный для этого, может быть создан с помощью некоторой комбинации следующего:
- Айпи адрес
- Реальный IP-адрес
- IP-адрес прокси (пользователи часто используют один и тот же прокси)
- Печенье
- Веб-ошибки (менее надежны, потому что ошибки исправляются, но все же полезны)
- Ошибка PDF
- Ошибка Flash
- Java Bug
- Браузеры
- Отслеживание кликов (многие пользователи посещают одну и ту же серию страниц при каждом посещении)
- Отпечаток пальца в браузерах - установленные плагины (у людей часто бывают разные, несколько уникальные наборы плагинов)
- Кэшированные изображения (люди иногда удаляют свои куки, но оставляют кэшированные изображения)
- Использование BLOB-объектов
- URL-адреса (история браузера или файлы cookie могут содержать уникальные идентификаторы пользователей в URL-адресах, например https://stackoverflow.com/users/1226894 или http://www.facebook.com/barackobama?fref=ts )
- Обнаружение системных шрифтов (это малоизвестная, но часто уникальная подпись ключа)
- HTML5 и Javascript
- HTML5 LocalStorage
- HTML5 Geolocation API и обратное геокодирование
- Архитектура, язык ОС, системное время, разрешение экрана и т. Д.
- 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 |
| Стороннее печенье | 60 | 4 |
| Флеш печенье | 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 |
| AOLTR | 70 | 4 |
| API информации о сети | 40 | 3 |
| API состояния батареи | 20 | 1 |
+ ------------------------- + -------- + ------------ +
Для каждого фрагмента информации, который вы можете собрать по данному запросу, присвойте соответствующий балл, а затем используйте « Важность» для разрешения конфликтов, когда оценки совпадают.
Доказательство концепции
Для простого подтверждения концепции взгляните на Perceptron . Персептрон - это модель РНК, которая обычно используется в приложениях для распознавания образов. Существует даже старый класс PHP, который отлично его реализует, но вам, вероятно, придется изменить его для своих целей.
Несмотря на то, что Perceptron является отличным инструментом, он все же может возвращать несколько результатов (возможных совпадений), поэтому использование сравнения Score and Difference по-прежнему полезно для определения лучших из этих совпадений.
Предположения
- Храните всю возможную информацию о каждом пользователе (IP, куки и т. Д.)
- Если результат точного соответствия, увеличьте счет на 1
- Если результат не является точным, уменьшите счет на 1
ожидание
- Создать метки РНК
- Генерация случайных пользователей, имитирующих базу данных
- Создать одного Неизвестного пользователя
- Генерация неизвестной пользовательской РНК и значений
- Система объединит информацию РНК и научит Перцептрона
- После тренировки персептрона система получит набор весов.
- Теперь вы можете протестировать шаблон неизвестного пользователя, и перцептрон выдаст набор результатов.
- Хранить все положительные совпадения
- Сортируйте совпадения сначала по очкам, затем по разнице (как описано выше).
- Выведите два ближайших совпадения или, если совпадений не найдено, выведите пустые результаты
Код для подтверждения концепции
$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)
Принт_р «Д»:
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 |
| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
от х1 до х20 представляют функции, преобразованные кодом.
// 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
Вывод
Идентификация пользователя без уникального идентификатора не является простой или простой задачей. это зависит от сбора достаточного количества случайных данных, которые вы можете получить от пользователя различными способами.
Даже если вы решите не использовать искусственную нейронную сеть, я предлагаю по крайней мере использовать простую матрицу вероятностей с приоритетами и вероятностями - и я надеюсь, что приведенный выше код и примеры дадут вам достаточно, чтобы продолжить.