没有Cookie或本地存储的用户识别
我正在构build一个分析工具,我目前可以从其用户代理获取用户的IP地址,浏览器和操作系统。
我想知道是否有可能不使用cookie或本地存储检测同一用户? 我不期待在这里的代码示例; 只是一个简单的提示,在哪里看得更远。
忘了提及,如果它是相同的计算机/设备,它将需要跨浏览器兼容。 基本上我是在设备识别之后不是真正的用户。
介绍
如果我正确地理解了你,你需要确定一个你没有唯一标识符的用户,所以你想通过匹配随机数据来找出他们是谁。 您不能可靠地存储用户的身份,因为:
- Cookies可以被删除
- IP地址可以更改
- 浏览器可以改变
- 浏览器caching可能被删除
一个Java小程序或Com对象本来就是一个使用硬件信息散列的简单解决scheme,但是现在人们非常安全,很难让人们在他们的系统上安装这些types的程序。 这使您坚持使用Cookie和其他类似的工具。
Cookie和其他类似的工具
您可以考虑构build数据configuration文件,然后使用概率testing来识别可能的用户 。 一个有用的configuration文件可以通过下面的一些组合产生:
- IP地址
- 真实的IP地址
- 代理IP地址(用户经常使用相同的代理)
- cookies
- HTTP Cookie
- 会话Cookie
- 第三方Cookie
- 闪存cookies( 大多数人不知道如何删除这些 )
- Web Bug(不太可靠,因为错误得到修复,但仍然有用)
- PDF错误
- Flash Bug
- Java错误
- 浏览器
- 点击跟踪(许多用户在每次访问时访问同一系列页面)
- 浏览器指纹 – 安装的插件(人们通常有不同的,有点独特的插件集)
- caching的图像(人们有时删除他们的cookie,但留下caching的图像)
- 使用Blob
- URL(浏览器历史logging或Cookie可能在URL中包含唯一的用户标识,例如https://stackoverflow.com/users/1226894或http://www.facebook.com/barackobama?fref=ts )
- 系统字体检测 (这是一个鲜为人知但通常是唯一的密钥签名)
- HTML5和Javascript
- HTML5 LocalStorage
- HTML5地理位置API和反向地理编码
- 体系结构,操作系统语言,系统时间,屏幕分辨率等
- networking信息API
- 电池状态API
我列出的项目当然是用户可以唯一识别的几种可能的方式。 还有更多。
使用这组随机数据元素来构build数据configuration文件,下一步是什么?
下一步是开发一些模糊逻辑 ,或者更好的是人工neural network (使用模糊逻辑)。 无论哪种情况,这个想法都是训练你的系统,然后把它的训练和贝叶斯推理结合起来,以提高结果的准确性。
PHP的NeuralMesh库允许您生成人工neural network。 要实现贝叶斯推理,请查看以下链接:
- 使用PHP实现贝叶斯推断,第1部分
- 使用PHP实现贝叶斯推断,第2部分
- 使用PHP实现贝叶斯推断,第3部分
在这一点上,你可能会想:
为什么这么多的math和逻辑看似简单的任务?
基本上,因为这不是一个简单的任务 。 实际上,你试图达到的是纯粹的概率 。 例如,给定以下已知的用户:
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的概率是多less? 那两场比赛哪一场最有可能?
为了有效地回答这个问题,您需要了解频率与概率格式以及为什么联合概率可能是更好的方法。 这里的细节太多了(这就是为什么我给你的链接),但一个很好的例子是一个医疗诊断向导应用程序 ,它使用症状的组合来识别可能的疾病。
想象一下构成您的数据档案(上述例子中的B + C + E + G + F + K)作为症状的一系列数据点,以及作为疾病的未知用户。 通过识别疾病,你可以进一步确定一个适当的治疗方法(把这个用户当作User1)。
很明显,我们发现一种以上症状的疾病更容易识别。 事实上,我们可以识别的症状越多,我们的诊断就越容易和准确。
还有其他的select吗?
当然。 作为替代措施,您可以创build自己的简单评分algorithm,并将其基于完全匹配。 这并不像概率那么高效,但是对于您来说可能更简单。
作为一个例子,考虑这个简单的分数图:
+ ------------------------- + -------- + ------------ + | 属性| 重量| 重要性| + ------------------------- + -------- + ------------ + | 真实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 | | caching的图像| 40 | 3 | | url| 60 | 4 | | 系统字体检测| 70 | 4 | | 本地存储| 90 | 8 | | 地理位置| 70 | 6 | | AOLTR | 70 | 4 | | networking信息API | 40 | 3 | | 电池状态API | 20 | 1 | + ------------------------- + -------- + ------------ +
对于您可以在给定请求中收集的每条信息,授予相关分数,然后使用“ 重要性”在分数相同时解决冲突。
概念validation
对于一个简单的概念certificate,请看Perceptron 。 感知器是通常用于模式识别应用的RNA模型 。 甚至有一个完美实现它的旧PHP类 ,但你可能需要修改它为你的目的。
Perceptron尽pipe是一个很棒的工具,但仍然可以返回多个结果(可能的匹配),所以使用Score和Difference比较对于识别这些匹配中的最好结果仍然是有用的。
假设
- 存储关于每个用户的所有可能的信息(IP,cookies等)
- 如果结果完全匹配,则将分数提高1
- 如果结果不完全匹配,请将分数降低1
期望
- 生成RNA标签
- 生成模拟数据库的随机用户
- 生成一个未知的用户
- 生成未知的用户RNA和值
- 系统将合并RNA信息并教授感知器
- 感知器训练完毕后,系统会有一组权重
- 你现在可以testingUnknown用户的模式,Perceptron将产生一个结果集。
- 存储所有正面匹配
- 首先按比分对比赛进行sorting,然后按差分(如上所述)
- 输出两个最接近的匹配,或者,如果找不到匹配,则输出空结果
概念validation码
$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(传感器和期望),初始权重,输出(传感器,总和,networking),错误,校正和最终权重 。
+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+ | 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代表由代码转换的function。
// 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
结论
识别没有唯一标识符的用户不是一个简单或简单的任务。 它依赖于收集足够数量的随机数据,您可以通过多种方法从用户那里收集这些数据。
即使你select不使用人工neural network,我也build议至less使用一个简单的概率matrix来表示优先级和可能性 – 我希望上面提供的代码和例子足以让你继续。
这种技术(检测没有cookie的同一用户 – 甚至没有ip地址)被称为浏览器指纹 。 基本上,您可以抓取有关浏览器的信息 – 使用JavaScript,Flash或Java(f.ex.安装的扩展名,字体等)可以获得更好的结果。 之后,如果需要,可以将结果散列存储。
这是不可靠的,但是:
83.6%的浏览者有独特的指纹; 在启用了Flash或Java的用户中,为94.2%。 这不包括cookies!
更多信息:
上面提到的拇指打印工作,但仍然可能遭受歧视。
一种方法是将UID添加到与用户的每次交互的URL中。
http://someplace.com/12899823/user/profile
网站中的每个链接都适用于此修饰符。 这与ASP.Net用于在页面之间使用FORM数据的方式类似。
你看了Evercookie ? 它可能或不可以跨浏览器工作。 从他们的网站摘录。
“如果用户在一个浏览器上煮熟并切换到另一个浏览器,只要他们仍然有本地共享对象的cookie,cookie将在两个浏览器中重现。
你可以用一个caching的png来做到这一点,这将是有点不可靠(不同的浏览器行为不同,如果用户清除caching,它将失败),但这是一个选项。
1:build立一个数据库,存储一个唯一的用户id作为hexstring
2:创build一个genUser.php(或任何语言)文件,生成一个用户ID,将其存储在数据库中,然后创build一个真正的颜色.png的hexstring的值(每个像素将是4字节),并返回即浏览器。 一定要设置内容types和caching标题。
3:在HTML或JS中创build一个像<img id='user_id' src='genUser.php' />
4:将该图像绘制到canvasctx.drawImage(document.getElementById('user_id'), 0, 0);
5:使用ctx.getImageData
读取该图像的字节,并将整数转换为hexstring。
6:这是您现在caching在用户计算机上的唯一用户标识。
根据你所说的话:
基本上我是在设备识别之后不是真正的用户
最好的办法是发送作为NIC ID的MAC地址。
你可以看看这篇文章: 我怎样才能获得一个连接客户端的MAC和IP地址在PHP中?
JavaScript的Macsearch器
你可以用etags来完成。 虽然我不确定这个法律是否是一堆诉讼案件。
如果你正确地警告你的用户,或者你有类似的内部网站这样的东西,那也可以。
您可能会创build一个blob来存储设备标识符…
缺点是用户需要下载blob( 可以强制下载 ),因为浏览器无法访问文件系统来直接保存文件。
参考:
https://www.inkling.com/read/javascript-definitive-guide-david-flanagan-6th/chapter-22/blobs
效率低下,但可能会给你想要的结果,将轮询一个API在你身边。 在间隔发送用户数据的客户端有一个后台进程。 您将需要一个用户标识符发送到您的API。 一旦你有,你可以发送任何信息关联到唯一的标识符。
这消除了对cookie和本地存储的需求。
我不敢相信, http://browserspy.dk在这里还没有提到!; 该网站描述了许多特征(在模式识别方面),可以用来build立一个分类器。
而原因,评估function,我会build议特别支持向量机和libsvm 。
跟踪他们在一个会议或跨会议?
如果您的站点是HTTPS Everywhere,则可以使用TLS会话ID来跟踪用户的会话
- 创build一个跨平台的虚拟(nsapi)插件,并在用户下载插件时(例如login后)为插件名称或版本生成一个唯一的名称。
- 为插件提供安装程序/根据策略进行安装
这将要求用户自愿安装标识符。
一旦插件安装,任何(插件启用)浏览器的指纹将包含这个特定的插件。 要将信息返回给服务器,需要在客户端有效检测插件的algorithm,否则IE和Firefox> = 28的用户将需要一个可能的有效标识表。
这需要对可能被浏览器供应商closures的技术进行相对高的投资。 当你能够说服你的用户安装一个插件时,可能还会有一些选项,例如安装本地代理 ,使用vpn或修补networking驱动程序。
不想被识别的用户(或他们的机器)总是会find一种方法来防止它。