没有Cookie或本地存储的用户识别


132

我正在构建一个分析工具,目前可以从其用户代理获取用户的IP地址,浏览器和操作系统。

我想知道是否有可能在不使用Cookie或本地存储的情况下检测到同一用户?我不希望在这里有代码示例。只是进一步了解的简单提示。

忘记提及如果它是同一台计算机/设备,则需要跨浏览器兼容。基本上,我是在设备识别之后才真正不是用户。


5
并非如此-至少不是您可以依靠的任何方式都是准确的。也许是这三者结合在一起的哈希值,但是,如果一个房子中有多个人使用相同的浏览器和操作系统,则仍然无法使用。而且,大多数ISP提供动态IP地址,这意味着它们经常更改,并且也不能依靠它们进行标识。
2012年

2
然后,您不知道什么会话。您的用例正是会话设计的目的。会话与登录或身份验证无关。您的Web服务器将告诉客户端发送带有会话标识符的cookie。您可以使用客户端发送给您的会话ID来识别该客户端。
男性vs代码

4
Cookies仍然可以使用吗?为什么要避免使用Cookie?
巴巴

2
这真的很简单,我一直使用它,请用户输入用户名和密码!
阿米特·克里普兰尼

2
这是一个最小的javascript解决方案(在这种情况下为非跨浏览器):github.com/carlo/jquery-browser-fingerprint我提到了它,因为它使我想到默认情况下许多插件都是跨浏览器安装的,而没有用户方面的任何选择。仔细地将其分类(这不是一个小任务,但仍然...)可能会导致较大的基于设备的指纹的浏览器不可知属性。
hexalys

Answers:


389

介绍

如果我对您的理解正确,则需要确定一个没有唯一标识符的用户,因此您想通过匹配随机数据来确定他们是谁。您不能可靠地存储用户的身份,因为:

  • Cookies可以删除
  • IP地址可以更改
  • 浏览器可以更改
  • 浏览器缓存可能已删除

Java Applet或Com Object使用硬件信息的哈希本来是一种简单的解决方案,但是如今人们对安全性的意识如此之深,以至于很难让人在他们的系统上安装这类程序。这使您无法使用Cookie和其他类似工具。

Cookies和其他类似工具

