- _nosay
App数据传输安全篇(1)
2017-08-17 14:26:30
今天做app与服务端数据通信的时候,刚好做到数据加密这一块,特来记录一下实现过程。
在app中,必定需要和服务端进行数据通信,而如果数据通过明文传输,很可能被别有用心的人通过抓包,分析数据等进行漏洞分析,再如果服务端api确实存在逻辑漏洞,那就可能会给客户带画困扰,甚至会带来一场灾难。所以再我看来,数据加密这一块是必不可少的。
大家可以根据各自的需求,可以设计自己的加密方式,可以选择一种或几种密码对数据进行加密。本文比较简单的采用rsa进行加密,具体过程如下:
首先需要在控制台生成相应的公钥和私钥,以供加密解密之用。如果是双服务器之间进行通信,需要各自生成各自的公私钥,然后进行交换,以供解密之用。当然服务器和app也可以采用此流程,但本人目前针对此博客做一个app出来,显然没有那么高的要求,所以只要生成一方的公私钥就可以啦,打开控制台,输入:
//生成长度为 1024 的私钥:private_key.pem (文件名可自定义) openssl genrsa -out private_key.pem 1024 //生成解密所需 .pem公钥文件:public_key.pem(文件名可自定义) openssl rsa -in private_key.pem -pubout -out public_key.pem //使用私钥文件创建所需的证书:rsaCertReq.csr(文件名可自定义) openssl req -new -key private_key.pem -out rsaCertReq.csr //使用 x509 创建证书:rsaCert.crt(文件名可自定义) openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt //生成 .der 格式的公钥:public_key.der(文件名可自定义) openssl x509 -outform der -in rsaCert.crt -out public_key.der //生成解密所需 .p12文件:private_key.p12(文件名可自定义) openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt
这样我们所需的全部文件就已经生成完毕了,其中 public_key.der 和 private_key.p12 是供ios加密解密之用的,后面我们会在apicloud中会使用到,public_key.pem,private_key.pem是供服务端和安卓加密解密用的,后面我们会在服务端用到。需要注意的是,我们在生成的过程中输入的密码,需要牢牢的记录下来,我们后面也会用到。
然后打开apicloud端,我们需要封装一个rsa.js文件,用来处理加密解密,以供全局使用。因为apicloud中,把所有的模块设置为异步执行,暂没有同步执行方案,所以我们无法在外部取到signature加密解密的值,比较坑,不过也没有关系,rsa.js代码如下,我们把它放到script/common下面
function rsaEncodeAndSend(url,postStr)
{
var signature = api.require('signature');
signature.rsa({
data: postStr,
publicKey: 'widget://res/rsa/public_key.der'
}, function(ret) {
if(ret.status) //如果加密成功
{
api.ajax({
url: url,
method: 'post',
data: {
values: {
data: ret.value
},
}
},function(ret, err){
if (ret) {
alert( JSON.stringify( ret ) );
} else {
alert( JSON.stringify( err ) );
}
});
}else{
console.log('加密失败')
}
});
}其中publicKey就是我们在控制台生成的der证书,放到代码相应的位置就可以了。当然这只是加密的,解密的我现在暂时没有好的办法封装,如果你有什么好的办法,可以通过邮箱联系我,谢谢。
我们先做登陆模块,界面如下

