前端必须懂的计算机网络知识—(XSS、CSRF和HTTPS)

4,241 阅读13分钟

前端必须懂的计算机网络知识系列文章:

HTTP为什么不安全

  1. 可能被窃听:
  • HTTP 本身不具备加密的功能,HTTP 报文使用明文方式发送
  • 由于互联网是由联通世界各个地方的网络设施组成,所有发送和接收经过某些设备的数据都可能被截获或窥视。(例如大家都熟悉的抓包工具:Wireshark),即使经过加密处理,也会被窥视是通信内容,只是可能很难或者无法破解出报文的信息而已
  1. 认证问题
  • 无法确认你发送到的服务器就是真正的目标服务器(可能服务器是伪装的)
  • 无法确定返回的客户端是否是按照真实意图接收的客户端(可能是伪装的客户端)
  • 无法确定正在通信的对方是否具备访问权限,Web服务器上某些重要的信息,只想发给特定用户即使是无意义的请求也会照单全收。无法阻止海量请求下的 DoS 攻击(Denial of Service,拒绝服务攻击)。
  1. 可能被篡改
  • 请求或响应在传输途中,遭攻击者拦截并篡改内容的攻击被称为中间人攻击(Man-in-the-Middle attack,MITM)。

XSS攻击

XSS,即为(Cross Site Scripting),中文名为跨站脚本,跨站脚本的重点不在“跨站”上,而在于“脚本”上。大多数XSS攻击的主要方式是嵌入一段远程或者第三方域上的JS代码,实际上是在目标网站的作用域下执行了这段第三方域上的js代码。

反射型XSS(非持久型XSS)

特点:就像镜子反射一样,浏览器发射含XSS的url,服务器将其反射回来

  1. 浏览器发生请求时,XSS代码出现在请求URL中,作为参数提交到服务器,
  2. 服务器解析并响应,响应结果中包含XSS代码,
  3. 最后浏览器解析并执行。

案例:表单提交

//test.html
<body>
    <textarea name="txt" id="txt" cols="80" rows="10">
    <button type="button" id="test">测试</button>
    <script>
        var test = document.querySelector('#test')
        test.addEventListener('click', function () {
            var url = `/test?test=${txt.value}`   //1.发送一个GET请求
            var xhr = new XMLHttpRequest()
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                        // 3. 客户端解析JSON,并执行
                        var str = JSON.parse(xhr.responseText).test
                        var node = `${str}`
                        document.body.insertAdjacentHTML('beforeend', node)
                    } else {
                        console.log('error', xhr.responseText)
                    }
                }
            }
            xhr.open('GET', url, true)
            xhr.send(null)
        }, false)
    </script>  
</body>
//server.js
var express = require('express');
var router = express.Router();
router.get('/test', function (req, res, next) {
 // 2.服务端解析成JSON后响应
    res.json({
        test: req.query.test
    })
})

现在我们通过给textarea添加一段有攻击目的的img标签:

反射型XSS1
点击<测试>按钮,一个XSS攻击就发生了。下面图片中是获取了本地的部分cookie信息:
反射型XSS1
上面只是模拟攻击,通过alert获取到了个人的cookie信息。一般黑客会注入一段第三方的js代码,然后将获取到的cookie信息存到他们的服务器上,拿到我们的身份认证做一些违法的事情了。

存储型XSS(持久型XSS)

特点:黑客将XSS代码发送给服务器,然后通过服务器散播

  1. 黑客将XSS代码发送到服务器(不管是数据库、内存还是文件系统等。)
  2. 其他人请求页面的时候就会带上XSS代码了。

案例:最典型的就是留言板XSS。

  • 黑客提交了一条包含XSS代码的留言到数据库。
  • 当目标用户查询留言时,那些留言的内容会从服务器解析之后加载出来。
  • 浏览器发现有XSS代码,就当做正常的HTML和JS解析执行。XSS攻击就发生了。

DOM型XSS

特点:DOM XSS代码不需要服务器端的解析响应的直接参与,而是完全通过浏览器端的DOM解析。

  • 浏览器的代码中含有eval,new Function等将字符串内容执行的代码
  • 在执行的字符串中嵌入可以执行XSS代码字符串 案例:
test.addEventListener('click', function () {
  var node = window.eval(txt.value)
  window.alert(node)
}, false)

//txt中的代码如下:
<img src='null' onerror='alert(123)' />

XSS危害

  • 通过document.cookie盗取cookie
  • 使用js或css破坏页面正常的结构与样式
  • 流量劫持(通过访问某段具有window.location.href定位到其他页面)
  • Dos攻击:利用合理的客户端请求来占用过多的服务器资源,从而使合法用户无法得到服务器响应。
  • 利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、发私信等操作。
  • 利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。

XSS防御

