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证书,放到代码相应的位置就可以了。当然这只是加密的,解密的我现在暂时没有好的办法封装,如果你有什么好的办法,可以通过邮箱联系我,谢谢。


    我们先做登陆模块,界面如下

     image.png


相应代码  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页面大概就已经写好了,我们抓包看一下

image.png


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