无需 cookie 或本地存储的用户识别

IT技术 javascript php http-headers fingerprinting
2021-01-20 16:36:32

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

我想知道是否有可能在不使用 cookie 或本地存储的情况下检测到同一用户?我不期待这里的代码示例;只是一个简单的提示,告诉你在哪里可以看得更远。

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

6个回答

介绍

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

  • Cookie 可以删除
  • IP地址可以更改
  • 浏览器可以改变
  • 浏览器缓存可能被删除

Java Applet 或 Com Object 本来是使用硬件信息散列的简单解决方案,但现在人们非常关注安全性,很难让人们在他们的系统上安装这些类型的程序。这让您无法使用 Cookie 和其他类似工具。

Cookie 和其他类似工具

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

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

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

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

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

人工神经网络

PHPNeuralMesh库允许您生成人工神经网络。要实施贝叶斯推理,请查看以下链接:

这时候,你可能会想:

为什么对于一项看似简单的任务需要如此多的数学和逻辑?

基本上,因为这不是一项简单的任务实际上,您要实现的是Pure Probability例如,给定以下已知用户:

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 Cookie | 80 | 8 |
| 会话 Cookie | 80 | 6 |
| 第 3 方 Cookie | 60 | 4 |
| 快闪饼干 | 90 | 7 |
| PDF 错误 | 20 | 1 |
| 闪存错误 | 20 | 1 |
| Java 错误 | 20 | 1 |
| 常见页面 | 40 | 1 |
| 浏览器指纹 | 35 | 2 |
| 已安装的插件 | 25 | 1 |
| 缓存图像| 40 | 3 |
| 网址 | 60 | 4 |
| 系统字体检测 | 70 | 4 |
| 本地存储 | 90 | 8 |
| 地理定位 | 70 | 6 |
| AOLTR | 70 | 4 |
| 网络信息API | 40 | 3 |
| 电池状态API | 20 | 1 |
+-------------------------+--------+------------+

对于您可以根据给定请求收集的每条信息,授予相关分数,然后在分数相同时使用重要性解决冲突。

概念证明

有关概念的简单证明,请查看Perceptron感知器是一种RNA 模型,通常用于模式识别应用。甚至有一个旧的PHP 类可以完美地实现它,但您可能需要根据自己的目的修改它。

尽管是一个很棒的工具,感知器仍然可以返回多个结果(可能的匹配),因此使用分数和差异比较仍然有助于识别这些匹配中最佳匹配。

假设

  • 存储关于每个用户的所有可能信息(IP、cookies 等)
  • 如果结果完全匹配,则将分数增加 1
  • 如果结果不完全匹配,则将分数减 1

期待

  1. 生成 RNA 标签
  2. 生成模拟数据库的随机用户
  3. 生成单个未知用户
  4. 生成未知用户 RNA 和值
  5. 系统将合并 RNA 信息并教导感知器
  6. 训练感知器后,系统会有一组权重
  7. 您现在可以测试未知用户的模式,感知器将生成一个结果集。
  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”的打印_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 (Sensor & Desired), Initial Weights, Output (Sensor, Sum, Network), Error, Correction 和 Final Weights

+----+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+----+---------+---------+---------+---------+---------+---------+---------+---------+---------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----------+----+----+----+----+----+----+----+----+----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----------+
| 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 很棒的工作,我一直试图采用一些多级策略来识别用户,但正如您所说,可以清除缓存、更改 IP、代理或 NAT 背后的用户(尤其是那些人)、删除 cookie 等。 . 但即使付出了这么多努力,这也是一个概率问题,例如,如果坏用户正在使用Tor浏览器,那么大多数(如果不是全部)提到的检测策略都将不起作用。我喜欢browserleaks.com但随着 Tor 都返回未定义或未知
2021-03-13 16:36:32
只是一个注释,仅用于从这个出版物的宝石中“去除一些灰尘”:截至 17 年 9 月 7Implement Bayesian inference using PHP的断开链接列表:- ,所有 3 个部分。- Frequency vs Probability - Joint Probability -Input (Sensor & Desired), Initial Weights, Output (Sensor, Sum, Network), Error, Correction and Final Weights
2021-03-28 16:36:32
2021-03-31 16:36:32
@Baba“使用 Blob”对浏览器进行指纹识别是什么意思?
2021-04-01 16:36:32
@Baba 如何使用它来对浏览器进行指纹识别?只需在任何给定时间检查当前的内容?
2021-04-07 16:36:32

