- _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页面大概就已经写好了,我们抓包看一下
这样我们就把加密的字符串发给了服务端,接下来我们需要完成服务端的解密功能。