您现在的位置是:首页 >技术交流 >从Referer到XMLHttpRequest:探究Web安全中的重要知识点网站首页技术交流
从Referer到XMLHttpRequest:探究Web安全中的重要知识点
目录
Referrer-policy(可以一定程度上防御CSRF攻击)
AJAX(Asynchronous JavaScript and XML)
XMLHttpRequest.onreadystatechange
XMLHttpRequestEventTarget.ontimeout
XMLHttpRequest.withCredentials
XMLHttpRequest.setRequestHeader()
XMLHttpRequest.overrideMimeType()
XMLHttpRequest.getResponeHeader()
XMLHttpRequest.getAllResponseHeader
Referer
概念
Referer是HTTP请求头信息中的一个字段,它用于表示当前请求是从哪个URL页面跳转过来的。一般情况下,浏览器会自动发送Referer字段给服务器端,服务器端可以通过该字段来进行一些操作,比如判断当前请求是否合法、实现防盗链等功能。同时,由于历史原因和安全问题,Referer也存在一些限制和漏洞,需要我们在使用时进行注意和规避。
Referrer-policy(可以一定程度上防御CSRF攻击)
Referrer-policy的作用就是为了控制请求头部中的referer的内容
包含了以下信息:
-
no-referer:整个referer首部会被移除,访问来源信息不随着请求一起发送。
-
no-referer-when-downgrade:在没有指定任何策略的情况下用户代理的默认行为。在同安全等级的情况下,发送文件的源为引用地址(HTTPS->HTTPS);在降级的情况下不会发送此首部(HTTPS->HTTP)
-
origin:在任何情况下,仅发送文件的源作为引用地址,
-
orgin-when-cross-origin:对于同源的请求,会发送完整的URL作为引用地址,但是对于非同源请求仅仅发送文件的源
-
same-orgin:对于同源的请求会发送引用地址,但是对于非的同源请求则不发送引用地址信息。
-
strict-origin:在同等安全级别的情况下,发送文件的源作为引用地址(HTPPS->HTTPS),但是在降级的情况下不会发送(HTTPS->HTTP)
-
strict-origin-cross-origin,对于同源的请求,会发送完整的URL作为引用地址;在同安全等级的情况下,发送文件的源为引用地址(HTTPS->HTTPS);在降级的情况下不会发送此首部(HTTPS->HTTP)
-
unsafe:无论是同源请求还是非同源请求,都发送完整的URL(移除参数信息后)作为引用地址。(最不安全了)
同源
那些网址符合同源的要求:协议相同、host相同、端口相同
例如:
http://www.baidu.com https://www.baidu.com-<这两个非同源(因为协议不同)
如果嵌入的网页是其他网站的网页,你不了解对方会执行什么操作,因此就存在安全风险。为了限制<iframe>的风险,HTML提供了sandbox属性,允许设置嵌入的网页的权限,等同于提供了一个隔离层,即“沙箱“
iframe
概念:
iframe标签是HTML中的一个内置标签,它允许我们在一个网页中嵌入另外一个网页。通过使用iframe标签,我们可以将一个页面的某个部分独立出来,形成一个新的窗口,从而实现一些有趣的功能和效果。
具体来说,通过使用iframe标签,我们可以:
1. 在一个网页中嵌入其他网页,实现页面嵌套和组合;
2. 实现异步加载某些内容,比如广告、推荐内容等;
3. 实现一些特殊的展示效果,比如浮动窗口、弹出框等;
4. 实现网页和服务器端的交互,比如提交表单、上传文件等。
不过需要注意的是,由于iframe标签存在一些安全问题,比如跨域攻击等问题,因此在使用时需要进行一些安全措施,保证网站的安全性。
sandbox(沙箱):
sandbox是Web开发中的一个重要安全机制,它允许我们在一个沙盒环境中执行不信任的代码,从而提高应用程序的安全性,它可以通过限制某些权限和行为,增强应用程序的安全性,避免安全漏洞和攻击风险。具体来说,sandbox可以通过以下方式增强Web应用程序的安全性:
1. 限制JavaScript的权限
2. 防止跨站点脚本攻击
3. 防止点击劫持
设置嵌入的网页的权限:
对嵌入网页可以设置的权限:
* allow-forms:允许提交表单。
* allow-modals:允许提示框,即允许执行window.alert()等会产生弹出提示框的 JavaScript 方法。
* allow-p
opups:允许嵌入的网页使用window.open()方法弹出窗口。
* allow-popups-to-escape-sandbox:允许弹出窗口不受沙箱的限制。
* allow-orientation-lock:允许嵌入的网页用脚本锁定屏幕的方向,即横屏或竖屏。
* allow-pointer-lock:允许嵌入的网页使用 Pointer Lock API,锁定鼠标的移动。
* allow-presentation:允许嵌入的网页使用 Presentation API。
* allow-same-origin:不打开该项限制,将使得所有加载的网页都视为跨域。
* allow-scripts:允许嵌入的网页运行脚本(但不创建弹出窗口)。
* allow-storage-access-by-user-activation:sandbox属性同时设置了这个值和allow-same-origin的情况下,允许<iframe>嵌入的第三方网页通过用户发起document.requestStorageAccess()请求,经由 Storage Access API 访问父窗口的 Cookie。
* allow-top-navigation:允许嵌入的网页对顶级窗口进行导航。
* allow-top-navigation-by-user-activation:允许嵌入的网页对顶级窗口进行导航,但必须由用户激活。
* allow-downloads-without-user-activation:允许在没有用户激活的情况下,嵌入的网页启动下载。
注意,不要同时设置allow-scripts和allow-same-origin属性,这将使得嵌入的网页可以改变或删除sandbox属性。
cookie的原理:
用户第一次登录时,浏览器自动记录账号和密码等信息;账号明文,密码使用密文机密;cookie保存用户信息完成后,将这些信息发送给客户端,客户端将这些信息保存在本地;
如何设置Referrer?
方法一:
例如:
然后在浏览器测试会发现确实没有了referrer:
注意:如果使用链接的方式将该网页显示出来则referrer还是会照常存在的
例:
b网页将a网页通过链接的方式引入
当在浏览器中通过点击链接访问a网页时:
方法二:
也可以使用js中的referrerpolicy策略来设置no-referrer
例:
在浏览器中查看:
盗链
盗链是指自己的页面展示的但是实际并不在自己服务器的一些内容,获取别人的资源地址,绕过比人的资源展示页面,直接在自己的页面上向最终用用户提供此内容,一般被盗链的都是图片、可执行文件、音频文件、压缩文件等资源。通过盗链的手段可以减轻自己的服务器的负担
盗链的工作原理
通过referer或者签名,网站可以检测到目标页面访问的来源网页,如果是资源文件,则可以追踪显示到它网页地址,一旦检测到来源不是本站点,即进行阻止或者返回指定的页面
三种情况下可以引用图片:
- 本网站
- 无referer信息的情况下(即服务器认为从浏览器直接访问图片的图片的URL,所以这种情况下可以正常访问)
- 授权的网址
XMLHTTPRequest
AJAX(Asynchronous JavaScript and XML)
原理:通过JavaScript的异步通信,从服务器获取XML文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。后来,AJAX这个词就成为了JavaScript脚本发起HTTP通信的代名词,也就是说,只要用脚本发起通信,就可以叫做AJAX通信
具体来说,AJAX包括以下几个步骤:
- 创建XMLHttpRequest实例
- 发出HTTP请求
- 接收服务器传回的数据
- 更新网页数据
- AJAX最关键的功能:页面无刷新
- AJAX通过原生的XMLHTTPRquest对象发出的HTTP请求,得到服务器返回的数据后,再进行处理。现在服务器返回的都是JSON格式的数据,XML格式已经过时了。
- XMLHTTPRequest对象是AJAX的主要接口,用于浏览器与服务器之间的通信。
- XMLHttpRequest本身是一个构造函数,可以使用new命令生成实例,并且它没有任何的参数。
- 一旦拿到服务器返回的数据,AJAX 不会刷新整个网页,而是只更新网页里面的相关部分,从而不打断用户正在做的事情。
例如:
var xhr new XMLHttpRequest();
注意:AJAX只能向同源网址(协议、域名、端口都相同)发出HTTP请求,如果发出跨域请求,就会报错
XMLHttpRequest的实例属性
XMLHttpRequest.readyState
这个属性会返回一个整数,表示实例对象的当前状态。该属性为只读
返回值类型:
- 0:表示XMLHttpRequest实例已经生成,但是实例的open()方法还没有被调用
- 1:表示open()方法已经调用,但是实例的send()方法还是没有调用,仍然可以使用实例的
setRequestHeader()
方法,设定 HTTP 请求的头信息。 - 3:表示正在接受服务器传来的数据体(body部分)。这时,如果实例的
responseType
属性等于text
或者空字符串,responseText
属性就会包含已经收到的部分信息。 - 4:表示服务器返回的数据已经完全接受,或者本次接受已经失败
通信过程中,每当实例对象发生状态变化,它的
readyState
属性的值就会改变。这个值每一次变化,都会触发readyStateChange
事件。
XMLHttpRequest.onreadystatechange
这个属性指向一个监听函数。
readstatecahnge事件发生时(实例的readystate属性变化),就会执行这个属性
另外,如果使用实例的
abort()
方法,终止 XMLHttpRequest 请求,也会造成readyState
属性变化,导致调用XMLHttpRequest.onreadystatechange
属性。
XMLHttpRequest.response
这个属性表示服务器返回的数据体(即回应的body部分),它可能是任意的数据类型,具体的类型由XMLHttpRequest.responeType属性决定。该属性为只读
如果本次请求没有成功或者数据不完整,该属性等于null。但是如果responeType属性等于text或者字符串,在请求没有结束之前(readyState等于3的阶段),response属性包含服务器已经返回的部分数据
XMLHttpRequest.responeType
这个属性是一个字符串,表示服务器返回数据的类型。这个属性是可读可写的,可以是在调用open()方法之后、调用send()之前,设置这个属性的值,告诉浏览器如何解读返回的数据。如果responeType设为空字符串,就等于默认值text。
返回值的类型包括:
- "":等同于text,表示服务器返回文本数据
- "arratbufferr":表示服务器返回二进制数组
- "blob":表示服务器返回二进制对象
- "document":表示服务器返回一个文档对象
- "json":JSON对象
- "text":字符串
text类型适合大多数情况,直接处理文本也比较方便
document类型适合返回HTML/XML文档的情况,这意味着,对于那些打开CORS的网站,可以直接yongAJAX抓取网页,然后不用接卸HTML字符串,直接抓取回来的数据进行DOMC操作
Blob类型适合读取二进制数据,比如图片文件
如果将这个属性设为
ArrayBuffer
,就可以按照数组的方式处理二进制数据。如果将这个属性设为
json
,浏览器就会自动对返回数据调用JSON.parse()
方法。也就是说,从xhr.response
属性(注意,不是xhr.responseText
属性)得到的不是文本,而是一个 JSON 对象。
XMLHttpRequest.responeText
这个属性返回从服务器接受到的字符串,该属性为只读。
只有HTTP请求接受完成以后,该属性才会包含完整的数据
XMLHttpRequwst.responeXML
这个属性返回从服务器接受到的HTML或者XML对象,该属性为只读。如果本次请求没有成功,或者收到的数据不能被解析为XML或者HTML,该属性就等于null
该属性生效的前提HTTP回应的content-Type头信息等于text/xml或者application/xml。这要求在发送请求前,XMLHttpRequest。responseType属性要设置为document。
如果HTTP回应的content-Type头信息不等于text/xml和application/xml,但是想从responseXML拿到数据(即把数据按照DOM格式解析),那么就要手动调用
XMLHttpRequest.overrideMimeType()
方法,强制进行 XML 解析。该属性得到的数据,是直接解析后的文档 DOM 树。
XMLHttpRequwst.responeURL
该属性是字符串,表示发送数据的服务器的网址
注意:这个属性的值与open()方法指定的请求网址不一定相同。如果服务器端发生跳转,这个属性返回最后实际返回数据的网址,另外原始URL包括锚点,该属性会把锚点剥离
XMLHttpRequest.status
这个属性返回一个整数,表示服务器回应的HTP状态码。请求发送之前状态码为0,发送成功为200,如果没有返回码默认也为200
常见的返回码:
-
200, OK,访问正常
-
301, Moved Permanently,永久移动
-
302, Moved temporarily,暂时移动
-
304, Not Modified,未修改
-
307, Temporary Redirect,暂时重定向
-
401, Unauthorized,未授权
-
403, Forbidden,禁止访问
-
404, Not Found,未发现指定网址
-
500, Internal Server Error,服务器发生错误
XMLHttpRequests.tatusText
这个属性返回一个字符串,表示服务器发送的状态提示。不同status属性,该属性包含整个状态信息,比如"OK"和"Not Found"。该属性为只读
在请求发送之前(即调用open()方法之前),该属性的值为空字符串;如果服务器没有返回状态提示,该属性的值默认为“OK”。
XMLHttpRequest.timeout
这个属性返回一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。
如果该属性等于0,就表示没有时间限制
XMLHttpRequestEventTarget.ontimeout
这个属性用于设置一个监听函数,如果发生timeout事件就会执行这个监听函数。
事件监听属性:
XMLHttpRequest 对象可以对以下事件指定监听函数。
-
XMLHttpRequest.onloadstart:loadstart 事件(HTTP 请求发出)的监听函数
-
XMLHttpRequest.onprogress:progress事件(正在发送和加载数据)的监听函数
-
XMLHttpRequest.onabort:abort 事件(请求中止,比如用户调用了
abort()
方法)的监听函数 -
XMLHttpRequest.onerror:error 事件(请求失败)的监听函数
-
XMLHttpRequest.onload:load 事件(请求成功完成)的监听函数
-
XMLHttpRequest.ontimeout:timeout 事件(用户指定的时限超过了,请求还未完成)的监听函数
-
XMLHttpRequest.onloadend:loadend 事件(请求完成,不管成功或失败)的监听函数
progress事件的监听函数有一个事件对象参数,该对象有三个属性:
- load属性返回已经传输的数据量,
- total属性返回总的数据量,
- legthComputable属性返回一个布尔值,表示加载的进度是否可以计算
所有的这些监听函数中,只有progress事件的监听函数有参数,其他函数都没有参数
注意:如果发生网络错误(比如服务器无法连接),onerror时间无法获取错误信息。也就是说,可能没有错误对象,所以这样只能显示报错的提示
XMLHttpRequest.withCredentials
这个属性是一个布尔值,表示跨域请求时,用户信息(比如Cookie和认证的HTTP头信息)是否会包含在其中,默认为false。
如果需要跨域AJAX请求发送Cookie,需要withCredentials属性设置为true。(同源的请求不需要设置这个属性)
为了让这个属性生效,服务器必须显式返回Access-Control-Allow-Credentials这个头信息
withCredentials
属性打开的话,跨域请求不仅会发送 Cookie,还会设置远程主机指定的 Cookie。注意,脚本总是遵守同源政策,无法从
document.cookie
或者 HTTP 回应的头信息之中,读取跨域的 Cookie,withCredentials
属性不影响这一点
XMLHttpRequest.upload
XMLHttpRequest不仅可哟发送请求,还可以发送文件,这就是AJAX文件上传。
发送文件后,通过这个属性可以得到一个对象,通过观察这个对象,可以得知上传的进展。主要方法就是监听和这个对象的各种事件loadstart,loadend,load,abort,error,progress,timeout
XMLHttpRequest的实例方法
XMLHttpRequest.open()
这个方法用于指定HTTP请求的参数,或者说初始化XMLHttpRequest对象。他一共可以接受五个参数
void open(
string method,
string url,
optional boolean async,
optional string user,
optional string password
);
- method:表示HTTP动词方法,比如GET、POST、PUT、DELETE、HEAD等
- url:表示请求发送目标URL
- async:布尔值,表示请求是否异步,默认为true。如果设为false,则send()方法只有等到收到服务器返回来结果,才会进行洗衣不操作。该参数可选
- user:表示用于认证的用户名,默认为空字符串。该参数可选。
- password:表示用于认证的密码,默认为空字符串。该参数可选
注意:如果对使用过open()方法的AJAX请求,再使用这个方法,等同于调用abort(),即终止请求
XMLHttpRequest.send()
这个方法用于实际发送HTTP请求。
它的参数是可选的,如果不带参数,就表示HTTP请求只有一个URL,没有数据体,典型例子就是GET请求;如果有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是POST请求。
注意,所有 XMLHttpRequest 的监听事件,都必须在
send()
方法调用之前设定。
send
方法的参数就是发送的数据。多种格式的数据,都可以作为它的参数。
比如:
void send();
void send(ArrayBufferView data);
void send(Blob data);
void send(Document data);
void send(String data);
void send(FormData data);
如果send()发送DOM对象,在发送之前,数据会先被串行化。
如果发送二进制数据,做好是发送ArrayBufferView或者Blob对象,这使得通过AJAX上传文件成为了可能
XMLHttpRequest.setRequestHeader()
这个方法用于设置浏览器发送的HTTP请求的头信息。
该方法接受两个参数。第一个参数是字符串,表示信息的字段名,第二个参数是字段值。
该方法必须在open()之后、send()之前调用,如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。
XMLHttpRequest.overrideMimeType()
这个方法用来指定MIME类型,覆盖服务器返回的真正的MIME类型,从而让浏览器进行不一样的处理。
举例来说,服务器返回的数据类型是text/xml,由于种种原因浏览器解析不成功报错,这时就拿不到数据了。为了拿到原始数据,我们可以把MIME类型改为text/plain,这样浏览器就不会去自动解析,从而我们就可以拿到原始文本了。
例如:
xhr.oberrideMimeType('text/plain')
注意:该方法必须在send()方法之前调用
修改服务器返回的类型,不是正常情况下应该采取的方法。如果希望服务器返回指定的数据类型们可以适应responeType属性告诉服务器。只有在服务器无法返回某种数据类型时,才使用overrideMimeType()方法。
XMLHttpRequest.getResponeHeader()
这个方法返回HTTP头信息指定字段的值。这个方法的参数不区分大小写
如果还没有收到服务器回应挥着指定字段不存在,返回null。
如果有多个字段同名,他们的值会被连接为一个字符串,每个字段之间使用(逗号加空格)分割。
XMLHttpRequest.getAllResponseHeader
这个方法返回一个字符串,表示是服务器发来的所有HTTP头信息。格式为字符串,每个头信息之间使用CRLF分割(回车加换行)
如果没有收到服务器回应,该属性为null。
如果发生网络错误,该属性为空字符串。
XMLHttpRequest.abort()
这个方法用来终止已经发出的HTTP请求。调用这个方法以后,readstate属性变为4,status属性变为0。
XMLHttpRequest的实例事件
readyStateChange事件
readyState
属性的值发生改变,就会触发 readyStateChange 事件。
我们可以通过onReadyStateChange
属性,指定这个事件的监听函数,对不同状态进行不同处理。尤其是当状态变为4
的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。progress事件
上传文件时,XMLHttpRequest 实例对象本身和实例的upload
属性,都有一个progress
事件,会不断返回上传的进度。
load事件
oad 事件表示服务器传来的数据接收完毕
erro事件
error 事件表示请求出错
abort事件
load 事件表示服务器传来的数据接收完毕,error 事件表示请求出错,
loadend事件
abort
、load
和error
这三个事件,会伴随一个loadend
事件,表示请求结束,但不知道其是否成功。
timeout事件
服务器超过指定时间还没有返回结果,就会触发 timeout 事件,具体的例子参见timeout
属性一节。