如何判断用户是否访问过某个网址

2024-09-03 13:08

本文主要是介绍如何判断用户是否访问过某个网址,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如何判断用户是否访问过某个网址

我们经常有这样的需求:想知道用户之前有没有访问过某个网址。有没有什么方法或技术能实现这一点呢?

初步探索

注意到,在大部分浏览器默认设置里,用户访问过的链接和没访问过的链接颜色是不同的,如下图:

Snip20130811_28.png

即用户访问过的链接,computed color默认为紫色(或其他在CSS中指定的颜色):

Snip20130811 25

而没访问过的链接,computed color默认为蓝色(或其他在CSS中指定的颜色):

Snip20130811 27

那是不是说,我们可以在页面上加上我们感兴趣的链接,然后用JavaScript取得这些链接文本实际的颜色,即可知道用户是否访问过指定网址呢?

这个方法真的有效过。2010年有一篇安全文章上即介绍了这种方法,并将这类方法称为“历史嗅探”(history sniffing)。

遗憾的是,各大浏览器厂商都已经注意到了这个问题,根据我的测试,目前最新的浏览器中都对这个问题进行了防范,获取超链接的Computed Style时,无论这个链接是否被访问过,取得的颜色都是未访问过时的那种颜色。

看起来根据颜色获取的方案目前行不通了,不过神奇的是,现在我们有了另外一种方案。

requestAnimationFrame

Context Information Security公司最近出了一份名为《PIXEL PERFECT TIMING ATTACKS WITH HTML5》的报告,其中提到了一种很有创意的方案:使用HTML5中的requestAnimationFrame,根据浏览器渲染已访问过及未访问过的链接的时间差,判断指定链接是否访问过。

现代浏览器绘图时每一帧的流程大致如下图所示:

Browser rendering phases

大致流程为:JS修改某个元素的样式,浏览器重新计算对应元素的外观及位置,然后将它们绘制出来,这个过程即是一帧。而requestAnimationFrame的作用则是可以注册一个函数,在下一帧开始绘制之前进行调用。

requestAnimationFrame的初衷是让开发人员可以更好地管理动画,绘制更平滑的动画,如这篇博客中所说的。不过,这个接口也让获取不同元素的渲染时间成为了可能。

工作原理

在开始之前,我们需要了解的是浏览器是如何渲染访问过的链接和未访问过的链接的。

当浏览器渲染一个页面时,浏览器必须区分出某个链接是否曾访问过。每个浏览器都有一个记录访问过的链接的数据库,此时它要做的就是从这个数据库中查询指定的URL是否存在。

IE与Firefox中,如果链接已经渲染到页面上了,查询还没完成,浏览器会先使用“未访问过”的样式来渲染,查询结果返回时,如果指定链接是已访问过,那么浏览器就重绘指定的链接。而这个“重绘”是需要时间的,可以使用requestAnimationFrame来监测。

Chrome浏览器和Firefox、IE不同,它会一直等到数据库URL查询完成才将链接渲染到屏幕上。

除了初始渲染之外,使用JavaScript修改链接的href也有可能引发浏览器重绘。测试显示,在Firefox中,修改一个链接的href,如果改变了它的“已访问”状态,则会引发重绘。但在IE中这不能工作,一个链接一旦创建,改变href永远也不会同时改变它的“已访问”状态。

Chrome中有点例外,只改变href并不会引发重绘,不过,如果在改变href的同时也“触碰”一下链接的样式(但不修改),则当新href需要改变“已访问”状态时,浏览器会重绘对应的链接。

所谓的“触碰”指的是类似这样的操作:

简单来说,基本原理就是这样:创建链接,改变它的href,使用requestAnimationFrame来监测接下来若干帧的耗时,判断是否发生了重绘,如果发生了重绘,说明指定链接的“已访问”状态发生了变化,即可判断出指定链接是否被访问过。

当然,实际操作过程中还有不少问题需要考虑,比如,浏览器渲染通常都非常快,重绘的时间可能也会非常短,导致完全无法区分。解决方案主要有两个,一是增加链接数,创建多个A链接,指向同一个URL,需要时使用JS同时改变这些A链接的href属性为另一个值。另一个方案是给元素加一些耗时的样式,比如文字阴影,并且让模糊半径尽可能大,这样在重绘时需要的时间就会多很多了。

实践

我写了一个针对Chrome浏览器的demo,你可以使用Chrome访问 http://oldj.net/static/history-sniffing/test.html 测试。

这个测试需要一个已知的已访问过的URL作为基准链接,既然你在读这篇博客,并且可能会点击上面的测试链接,那我就把上面的那个测试链接作为基准链接了。接下来,依次测试各个链接,看是否会引发重绘,据此判断你是否曾经访问过指定链接。

效果如下图:

Snip20130811 29

已访问过的链接除了会使用紫色显示(这是浏览器自带的功能)外,我还在前面加了一个“[v]”标记,未访问过的链接前面则是“[ ]”。另外,你也可以在下面的输入框中输入新的URL,看看能不能正确判断。

原理即是上面提到的,修改链接地址后,根据接下来各帧的时间,判断是否发生了重绘(redraw),如果有,则认为链接的已访问状态发生了改变。

Redraw 1

Redraw 2

这是一个Chrome的例子,根据contextis的白皮书,稍加改造,即可适用于IE及Firefox。

更多

