web-security

随机token

整个处理流程:

  1. 在需要处理盗刷的接口添加token参数校验
  2. web端在需要处理盗刷页面注入token
  3. Native(IOS,android)通过API形式请求token,在相应的接口请求时带上

服务端生成token

使用redis管理token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//生成token
Redis.SETEX(token,ttl, 1,cb);
//验证token
Redis.GET(token,function (err, data) {
if(data && data < 4){
//每个token在ttl时间间隔内只能使用3次
self.incrToken(token,tokenPre,cb);
}else{
self.removeToken(token,tokenPre,function(err,data){
cb(null,false);
});
}
});
}

web端在render前注入token以及script脚本以便获取token值

token值切分成每个字符插入到HTML dom 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var bodyEndStr = '</body>';
var bodyStartStr = '<body';
var token = '后端生成的token';
var tokenId = '_' + FN_UTIL.generateSalt(Math.ceil(Math.random()*10));//随机生成字符数字字符串作为dom id,为了避免第一个字符是数字,所以添加了前缀_
var bodyStarIndex = html.indexOf(bodyStartStr);//body标签开启位置
var bodyEndIndex = html.indexOf(bodyEndStr);//body标签结束位置
var htmlTagLastIndexs = FN_UTIL.matchHtmlTags(html.substring(bodyStarIndex,bodyEndIndex+1));
var htmlTagLen = htmlTagLastIndexs.length;
var tokenLen = token.length;
var times = Math.floor(htmlTagLen/tokenLen);
var randomIndex = 0;
var addLen = 0;
var scriptStr = 'var r="";function g(id){return document.getElementById(id).innerHTML}';
for(var i = 0 ; i < tokenLen ; i++){
var htmlTag = FN_UTIL.makeHtmlTag(tokenId+i,token[i]);
randomIndex += Math.floor(Math.random()*times);
if(!htmlTagLastIndexs[randomIndex]){
randomIndex = htmlTagLen - 1;
}
var curIndex = bodyStarIndex+htmlTagLastIndexs[randomIndex]+addLen;
var prevHtml = html.substring(0,curIndex);
var nextHtml = html.substr(curIndex);
scriptStr += 'r += g("'+(tokenId+i)+'");';
if(i === tokenLen-1){
scriptStr += 'w.xxx=r;';
html = prevHtml.concat(htmlTag,'<script>(function(w){'+scriptStr+'})(window)</script>',nextHtml);//执行js获取token值
}else{
html = prevHtml.concat(htmlTag,nextHtml);
}
addLen += htmlTag.length;
}

FN_UTIL 的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var SALTCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var htmlTags = ['b','big','i','small','tt','abbr','acronym','cite','code','dfn','em','kbd','strong','samp','time','bdo','map','q','span','sub','sup','button','label'];
module.exports = {
generateSalt:function(length){
var i, r = [];
for (i = 0; i < length; ++i) {
r.push(SALTCHARS[Math.floor(Math.random() * SALTCHARS.length)]);
}
return r.join('');
},
matchHtmlTags:function(html){
var reg = new RegExp(/<\/[^>]+>/gim);
var lastIndexs = [];
var result;
while((result = reg.exec(html)) != null){
lastIndexs.push(reg.lastIndex);
}
return lastIndexs;
},
makeHtmlTag:function(id,val){
var tag = this.randomHtmlTagName();
return '<'+tag+' id="'+id+'" style="display:none;">' + val + '</'+tag+'>';
},
randomHtmlTagName:function(){
return htmlTags[Math.floor(Math.random()*htmlTags.length)];
}
}

Native(IOS,Android)端

1
2
3
4
5
//获取服务器端的时间戳
var serverTime = getServerTime();
//使用对称加密算法加密特定字符串和时间戳
var encodeData = crypto.encode('xxxx_time');
//服务器端解密加密数据,提取时间戳,与server时间戳对比,在一定差值(10s)内允许生成token,客户端通过token,执行后续操作

优点

  • 整个防盗流程都是技术手段在处理,对用户透明,既起到一定的安全防护又不失用户体验
  • Native端通过加密获取token,即使获取token的接口被监听,有时间戳校验,也是在很大程度上防止盗刷

注意,随机的标签不能是块级元素,不然原本块级元素内包含内联元素,嵌入块级元素后会导致元素布局错乱.内联元素列表参照https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements

粤ICP备18054847号-2
本站总访问量次 本站访客数人 本文总阅读量
{% if theme.baidu_push %} {% endif %}