XSS攻击可以看出,不能原样的将用户输入的数据直接存到服务器,需要对数据进行一些处理:

  • 过滤危险的DOM节点。如具有执行脚本能力的script, 具有显示广告和色情图片的img, 具有改变样式的link, style, 具有内嵌页面的iframe, frame等元素节点。
  • 过滤危险的属性节点。如on-, style, src, href等
  • 对cookie设置httpOnly,但是也会导致前台无法操作cookie,不太推荐。

CSRF

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,攻击者盗用了你的身份,以你的名义发送恶意请求。

CRFS
特点:

  1. 登录受信任网站A,并在本地生成Cookie。
  2. 在不登出受信任网站A的情况下,访问危险网站B。
  3. 危险网站会向受信任A的网站发送请求,同时会携带受信任网站A本地生成Cookie(用同一个浏览器访问同一个域的接口)

案例:

//钓鱼网站,利用用户的cookie进行权限操作转账
<body onload="steal()">
    <iframe name="steal" display="none">
&emsp;&emsp;    <form method="POST"name="transfer"&emsp;action="http://www.myBank.com/Transfer.php">
&emsp;&emsp;&emsp;&emsp;    <input type="hidden" name="toBankId" value="11">
&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;<input type="hidden" name="money" value="1000">
&emsp;&emsp;&emsp;&emsp;</form>
&emsp;&emsp;</iframe>
</body>

CSRF的危害

  1. 篡改目标网站上的用户数据;
  2. 盗取用户隐私数据;
  3. 作为其他攻击向量的辅助攻击手法;
  4. 传播CSRF蠕虫。

CSRF的防御

  1. 验证码,因为验证码必须在受信任的网站上发送给浏览器的,并且伪造的网站和受信任的网站非同源,所以没有办法获取受信任网站发送的session,所以验证码是没有办法伪造的。
  2. refer,标识了当前请求的页面的源,伪造网站可以篡改成受信任的网站源,并不保险
  3. token,由于它是通过服务的发送给客户端的令牌,并且存储在浏览器的localstorage中,由于同源策略,并且token还有校验规则,所以token并不能轻易篡改。

Token和JWT

Token的特点

  1. session是通过cookie传输的,每次访问网站都会自主的带上,所以存在安全问题和浪费带宽,token存储在localstorage,可以选择性发送给后台,由于同源策略,安全性高
  2. session在集群服务器中很难实现共享,服务器必须存在一份sessionq清单,token不需要存储清单,共用一个密钥,发送token可以实现共享
  3. session销毁后会从清单消失,token的注销不好实现,没有清单证明这个token不能用,所以得用一个黑名单来标注

JWT

jwt是实现token的一种方式,一个token分3部分,3部分之间用“.”号做分隔:

  1. header:头部为base64字符串
  2. payload:载荷为base64字符串
  3. signature:签证 hader+payload经过key加密得出

EXP:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

  • JWT原理:
let jwt = {
    //解码
    decode(token,secret){
        let [header,content,sign] = token.split('.');
        let h = JSON.parse(this.formBase64ToString(header));
        let c = JSON.parse(this.formBase64ToString(content));
        if(sign !== this.sign([header,content].join('.'),secret){
            throw new Error ('Not allowd')
        }
        return c
    },
    //加码
    encode(payload,secret){
        let header = this.toBase64(JSON.stringify({'type':'JWT',alg:'HS25' }));
        let content = this.toBase64(JSON.stringify(payload));
        let sign = this.sign([header,content].join('.'),secret);
        return [header,content,sign].jion('.');
    }
    //转换为base64
    toBase64(str){
        return Buffer.from(str).toString('base64');
    }
    //签名
    sign(str,secret){
        return require('crypto').createHmac('sha256',secret).update(str);
    }
}

module.exports = jwt;
  • JWT后台:
const express = require('express');
const bodyParser = require('body-parser')
const jwt = require('jwt-simple')
const userList=[
    {id:1,username:'kbz',password:'123456'}
]
const SECRET = 'MISS';
let app = express();
app.use(bodyParser.json);

//因为请求头中有Authorization字段,这和跨域时要设置name传参数一样,可以参考:
//前端必须懂的计算机网络知识—(跨域、代理、本地存储)
//https://juejin.cn/post/6844903686775242760
app.use(function(req,res,next){
    res.setHeader('Access-Control-Allow-Origin',"*");
    res.setHeader('Access-Control-Allow-Headers',"Content-Type,Authorization");
    res.setHeader('Access-Control-Allow-Methods',"GET,POST,OPTIONS");
    if(req.method === 'OPTIONS'){
        res.end();
    }
    next();
})

//登陆时发送token
app.post('/login',function(req,res,next){
    let user = req.body;
    userList.find(person=>(person.id === user.id));
    if(user){
        jwt.encode({
            id:user.id,
            username:user.username
        },SECRET)
        res.json({
            code:0,
            data:{
                token
            }
        })
    }else{
        res.json({
            code:1,
            data:'用户不存在'
        })
    }
})

// 需要验证权限,则取token验证
// 请求头Authorization:Bearer token
app.get('/order',function(req,res,next){
    let authorization = req.headers['authorization'];
    if(authorization){
        let token = authorization.split(' ')[1];
        try{
            let user = jwt.decode(token,SECRET);
            req.user = user;
        }catch(e){
            res.status(401).send('Not Allowed')
        }
    }else{
        res.status(401).send('Not Allowed')
    }

})
app.listen(3000);
  • JWT前台
//login.html
<input type="text" id='username' class="form-control">
<input type="text" id='password' class="form-control">
<span onclick='login()'></span>
<scrpipt>
    axios.post('/user').then(res=>{
        localStorage.setItem('token',res.data.data.token)
        location.href='/order'
    })
</script>

//order.html
<scrpipt>
    axios.interceptors.request.use(function(config){
        let token = localStorage.getItem('token');
        if(token){
            config.headers.Authorization = 'Bearer '+ token
        }
    })
    axios.get('/order').then(res=>{
        console.log(res);
    })
</script>

HTTPS(HTTP+TLS/SSL)

TLS/SSL

TLS/SSL的功能实现主要依赖于三类基本算法:

  • 非对称加密实现身份认证和密钥协商
  • 对称加密算法采用协商的密钥对数据加密
  • 散列函数验证信息的完整性,针对于密钥泄露的不安全性
    SSL
    结合三类算法的特点,TLS 的基本工作方式:
  1. 客户端使用非对称加密与服务器进行通信,实现身份验证并协商对称加密使用的密钥
  2. 然后对称加密算法采用协商密钥对信息以及信息摘要进行加密通信,不同的节点之间采用的对称密钥不同,从而可以保证信息只能通信双方获取

对称加密

  • 相同的密钥可以用于信息的加密和解密,掌握密钥才能获取信息,能够防止信息窃听,通信方式是1对1(前提示密钥不泄露)
  • 算法公开、计算量小、加密速度快、加密效率高
  • 客户端、服务器双方都需要维护大量的密钥,维护成本很高
  • 因每个客户端、服务器的安全级别不同,密钥容易泄露,交易双方都使用同样钥匙,安全性得不到保证

非对称加密

  • 非对称加密算法的特点加密和解密分别使用不同的密钥,客户端用公钥对请求内容加密,服务器使用私钥对内容解密,反之亦然。
  • 相对来说加解密速度较慢,使用非对称加密在数据加密解密过程需要消耗一定时间,降低了数据传输效率
  • 公钥是公开的,所以针对私钥加密的信息,黑客截获后可以使用公钥进行解密,获取其中的内容
  • 公钥并不包含服务器的信息,使用非对称加密算法无法确保服务器身份的合法性,存在中间人攻击的风险,服务器发送给客户端的公钥可能在传送过程中被中间人截获并篡改

完整性验证算法

  • 常见的有 MD5、SHA1、SHA256,该类函数特点是函数单向不可逆、对输入非常敏感、输出长度固定,针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改并验证数据的完整性
  • 在信息传输过程中,散列函数不能单独实现信息防篡改,因为明文传输,中间人可以修改信息之后重新计算信息摘要,因此需要对传输的信息以及信息摘要进行加密

密钥协商

密钥协商
HTTPS

身份验证

由于非对称加密无法确保服务器身份的合法性,存在中间人攻击的风险,例如:

  1. 客户端 C 和服务器 S 进行通信,中间节点 M 截获了二者的通信
  2. 节点 M 自己计算产生一对公钥pub_M和私钥 pri_M
  3. C 向 S 请求公钥时,M 把自己的公钥pub_M发给了 C
  4. C 使用公钥pub_M加密的数据能够被M解密,因为M掌握对应的私钥pri_M,而C无法根据公钥信息判断服务器的身份,从而 C 和 M 之间建立了“可信”加密连接
  5. 中间节点 M 和服务器S之间再建立合法的连接,因此 C 和 S 之间通信被M完全掌握,M 可以进行信息的窃听、篡改等操作。
    证书
  • 服务方 Server 向第三方机构CA提交公钥、组织信息、个人信息(域名)等信息并申请认证;
  • CA通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等;
  • 如信息审核通过,CA会向申请者签发认证文件-证书。证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名; 签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用CA的私钥对信息摘要进行加密,密文即签名;
  • 客户端 Client 向服务器 Server 发出请求时,Server 返回证书文件;
  • 客户端 Client 读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后,利用对应 CA的公钥解密签名数据,对比证书的信息摘要,如果一致,则可以确认证书的合法性,即公钥合法;
  • 客户端还会验证证书相关的域名信息、有效时间等信息;客户端会内置信任CA的证书信息(包含公钥),如果CA不被信任,则找不到对应 CA的证书,证书也会被判定非法。
  • 客户端的证书从生产出来之后就内置在主机中

搭建一个HTTPS服务器

搭建一个HTTPS服务器

结语

前端必须懂的计算机网络知识系列已经全部结束,欢迎大家研究讨论!