ajax、跨域与安全机制

ajax、安全机制、跨域.png

1、ajax

能够在不从新加载整个网页的状况下,对网页的某部分进行更新。
ajax-yl.pngphp

XHR建立对象:

XMLHttpRequest 对象用于和服务器交换数据。(IE六、IE5不支持)css

let xhr = new XMLHttpRequest();

XHR请求

向服务器发送请求:html

  • open(method,url,async)

功能:规定请求的类型、URL 以及是否异步处理请求。
参数前端

  • method :请求的类型,GET 或 POST。
  • url:文件在服务器上的位置,该文件能够是任何类型的文件。
  • async:true(异步)或 false(同步)
  • send(string)

功能:将请求发送到服务器。
参数git

  • string :仅用于 POST 请求。

GET请求

xhr.open("GET","/try/ajax/demo_get.php",true);
xhr.send();

POST请求

简单请求:github

xhr.open("POST","/try/ajax/demo_post.php",true);
xhr.send();

POST 提交表单:
使用 setRequestHeader() 来添加 HTTP 头。而后在 send() 方法中规定但愿发送的数据。ajax

xhr.open("POST","/try/ajax/demo_post2.php",true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send("fname=Henry&lname=Ford");
  • setRequestHeader(header,value)

功能:向请求添加 HTTP 头。
参数算法

  • header:规定头的名称。
  • value:规定头的值。

XHR响应

如需得到来自服务器的响应,使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性。数据库

属性 描述 备注
responseText 得到字符串形式的响应数据。 若是来自服务器的响应并不是XML,请使用responseText属性。
responseXML 得到XML形式的响应数据。 若是来自服务器的响应是 XML且须要做为XML对象进行解析,请使用 responseXML 属性。
xhr.responseText
xhr.responseXML

XHR属性

  • onreadystatechange

描述:存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数。
参数express

  • header:规定头的名称。
  • value:规定头的值。
  • readyState

描述:存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
状态

  • 0: 请求未初始化,尚未调用send()方法
  • 1: 服务器链接已创建,已调用send()方法
  • 2: 请求已接收,send()方法执行完成
  • 3: 请求处理中,正在解析响应内容
  • 4: 请求已完成,且响应已就绪
  • status

状态

  • 1xx: 表示通知消息。
  • 2xx: 表示成功。
  • 200: 请求成功,已经正常处理完毕
  • 3xx: 表示重定向。

    • 301:永久性重定向。
    • 302:临时性重定向。
    • 304:请求被重定向到客户端本地缓存。
  • 4xx: 表示客户端差错。

    • 400:客户端请求存在语法错误。
    • 401:客户端请求没有通过受权。
    • 403:客户端的请求被服务器拒绝。
    • 404:客户端请求的URL在服务器端不存在。
  • 5xx 表示服务器差错

    • 500:服务端永久错误。
  • 当请求被发送到服务器时,咱们须要执行一些基于响应的任务。
  • 每当 readyState 改变时,就会触发 onreadystatechange 事件。在 onreadystatechange 事件中,咱们规定当服务器响应已作好被处理的准备时所执行的任务。
  • 当 readyState 等于 4 且状态为 200 时,表示响应已就绪。
xhr.onreadystatechange=function(){
    if (xmlhttp.readyState==4 && xmlhttp.status==200){
        document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
    }
}

注意: onreadystatechange 事件被触发 4 次(0 - 4), 分别是: 0-一、1-二、2-三、3-4,对应着 readyState 的每一个变化。

手写ajax

callback()形式:

function ajax(url, callback) {
    let xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            callback(xhr.responseText);
        }
    };
    xhr.send();
}

let url = "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";
ajax(url, function (data) {
    console.log(JSON.parse(data));
});

promise形式

function ajax(url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                resolve(xhr.responseText);
            }
        };
        xhr.send();
    });
}

let url = "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1";
ajax(url).then((res) => {
    console.log(JSON.parse(res));
}).catch((err) => {
    console.log(err);
});

2、没有同源策略限制的两大危险场景:

同源策略:是一种安全机制。同源策略限制从一个源加载的资源如何与来自另外一个源的资源进行交互。
同源:协议、主机、端口号都相同。
跨域:三者有一个不一样则是跨域。
非同源下:

  • CookieLocalStorageIndexDB没法读取
  • DOM没法得到
  • AJAX请求不能发送

跨域和跨站
同源策略做为浏览器的安全基石,其「同源」判断是比较严格的,相对而言,Cookie中的「同站」判断就比较宽松:只要两个 URL 的 eTLD+1 相同便可,不须要考虑协议和端口。其中,eTLD 表示有效顶级域名,注册于 Mozilla 维护的公共后缀列表(Public Suffix List)中,例如,.com、.co.uk、.github.io 等。eTLD+1 则表示,有效顶级域名+二级域名,例如 taobao.com 等。

举几个例子,www.taobao.com 和www.baidu.com是跨站,www.a.taobao.com 和www.b.taobao.com是同站,a.github.io 和 b.github.io 是跨站(注意是跨站)。

