我正在构建一个分析工具,目前可以从其用户代理获取用户的IP地址,浏览器和操作系统。
我想知道是否有可能在不使用Cookie或本地存储的情况下检测到同一用户?我不希望在这里有代码示例。只是进一步了解的简单提示。
忘记提及如果它是同一台计算机/设备,则需要跨浏览器兼容。基本上,我是在设备识别之后才真正不是用户。
我正在构建一个分析工具,目前可以从其用户代理获取用户的IP地址,浏览器和操作系统。
我想知道是否有可能在不使用Cookie或本地存储的情况下检测到同一用户?我不希望在这里有代码示例。只是进一步了解的简单提示。
忘记提及如果它是同一台计算机/设备,则需要跨浏览器兼容。基本上,我是在设备识别之后才真正不是用户。
Answers:
介绍
如果我对您的理解正确,则需要确定一个没有唯一标识符的用户,因此您想通过匹配随机数据来确定他们是谁。您不能可靠地存储用户的身份,因为:
Java Applet或Com Object使用硬件信息的哈希本来是一种简单的解决方案,但是如今人们对安全性的意识如此之深,以至于很难让人在他们的系统上安装这类程序。这使您无法使用Cookie和其他类似工具。
Cookies和其他类似工具
您可能考虑先构建一个数据概要文件,然后使用概率测试来识别一个可能的用户。可以通过以下某种组合来生成对此有用的配置文件:
当然,我列出的项目仅是几种可以唯一标识用户的可能方式。还有更多。
使用这组随机数据元素从中构建数据配置文件,下一步是什么?
下一步是开发一些模糊逻辑,或者更好的是开发一个人工神经网络(使用模糊逻辑)。无论哪种情况,其想法都是先训练您的系统,然后将其训练与贝叶斯推理相结合,以提高结果的准确性。
用于PHP 的NeuralMesh库允许您生成人工神经网络。要实现贝叶斯推理,请查看以下链接:
此时,您可能在想:
为什么要为看似简单的任务花费那么多数学和逻辑?
基本上,因为这不是一个简单的任务。实际上,您要实现的目标是“ 纯概率”。例如,给定以下已知用户:
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)。
显然,我们已经识别出多种症状的疾病更容易识别。其实症状比较多我们可以确定的越多,几乎可以肯定我们的诊断就越容易和准确。
还有其他选择吗?
当然。作为一种替代方法,您可以创建自己的简单评分算法,并将其基于完全匹配。这不像概率那样有效,但是对您来说可能更容易实现。
例如,考虑以下简单的得分表:
+ ------------------------- + -------- + ------------ + | 物业| 重量 重要性| + ------------------------- + -------- + ------------ + | 真实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错误| 20 | 1 | | 常用页面| 40 | 1 | | 浏览器指纹| 35 | 2 | | 已安装的插件| 25 | 1 | | 缓存的图像| 40 | 3 | | 网址| 60 | 4 | | 系统字体检测| 70 | 4 | | 本地存储| 90 | 8 | | 地理位置| 70 | 6 | | 奥特| 70 | 4 | | 网络信息API | 40 | 3 | | 电池状态API | 20 | 1 | + ------------------------- + -------- + ------------ +
对于可以根据给定请求收集的每条信息,授予相关分数,然后在分数相同时使用重要性解决冲突。
概念验证
对于概念的简单证明,请查看Perceptron。Perceptron是一种通常用于模式识别应用程序的RNA模型。甚至有一个旧的PHP类都可以完美地实现它,但是您可能需要出于自己的目的对其进行修改。
尽管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)
“ D”的Print_r:
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,您将能够看到Input(传感器和期望),初始权重,Output(传感器,总和,网络),错误,校正和最终权重。
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 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
结论
识别没有唯一标识符的用户并不是一件简单而简单的任务。它取决于收集足够数量的随机数据,您可以通过多种方法从用户那里收集这些数据。
即使您选择不使用人工神经网络,我也建议至少使用具有优先级和可能性的简单概率矩阵-我希望上面提供的代码和示例能够为您提供足够的帮助。
Implement Bayesian inference using PHP
,共3部分。- Frequency vs Probability
- Joint Probability
-Input (Sensor & Desired), Initial Weights, Output (Sensor, Sum, Network), Error, Correction and Final Weights
这种技术(用于检测没有Cookie或什至没有ip地址的相同用户)称为浏览器指纹识别。基本上,您会尽可能地浏览有关浏览器的信息-使用javascript,flash或java(例如已安装的扩展名,字体等)可以达到更好的效果。之后,您可以根据需要存储散列结果。
它不是万无一失的,但是:
所见浏览器的83.6%具有独特的指纹;其中启用Flash或Java的用户占94.2%。这不包括cookie!
更多信息:
上面提到的指纹验证有效,但仍然会产生细菌。
一种方法是将UID添加到与用户每次交互的url中。
http://someplace.com/12899823/user/profile
站点中的每个链接都使用此修饰符进行修改的地方。它类似于ASP.Net用于在页面之间使用FORM数据的方式。
您有看过Evercookie吗?它可能无法在所有浏览器上运行。他们网站的摘录。
“如果用户在一个浏览器上获得cookie并切换到另一浏览器,只要他们仍然具有本地共享对象cookie,该cookie将在两个浏览器中重现。”
您可以使用缓存的png来执行此操作,这在某种程度上是不可靠的(不同的浏览器的行为有所不同,如果用户清除其缓存则失败),但这是一个选择。
1:设置一个数据库,该数据库将唯一的用户标识存储为十六进制字符串
2:创建一个genUser.php(或任何语言)文件,该文件生成用户ID,将其存储在数据库中,然后从该十六进制字符串的值(每个像素为4个字节)中创建一个真彩色.png并返回到浏览器。确保设置内容类型和缓存头。
3:在HTML或JS中创建类似的图像 <img id='user_id' src='genUser.php' />
4:将该图像绘制到画布上 ctx.drawImage(document.getElementById('user_id'), 0, 0);
5:使用读取该图像的字节 ctx.getImageData
,然后将整数转换为十六进制字符串。
6:这是您的唯一用户ID,现在已缓存在您的用户计算机上。
I'm after device recognition
是他想要的东西的赠品,他阐述的位置:stackoverflow.com/questions/15966812/...
您可以使用etags。尽管我不确定这是一堆诉讼法律。
如果您适当地警告用户,或者您拥有类似Intranet网站的内容,那可能没问题。
您可能会创建一个Blob来存储设备标识符...
缺点是用户需要下载Blob(可以强制进行下载),因为浏览器无法访问文件系统来直接保存文件。
参考:
https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-22/blobs
我不敢相信,http://browserspy.dk在这里仍然没有被提及!该站点描述了许多功能(就模式识别而言),可用于构建分类器。
当然,为了评估这些功能,我建议使用支持向量机,尤其是libsvm。
这将要求用户自愿安装标识符。
安装插件后,任何(启用插件的)浏览器的指纹都将包含此特定插件。要将信息返回给服务器,需要一种在客户端有效检测插件的算法,否则IE和Firefox> = 28用户将需要一个可能的有效标识表。
这要求对一项可能被浏览器供应商关闭的技术进行相对较高的投资。当您能够说服用户安装插件时,可能还有诸如安装本地代理,使用vpn或修补网络驱动程序之类的选项。
不想被识别的用户(或他们的机器)总会找到一种方法来阻止它。
this will require the user to willingly install the identifier.
可能不是原始海报(OP)的意思。