您可能考虑先构建一个数据概要文件,然后使用概率测试来识别一个可能的用户。可以通过以下某种组合来生成对此有用的配置文件:

  1. IP地址
    • 真实IP地址
    • 代理IP地址(用户经常重复使用同一代理)
  2. 饼干
  3. Web错误(可靠性较差,因为错误已得到修复,但仍然有用)
    • PDF错误
    • Flash错误
    • Java错误
  4. 浏览器
    • 点击跟踪(许多用户每次访问都会访问相同系列的页面)
    • 浏览器指纹-已安装的插件(人们通常会使用多种多样且有些独特的插件集)
    • 缓存的图像(人们有时会删除其Cookie,但保留缓存的图像)
    • 使用斑点
    • URL(浏览器历史记录或cookie可能在URL中包含唯一的用户ID,例如https://stackoverflow.com/users/1226894http://www.facebook.com/barackobama?fref=ts
    • 系统字体检测(这是一个鲜为人知但通常唯一的密钥签名)
  5. HTML5和Javascript
    • HTML5 LocalStorage
    • HTML5地理位置API和反向地理编码
    • 架构,操作系统语言,系统时间,屏幕分辨率等
    • 网络信息API
    • 电池状态API

当然,我列出的项目仅是几种可以唯一标识用户的可能方式。还有更多。

使用这组随机数据元素从中构建数据配置文件,下一步是什么?

下一步是开发一些模糊逻辑,或者更好的是开发一个人工神经网络(使用模糊逻辑)。无论哪种情况,其想法都是先训练您的系统,然后将其训练与贝叶斯推理相结合,以提高结果的准确性。

人工神经网络

用于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是一个出色的工具,但它仍然可以返回多个结果(可能的匹配项),因此使用“得分和差异”比较仍然可以帮助您识别出最好的匹配项。

假设条件

  • 存储有关每个用户的所有可能信息(IP,Cookie等)
  • 如果结果完全匹配,则将分数提高1
  • 如果结果不完全匹配,则将分数降低1

期望

  1. 生成RNA标签
  2. 生成模拟数据库的随机用户
  3. 生成一个未知用户
  4. 生成未知的用户RNA和值
  5. 该系统将合并RNA信息并教授感知器
  6. 训练感知器后,系统将具有一组权重
  7. 现在,您可以测试未知用户的模式,Perceptron将产生一个结果集。
  8. 存储所有肯定的比赛
  9. 首先按照得分,然后按照差异对比赛进行排序(如上所述)
  10. 输出两个最接近的匹配项,或者,如果找不到匹配项,则输出空结果

概念验证代码

$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

结论

识别没有唯一标识符的用户并不是一件简单而简单的任务。它取决于收集足够数量的随机数据,您可以通过多种方法从用户那里收集这些数据。

即使您选择不使用人工神经网络,我也建议至少使用具有优先级和可能性的简单概率矩阵-我希望上面提供的代码和示例能够为您提供足够的帮助。


@Baba“使用Blob”对浏览器进行指纹识别是什么意思?
billmalarky 2014年


1
@Baba怎么用它来给浏览器指纹?只需在任何给定时间检查当前内容是什么?
billmalarky 2014年

@Baba的出色工作,我一直尝试采用一些多层次的策略来识别用户,但是正如您所说的,可以清除缓存,更改IP,代理或NAT之后的用户(尤其是那些人),删除Cookie等。但是,即使付出了这么多的努力,这还是很可能的,例如,如果不良用户正在使用Tor浏览器,则提到的大多数(如果不是全部)检测策略将无法正常工作。我喜欢browserleaks.com,但都带着Tor回来了,但都不确定或未知
Mi-Creativity

只是一个注释,其目的仅是“消除某些出版物中的灰尘”:截至07.09.17的断开链接列表:- 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
齐齐

28

这种技术(用于检测没有Cookie或什至没有ip地址的相同用户)称为浏览器指纹识别。基本上,您会尽可能地浏览有关浏览器的信息-使用javascript,flash或java(例如已安装的扩展名,字体等)可以达到更好的效果。之后,您可以根据需要存储散列结果。

它不是万无一失的,但是:

所见浏览器的83.6%具有独特的指纹;其中启用Flash或Java的用户占94.2%。这不包括cookie!

更多信息:


我认为,这仍然是答案。如果您需要识别设备,则只需获取这些数据-f.ex。操作系统,通用扩展名(及其版本),已安装的字体等…
pozs 2013年

这不能很好地工作。每个浏览器都支持会话和cookie。使用正确的工具完成工作。
男性vs代码

1
@ slash197文件缓存如何?我的意思是使用1px x 1px透明闪存介质以及一个内部保存有唯一生成ID的xml文件(该xml应该在服务器上创建一次,然后再下载到用户本地HD),即使用户删除cookie或注销,您仍然可以使用动作脚本sendAndLoad方法建立桥梁。
Mbarry

变化的最小值将影响哈希结果。例如冲击波播放器的版本。本地存储的xml缓存文件的可能解决方案,该文件在浏览器上生成了唯一的密钥+隐藏的1px x 1px Flash媒体(动作脚本),这样您就可以摆脱Cookie,如果这是主要问题,则可以避免会话过期。您仍然可以在sql数据库和用户本地计算机上的密钥之间建立桥梁。
Mbarry

@Mbarry我不是一个闪光灯爱好者,但是如果浏览器中有一个Flash阻止加载项,例如我禁用了1x1像素的Flash媒体,我是否可以?
slash197

7

上面提到的指纹验证有效,但仍然会产生细菌。

一种方法是将UID添加到与用户每次交互的url中。

http://someplace.com/12899823/user/profile

站点中的每个链接都使用此修饰符进行修改的地方。它类似于ASP.Net用于在页面之间使用FORM数据的方式。


我想到了,但这是用户修改它的最简单方法
slash197

1
不是id是自引用哈希。使它在密码上安全。
贾斯汀·亚历山大

另外,当某人正在浏览该网站时,此方法还可以,但是当回访的用户在一周后回来并仅输入不带id的网站地址时,您如何建议处理这种情况?
slash197

在这种情况下,@ slash197为什么不告诉用户登录,即使用户删除cookie也会发生什么。
Akash Kava

6

您有看过Evercookie吗?它可能无法在所有浏览器上运行。他们网站的摘录。

“如果用户在一个浏览器上获得cookie并切换到另一浏览器,只要他们仍然具有本地共享对象cookie,该cookie将在两个浏览器中重现。”


我想知道它是否与禁用JavaScript一起工作。你有经验吗?
Voitcus

它之所以被称为evercookie,是因为它无论如何都会起作用。他们几乎无法删除Cookie。
亚历克西斯·泰勒

无论如何,它都行不通。在描述的第一行:“ evercookie是javascript API ...”。如果禁用了javascript,它将无法正常工作。
gdw2 2014年

甚至不必禁用js。Ghostery和uBlock放弃evercookie
opengrid's

3

您可以使用缓存的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,现在已缓存在您的用户计算机上。


他想要一种可以在“跨浏览器”中跟踪用户的东西,但在这里无法使用(每个浏览器都有自己的缓存数据库)。
EricLaw

在哪里可以看到,他的问题仅要求“忘记提及它必须与跨浏览器兼容”,即可以在任何浏览器中使用。
hobberwickey 2015年

他的问题写得不好。I'm after device recognition是他想要的东西的赠品,他阐述的位置:stackoverflow.com/questions/15966812/...
EricLaw

2

根据您的意见:

基本上我是在设备识别之后才真正不是用户

最好的方法是发送mac地址(即NIC ID)。

您可以看一下这篇文章: 如何在PHP中获取连接的客户端的MAC和IP地址?


抱歉,但NIC ID很容易被欺骗。绝对不是最好的方法。
ass

@asgs浏览器指纹识别可能会更好,或者您认为最好的方法是什么?
Mehdi Karamosly,2015年

没有最好的方法,那就是它的可悲之处。但是,在我看来,结合Baba上面提出的概率研究和浏览器指纹识别是最好的。
ass 2015年

1

您可以使用etags。尽管我不确定这是一堆诉讼法律。

如果您适当地警告用户,或者您拥有类似Intranet网站的内容,那可能没问题。


Etag与跨浏览器不兼容。
slash197

1
Etag是HTTP / 1.1规范的一部分。所有流行的浏览器都支持etags,您几乎需要编写自己的浏览器以不支持ETag / If-None-Match标头。
Brian McGinity

我不是说它不支持它,而是说它与跨浏览器不兼容。如果将标记保存在Firefox中,则该标记在chrome中不可用,因此由于没有缓存,因此将再次下载内容。
slash197

现在我明白了你在说什么。你是对的。每个浏览器都有自己的缓存存储区,因此具有不同的etag。
Brian McGinity


0

效率低下,但可能会给您带来理想的结果,那就是轮询您这一边的API。在客户端有一个后台进程,该进程每隔一段时间发送一次用户数据。您需要一个用户标识符才能发送到您的API。一旦有了,您就可以发送与该唯一标识符关联的任何信息。

这消除了对cookie和本地存储的需求。


0

我不敢相信,http://browserspy.dk在这里仍然没有被提及!该站点描述了许多功能(就模式识别而言),可用于构建分类器。

当然,为了评估这些功能,我建议使用支持向量机,尤其是libsvm


0

在一个会话期间或跨会话跟踪它们?

如果您的站点是HTTPS Everywhere,则可以使用TLS会话ID来跟踪用户的会话


1
这里的问题是如何?
user455318 '02

-2
  1. 创建一个跨平台的虚拟(nsapi)插件,并在用户下载插件名称或版本时(例如,登录后)生成唯一名称。
  2. 提供插件的安装程序/根据策略进行安装

这将要求用户自愿安装标识符。

安装插件后,任何(启用插件的)浏览器的指纹都将包含此特定插件。要将信息返回给服务器,需要一种在客户端有效检测插件的算法,否则IE和Firefox> = 28用户将需要一个可能的有效标识表。

这要求对一项可能被浏览器供应商关闭的技术进行相对较高的投资。当您能够说服用户安装插件时,可能还有诸如安装本地代理,使用vpn或修补网络驱动程序之类的选项。

不想被识别的用户(或他们的机器)总会找到一种方法来阻止它。


嗨,欢迎堆栈溢出。请注意; this will require the user to willingly install the identifier.可能不是原始海报(OP)的意思。
Stefan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.