1. 没有同源策略限制的接口请求

CSRF攻击—跨站请求伪造

CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。

形成的问题:我的隐私泄露以及财产安全。包括:以你名义发送邮件,发消息,盗取你的帐号,甚至于购买商品,虚拟货币转帐

CSRF攻击原理:

CSRF攻击.png
CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然能够保证一个请求是来自于某个用户的浏览器,但却没法保证该请求是用户批准发送的!

CSRF攻击原理

若是用户是登陆状态,打开了以下这样的页面:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>csrf攻击</title>
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" name="viewport" />
</head>

<body style="display: none;">
    <form target="myIframe" id="csrf" action="https://www.kkkk1000.com/csrf/data/post_comment.php" metdod="POST">
        <input type="text" name="content" value="csrf攻击" />
    </form>

    <!-- iframe 用来防止页面跳转 -->
    <iframe id="myIframe" name="myIframe"></iframe>

    <script>
        var form = document.querySelector('#csrf');
        form.submit();
    </script>
</body>

</html>

就会自动在文章下发一条评论,这样就算完成了一次 CSRF 攻击。
固然,若是你把这个页面放服务器上,而后作成一个连接,用户点击这个连接,一样能够完成攻击。

CSRF特色
  • 攻击通常发起在第三方网站,而不是被攻击的网站。
  • 攻击是利用受害者在被攻击网站的登陆凭证,冒充受害者提交操做,仅仅是“冒用”,而不是直接窃取数据。
  • 攻击者预测出被攻击的网站接口的全部参数,成功伪造请求。
CSRF防护措施:

1.SameSite属性
Cookie 的 SameSite 属性用来限制第三方 Cookie,从而减小安全风险,能够用来防止 CSRF 攻击和用户追踪。

2.同源检测
在 HTTP 协议中,每个异步请求都会携带两个 Header ,用于标记来源域名:Origin Header,Referer Header。这两个 Header 在浏览器发起请求时,大多数状况会自动带上,而且不能由前端自定义内容。 服务器能够经过解析这两个 Header 中的域名,肯定请求的来源域。

3.验证码
而验证码会强制用户必须与应用进行交互,才能完成最终请求,并且由于 CSRF 攻击没法获取到验证码,所以一般状况下,验证码可以很好地遏制 CSRF 攻击。

4.Token 验证:
在 HTTP 请求中以参数的形式加入一个随机产生的 Token,并在服务器端创建一个拦截器来验证这个 Token,若是请求中没有 Token 或者 Token 内容不正确,则认为多是 CSRF 攻击而拒绝该请求。

添加 Token 验证的步骤:
一、服务器将 Token 返回到前端
用户打开页面时,前端发起请求,服务器会返回一个 Token,该 Token 经过加密算法对数据进行加密,通常 Token 都包括随机字符串和时间戳的组合,同时 Token 会存在服务器的 Session 中。以后页面加载完成时,使用 JS 遍历整个 DOM 树,在 DOM 中全部地址是本站的aform标签中加入 Token,其余的请求就在编码时手动添加 Token 这个参数。
二、前端发请求时携带这个 Token
对于 GET 请求,Token 将附在请求地址以后,这样 URL 就变成http://url?token=tokenvalue。 而对于form标签发起的 POST 请求来讲,要在form的最后加上:

<input type=”hidden” name=”token” value=”tokenvalue”/>

总之,就是前端发请求时把 Token 以参数的形式加入请求中。
三、服务器验证 Token 是否正确
当前端获得了 Token ,再次提交给服务器的时候,服务器须要判断 Token 的有效性,验证过程是先解密 Token,对比加密字符串以及时间戳,若是加密字符串一致且时间未过时,那么这个 Token 就是有效的。

xss—跨站脚本攻击

攻击者在网页中嵌入客户端脚本(例如JavaScript), 当用户浏览此网页时,脚本就会在用户的浏览器上执行,从而达到攻击者的目的.  好比获取用户的Cookie,导航到恶意网站,携带木马等。

XSS攻击方式:
  • 反射型

发出请求时,XSS代码出如今URL中,做为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一块儿传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,故叫反射型XSS。

演示:
index.ejs

<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css'/>
</head>
<body>
    <h1><%= title %></h1>
    <p>Welcome to <%= title %></p>
    <div class="">  
        <%- xss %>
    </div>
</body>
</html>

index.js

var express = require('express');
var router = express.Router();

router.get('/', function (req, res, next) {
    res.set("X-XSS-Protection", 0); //容许攻击
    res.render('index', {title: 'Express', xss: req.query.xss});
});

module.exports = router;

请求

127.0.0.1:3000/?xss=<p%20onclick="alert(%27点我%27))">点我</p>
  • 存储型

存储型XSS和反射型XSS的差异仅在于,提交的代码会存储在服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码。

XSS防护措施:
  • 编码 :对用户输入的数据进行HTML Entity(字符实体)编码
  • 过滤:移除用户上传的DOM属性,如onerror等;移除用户上传的Style节点、Script节点、Iframe节点等。
  • 校订:避免直接对HTML Entity解码;使用DOM Parse转换,校订不配对的DOM标签。