这种通过判断渲染时间来获取信息的方式非常可怕,它的强大之处在于可以获取第三方网站的访问记录,并且完全不需要用户知情。比如,可以通过这个方式了解用户是否访问过竞争对手的网站,或者了解用户是否访问过指定的限制级网站。

Princeton大学一篇2000年的论文《Timing Attacks on Web Privacy》就已经指出了这类计时攻击可能会泄露用户隐私。这篇论文里也提出了一种判断用户是否访问过指定网址的方法,做法是在当前页面加载指定网站的固定资源(访问过那个网站的用户都会下载的资源),比如logo或js文件,如果用户之前访问过,则浏览器会从本地缓存中读取对应的资源,速度会快很多,否则会重新下载。使用JavaScript或Java applet(2000年的论文,那时Java applet还很流行)可以度量这个时间,进而判断用户是否曾访问过指定网址。

这个十几年前提出的方案目前仍然是有效的,不过相对来说,本文中提出的方案更为先进,效率也更高。

另外,除了客户端时间外,服务器端时间也有可能泄露意想不到的数据。比如来自Stanford的论文《Exposing Private Information by Timing Web Applications》中提到,由于后台在处理不同类型的数据时使用的逻辑不同,耗费的时间也不同,因此,只要构造适当的请求,反复执行并度量时间,就可能获得很多隐私信息。

如何防范此类计时攻击呢?目前似乎没有很好的办法。对服务器端来说,最好的方案或许是使用一些手段,让所有请求的返回时间常量化,比如统一为500毫秒,如果某个请求早于500毫秒完成,就休眠一会儿,直到500毫秒时再返回。但这样的问题是,整个站点的响应速度将取决于最慢的那个页面,对于效率至上的团队来说,这可能是无法忍受的。如果不是常量化,而是在每个请求之后休眠一段随机的时间也是不行的,因为完全随机意味着统计学上有迹可循,只要有足够多的样本,攻击者完全可以将随机因素剔除出去。

对客户端的计时攻击来说,防范的办法就更少了,或许只有等待浏览器厂商注意到这类问题,并对浏览器特性做出修改,就像无法取得已访问链接的computed color一样。

参考链接

  • https://code.google.com/p/google-caja/wiki/HistoryMining
  • http://jeremiahgrossman.blogspot.com/2006/08/i-know-where-youve-been.html

这篇关于如何判断用户是否访问过某个网址的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1133039

相关文章

java中判断json key是否存在的几种方法

《java中判断jsonkey是否存在的几种方法》在使用Java处理JSON数据时,如何判断某一个key是否存在?本文就来介绍三种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目http://www.chinasem.cn录第一种方法是使用 jsONObject 的 has 方法

Spring Boot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)

《SpringBoot分层架构详解之从Controller到Service再到Mapper的完整流程(用户管理系统为例)》本文将以一个实际案例(用户管理系统)为例,详细解析SpringBoot中Co... 目录引言:为什么学习Spring Boot分层架构?第一部分:Spring Boot的整体架构1.1

k8s admin用户生成token方式

《k8sadmin用户生成token方式》用户使用Kubernetes1.28创建admin命名空间并部署,通过ClusterRoleBinding为jenkins用户授权集群级权限,生成并获取其t... 目录k8s admin用户生成token创建一个admin的命名空间查看k8s namespace 的

MySQL使用EXISTS检查记录是否存在的详细过程

《MySQL使用EXISTS检查记录是否存在的详细过程》EXISTS是SQL中用于检查子查询是否返回至少一条记录的运算符,它通常用于测试是否存在满足特定条件的记录,从而在主查询中进行相应操作,本文给大... 目录基本语法示例数据库和表结构1. 使用 EXISTS 在 SELECT 语句中2. 使用 EXIS

JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法

《JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法》:本文主要介绍JavaScript中比较两个数组是否有相同元素(交集)的三种常用方法,每种方法结合实例代码给大家介绍的非常... 目录引言:为什么"相等"判断如此重要?方法1:使用some()+includes()(适合小数组)方法2

如何通过try-catch判断数据库唯一键字段是否重复

《如何通过try-catch判断数据库唯一键字段是否重复》在MyBatis+MySQL中,通过try-catch捕获唯一约束异常可避免重复数据查询,优点是减少数据库交互、提升并发安全,缺点是异常处理开... 目录1、原理2、怎么理解“异常走的是数据库错误路径,开销比普通逻辑分支稍高”?1. 普通逻辑分支 v

从基础到进阶详解Python条件判断的实用指南

《从基础到进阶详解Python条件判断的实用指南》本文将通过15个实战案例,带你大家掌握条件判断的核心技巧,并从基础语法到高级应用一网打尽,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一... 目录​引言:条件判断为何如此重要一、基础语法:三行代码构建决策系统二、多条件分支:elif的魔法三、

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

通过配置nginx访问服务器静态资源的过程

《通过配置nginx访问服务器静态资源的过程》文章介绍了图片存储路径设置、Nginx服务器配置及通过http://192.168.206.170:8007/a.png访问图片的方法,涵盖图片管理与服务... 目录1.图片存储路径2.nginx配置3.访问图片方式总结1.图片存储路径2.nginx配置

最新Spring Security的基于内存用户认证方式

《最新SpringSecurity的基于内存用户认证方式》本文讲解SpringSecurity内存认证配置,适用于开发、测试等场景,通过代码创建用户及权限管理,支持密码加密,虽简单但不持久化,生产环... 目录1. 前言2. 因何选择内存认证?3. 基础配置实战❶ 创建Spring Security配置文件