这种技术(在没有 cookie 的情况下检测相同的用户 - 甚至没有 ip 地址)称为浏览器指纹识别基本上,您尽可能地抓取有关浏览器的信息 - 使用 javascript、flash 或 java(例如已安装的扩展程序、字体等)可以获得更好的结果。之后,您可以根据需要存储散列结果。

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

83.6% 的浏览器拥有独特的指纹;在启用 Flash 或 Java 的用户中,94.2%。这不包括饼干!

更多信息:

@slash197 文件缓存怎么样?我的意思是使用 1px x 1px 透明闪存媒体以及一个 xml 文件,其中包含一个唯一生成的 ID(在将 xml 下载到用户本地 HD 之前,应该在服务器上创建一次),即使用户删除 cookie 或注销,您仍然可以使用动作脚本 sendAndLoad 方法建立一个桥接器。
2021-03-13 16:36:32
最小的变化将影响哈希结果。例如冲击波播放器的版本。使用本地存储的 xml 缓存文件并在浏览器上生成唯一密钥 + 隐藏 1px x 1px 闪存媒体(动作脚本)的可能解决方案,这样您就可以摆脱 cookie、会话过期问题(如果这是主要问题)。您仍然可以在 sql 数据库和用户本地计算机上的密钥之间建立桥梁。
2021-03-20 16:36:32
@Mbarry 我不是一个 Flash 粉丝,但如果浏览器中有一个 Flash 阻止插件,就像我禁用了 1x1 像素的 Flash 媒体一样,我说得对吗?
2021-03-29 16:36:32
我想,这仍然是答案。如果您需要识别设备,您只需要获取这些数据 - f.ex。操作系统、通用扩展(及其版本)、安装的字体等...
2021-03-31 16:36:32
这不会奏效。每个浏览器都支持会话和 cookie。为工作使用正确的工具。
2021-04-08 16:36:32

上面提到的指纹可以工作,但仍然会受到影响。

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

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

站点中的每个链接都使用此修饰符进行了调整。它类似于 ASP.Net 过去在页面之间使用 FORM 数据的工作方式。

我想到了,但这是用户修改它的最简单方法
2021-03-23 16:36:32
另外,当有人浏览网站时,这种方法是可以的,但是当一个返回的用户在一周后回来并且只输入网站地址而没有id时,您建议如何处理这种情况?
2021-03-24 16:36:32
@slash197 在那种情况下为什么不告诉用户登录,即使用户删除 cookie 也会发生什么。
2021-03-27 16:36:32
不是 id 是自引用哈希。使其加密安全。
2021-04-04 16:36:32

你研究过Evercookie吗?它可能会也可能不会跨浏览器工作。从他们的网站摘录。

“如果用户在一个浏览器上获取 cookie 并切换到另一个浏览器,只要他们仍然拥有本地共享对象 cookie,cookie 就会在两个浏览器中复制。”

我想知道它是否适用于禁用 JavaScript。你有什么经验吗?
2021-03-27 16:36:32
无论如何都行不通。从描述的第一行开始:'evercookie is a javascript API...'。如果 javascript 被禁用,它将不起作用。
2021-04-02 16:36:32
甚至不必禁用 js。Ghostery 和 uBlock 删除 evercookie
2021-04-06 16:36:32
它被称为 evercookie 是有原因的,无论如何它都会起作用。他们几乎不可能移除cookie。
2021-04-07 16:36:32

您可以使用缓存的 png 执行此操作,它会有些不可靠(不同的浏览器行为不同,如果用户清除缓存会失败),但这是一个选项。

1:建立一个数据库,以十六进制字符串的形式存储唯一的用户ID

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/...
2021-03-15 16:36:32
你在哪里看到的,他的问题只要求“忘记提到它需要跨浏览器兼容”,即在任何浏览器中工作。
2021-04-03 16:36:32
他想要一些可以“跨浏览器”跟踪用户的东西,这在这里不起作用(每个浏览器都有自己的缓存数据库)。
2021-04-09 16:36:32