XSS 通常利用js脚本读取用户浏览器中的cookie,而若是在服务器端对某个cookie设置了 httpOnly属性,则没法经过 JS 脚本 读取到该cookie的信息,但仍是能经过Application中手动修改cookie,因此只是在必定程度上能够防止XSS攻击,不是绝对的安全。

2. 没有同源策略限制的Dom查询!

因为没有同源策略的限制,钓鱼网站能够直接拿到别的网站的Dom。

3、跨域的方式

哪些html标签能绕过跨域?
经过hrefsrc请求的资源不存在跨域问题,

<img src=跨域的图片地址/> //可用于统计打点,可以使用第三方统计服务
<link href=跨域的css地址/>
<script src=跨域的js地址> </script>

//<link /><script>可以使用CDN CDN通常都是外域
//<script>可实现JSONP

全部的跨域,都必须通过server端容许和配合,未经server端容许就实现跨域,说明浏览器有漏洞。

同源策略限制下接口请求的正确打开方式

ajax请求不能跨域,因此引入jsonp 和 cors

JSONP

JSONP的原理 :利用<script>标签中 src属性能够跨域的特性。
JSONP只支持GET请求,由于本质上<script>加载资源就是GET。JSONP的优点在于支持老式浏览器,以及能够向不支持CORS的网站请求数据。

具体实现:

  1. 本地写一个回调函数。
  2. 在远程执行这个回调函数,再将把远程的数据传到本地。远程服务器必须支持jsonp

示例:
jsonp.html

<body>
<script>
  function foo(data) {
    alert(data);
  }
</script>
<script src="http://localhost:8001/info?callback=foo"></script>
</body>

serve.js

app.get("/info", async (req, res) => {
  let funcName = req.query.callback;
  res.send(`${funcName}('你好')`);
  //foo('你好');
});

CORS

CORS是一个W3C标准,全称是"跨域资源共享"。CORS有两种请求,简单请求和非简单请求。

简单请求:

只要同时知足如下两大条件,就属于简单请求。
(1) 请求方法是如下三种方法之一:HEADGETPOST
(2) HTTP的头信息不超出如下几种字段:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type
Content-Type 只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain

原理:
浏览器若发现此次跨源ajax请求是简单请求,就自动在头信息之中,添加一个Origin字段,用来讲明,本次请求来自哪一个(协议 + 域名 + 端口)(前端实际上什么也不用干)。服务器根据这个值,决定是否赞成此次请求。

  • 若是Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被 XMLHttpRequestonerror 回调函数捕获。注意,这种错误没法经过状态码识别,由于HTTP回应的状态码有多是200。
  • 若是Origin指定的源,在许可范围内,服务器返回的响应,会多出几个头信息字段。
app.get("/",function(req,res){
//设置服务器端返回的响应的头字段 
    res.header("Access-Control-Allow-Origin","*");
    res.send("你好");
})

Access-Control-Allow-Origin

该字段是必须的。它的值要么是请求时 Origin字段的值,要么是一个 *,表示接受任意域名的请求。

请求头 origin 对应响应头 Access-Control-Allow-Origin

复杂请求

在发送真正的请求前会提早发送一次options请求(嗅探、预检请求),返回码是204,预检测经过才会真正发出请求,这才返回200。若是options得到的回应是拒绝性质的(或者没有权限),会中止发送实际请求信息。这里经过前端发请求的时候增长一个额外的headers来触发非简单请求。XHR会根据返回的Access-Control-*等头信息判断是否有对指定站点的访问权限,检查该请求是不是可靠安全的。

"预检"请求的头信息

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

(1) Origin: 用来讲明,本次请求来自哪一个源
(2) Access-Control-Request-Method: 该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT
(3)Access-Control-Request-Headers: 该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header

"预检"请求的响应

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

(1) Access-Control-Allow-Origin: 表示http://api.bob.com能够请求数据。该字段也能够设为星号,表示赞成任意跨源请求。与响应头 origin 对应。
(2) Access-Control-Allow-Methods: 代表服务器支持的全部跨域请求的方法。与响应头Access-Control-Request-Method对应。
(3)Access-Control-Allow-Headers: 代表服务器支持的全部头信息字段。与响应头Access-Control-Request-Headers对应。

一旦服务器经过了"预检"请求,之后每次浏览器正常的CORS请求,就都跟简单请求同样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

示例:

前端
fetch(`http://localhost:9871/api/cors?msg=helloCors`, {
  // 须要带上cookie
  credentials: 'include',
  // 这里添加额外的headers来触发非简单请求
  headers: {
    't': 'extra headers'
  }
}).then(res => {
  console.log(res)
})
后台
const cors= require('koa2-cors);
//配置 jsonp 的中间件
app.use(cors());

参考文章:https://segmentfault.com/a/11...
参考文章:http://www.ruanyifeng.com/blo...
*参考文章:跨站请求伪造—CSRF

相关文章
相关标签/搜索