相应代码 login_win.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0"/>
<meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
<link rel="stylesheet" type="text/css" href="../css/aui.css" />
</head>
<body>
<header class="aui-bar aui-bar-nav" id="aui-header">
<a class="aui-btn aui-pull-left" tapmode onclick="closeWin()">
<span class="aui-iconfont aui-icon-left"></span>
</a>
<div class="aui-title">登录</div>
</header>
</body>
<script type="text/javascript" src="../script/api.js"></script>
<script type="text/javascript">
apiready = function(){
api.parseTapmode();
var header = $api.byId('aui-header');
$api.fixStatusBar(header);
var headerPos = $api.offset(header);
var body_h = $api.offset($api.dom('body')).h;
api.openFrame({
name: 'login_frm',
url: 'login_frm.html',
bounces: true,
rect: {
x: 0,
y: headerPos.h,
w: 'auto',
h: 'auto'
}
})
};
function closeWin(){
api.closeWin({
});
}
</script>
</html>
login_frm.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,width=device-width,initial-scale=1.0" />
<meta name="format-detection" content="telephone=no,email=no,date=no,address=no">
<title>Hello APP</title>
<link rel="stylesheet" type="text/css" href="../css/aui.css" />
<style>
.login-third {
margin-top: 3rem;
}
</style>
</head>
<body>
<section class="aui-content aui-margin-t-15">
<ul class="aui-list aui-form-list">
<li class="aui-list-item">
<div class="aui-list-item-inner">
<div class="aui-list-item-label aui-border-r color-orange">
手机号 <small class="aui-margin-l-5 aui-text-warning">+86</small>
</div>
<div class="aui-list-item-input aui-padded-l-10">
<input type="number" value="13329014137" placeholder="输入手机号" id="mobile">
</div>
</div>
</li>
<li class="aui-list-item">
<div class="aui-list-item-inner">
<div class="aui-list-item-input" style="width: auto;">
<input type="number" placeholder="输入短信验证码" id="code">
</div>
<div class="aui-list-item-label aui-margin-r-15" style="width: 6rem;">
<div id="verify_code" class="aui-btn aui-btn-info" style="width: 6rem;">获取验证码</div>
</div>
</div>
</li>
</ul>
</section>
<section class="aui-content-padded">
<div class="aui-btn aui-btn-block aui-btn-info aui-btn-sm" tapmode>登录</div>
</section>
<section class="aui-content-padded login-third">
<p class="aui-font-size-12 aui-text-center aui-margin-b-15">第三方账号登录</p>
<div class="aui-grid" style="background: none;">
<div class="aui-row">
<div class="aui-col-xs-4">
<i class="aui-iconfont aui-icon-qq"></i>
</div>
<div class="aui-col-xs-4">
<i class="aui-iconfont aui-icon-wechat"></i>
</div>
<div class="aui-col-xs-4">
<i class="aui-iconfont aui-icon-weibo"></i>
</div>
</div>
</div>
</section>
</body>
<script type="text/javascript" src="../script/api.js"></script>
<script type="text/javascript" src="../script/common/config.js"></script>
<script type="text/javascript" src="../script/zepto.min.js"></script>
<script type="text/javascript" src="../script/login/login.js"></script>
<script type="text/javascript" src="../script/common/rsa.js"></script>
</html>其中login.js内容为
function setLeftTime() {
var second = Math.floor(leftsecond);
$("#verify_code").html(second + "秒后可重发");
$('#verify_code').removeClass('aui-btn-info');
leftsecond--;
if (leftsecond < 1) {
clearInterval(timer);
try {
$("#verify_code").html("获取验证码");
$('#verify_code').attr("onclick", 'sendMobileValidSMSCode()');
$("#mobile").removeAttr("readonly");
} catch (E) {
console.log('error');
}
return;
}
}
function sendMobileValidSMSCode() {
var mobile = $('#mobile').val();
var mbTest = /^(13|14|15|17|18)[0-9]{9}$/;
if (mbTest.test(mobile)) {
leftsecond = 60;
timer = setInterval(setLeftTime, 1000);
$("#mobile").attr("readonly", true);
var url = serverUrl + "/api/qiuhan/getSmsCode";
var postStr = {"phone":mobile};
rsaEncodeAndSend(url,JSON.stringify(postStr));
} else {
api.toast({
msg: '请输入正确的手机号码!',
duration: 2000,
location: 'bottom'
});
}
}
apiready = function() {
api.parseTapmode();
$('#verify_code').click(function() {
sendMobileValidSMSCode();
});
};这样我们前端app页面大概就已经写好了,我们抓包看一下

这样我们就把加密的字符串发给了服务端,接下来我们需要完成服务端的解密功能。