接口鉴权扩展-加密与解密的实践-前端加密方式


分两篇:

  1. 第一篇:了解加密加密的基本概念
  2. 第二篇:实践前后台的加密解密流程

目标

分几种场景来介绍前后端加密解密的方式

前端加密

从新浪微博登陆-看前端加密方式

参考:【JS逆向练习】新浪微博登录加密参数分析

深入分析微博密码加密and百万级数据采集分享(登录篇)

新浪微博的登陆,分析登陆接口,会发现你输入的账号和密码参数是找不到的,取而代之的是su和sp参数,su是加密后的账号,sp参数是加密后的密码。su参数是通过Base64处理的,sp是通过servicetime(时间戳),nonce(一次性参数),rsaPubkey(RSA公钥) 处理加密而成。分析的具体流程看上面参考链接即可。

在这里插入图片描述

从上面可以得知,密码的加密是通过RSA非对称加密,RSA公钥加密得到。提取RSA加密的js:

微博登陆加密js

RSA公钥加密,只能保证数据被其他人获取,但是不能保证篡改。在第一篇提到,前端加密的意义,一部分是保证用户输入的真实数据,不会被中间人获取。中间人即使获得sp,依然无法得知真正的密码是什么,防止信息泄露,防止撞库。

加密流程js:

这里会用到 crypto-js 来进行base64 的转换,当然可以使用其他js代替。

参考:https://github.com/gengzi/codecopy/tree/master/src/main/resources/js/securityInterface

encrypt.js

用户名:

function getusername(username) {
    var str = CryptoJS.enc.Utf8.parse(username);
    return CryptoJS.enc.Base64.stringify(str);
}

密码:

function getpassword(pwd, servicetime, nonce, rsaPubkey) {
    var RSAKey = new sinaSSOEncoder.RSAKey();
    RSAKey.setPublic(rsaPubkey, '10001');
    var password = RSAKey.encrypt([servicetime, nonce].join('\t') + '\n' + pwd);
    return password;
}

代码实践

思路:通过java调用js的方式来实践,对于前端参数的加密,后端解密数据

(1)编写加密js,使用java调用js加密处理,调用后端接口。

(2)后端接口接收参数,解密处理,拿出原始数据。

环境:java8

代码参考:https://github.com/gengzi/codecopy/tree/master/src/main/resources/js/

crypto-js.js 、base.js 、encrypt.js 、paramCrypot.js

java代码:fun.gengzi.codecopy.business.authentication 下的代码

第一步:

    /**
     * java 调用js 进行加密
     */
    @Override
    public void paramEncryptionToJs() {
        ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
        ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
        try {
            String basePath = ClassLoader.getSystemClassLoader().getResource("js/base.js").getPath();
            String jsencryptPath = ClassLoader.getSystemClassLoader().getResource("js/jsencrypt/jsencrypt.js").getPath();
            String cryptoPath = ClassLoader.getSystemClassLoader().getResource("js/crypto-js-4.0.0/crypto-js.js").getPath();
            String encryptPath = ClassLoader.getSystemClassLoader().getResource("js/securityInterface/encrypt.js").getPath();
            String paramCrypotPath = ClassLoader.getSystemClassLoader().getResource("js/securityInterface/paramCrypot.js").getPath();
            nashorn.eval(Files.newBufferedReader(Paths.get(basePath.substring(1))));
            nashorn.eval(Files.newBufferedReader(Paths.get(cryptoPath.substring(1))));
            nashorn.eval(Files.newBufferedReader(Paths.get(jsencryptPath.substring(1))));
            nashorn.eval(Files.newBufferedReader(Paths.get(encryptPath.substring(1))));
            nashorn.eval(Files.newBufferedReader(Paths.get(paramCrypotPath.substring(1))));
            Invocable in = (Invocable) nashorn;
            String username = "16636663456";
            String password = "gengzi666";
            Object o = in.invokeFunction("requestParamCrypotByJsencrypt", username, password);
            logger.info("加密后的参数数据 : {} ", o);
            // 发送请求
            if (StringUtils.isNoneBlank(o.toString())) {
                // 发送请求
                String body = HttpRequest.post(SecurityInterfaceConstans.PARAMENCRYPTIONURL)
                        .body(o.toString()).execute().body();
 
            }
        } catch (ScriptException | IOException | NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

第二步:

/**
     * 其实前端对参数的加密,来保证安全,就好像是一个纸老虎。但是依然能提高接口的安全程度。
     * <p>
     * 敏感数据加密后,攻击者无法仅通过网络抓包来详细了解敏感数据的内容。
     * 为了让这个纸老虎更加逼真,对前端js 混淆,添加一些无效参数,记录用户行为,将这些组合在一起,提升攻击者的难度。
     * 可能攻击者分析分析着,就放弃了。
     * <p>
     * 该接口演示,使用微博登陆的加密js,对用户名和密码进行加密处理,并提交至后台。
     *
     * @return
     */
    @ApiOperation(value = "前端-后端:提交参数的加密与解密", notes = "前端-后端:提交参数的加密与解密" +
            "演示后台解密")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "ParamEncryptionEntity", value = "ParamEncryptionEntity", required = true)})
    @ApiResponses({@ApiResponse(code = 200, message = "\t{\n" +
            "\t    \"status\": 200,\n" +
            "\t    \"info\": {\n" +
            "\t        }\n" +
            "\t    \"message\": \"success\",\n" +
            "\t}\n")})
    @PostMapping("/paramEncryption")
    @ResponseBody
    public ReturnData paramEncryption(@RequestBody ParamEncryptionEntity paramEncryptionEntity) {
        // 编写加密js 的function 方法
        // java 调用这些js 方法,执行加密操作,发送ajax 请求
        // 前端js混淆,引入额外参数,替换页面的原属性值
        // 后台接受 ajax 请求,获取参数,将参数解密处理,执行具体业务
        logger.info("paramEncryptionEntity : {}", paramEncryptionEntity.toString());
 
        String nonce = paramEncryptionEntity.getNonce();
        String su = Base64.decodeStr(paramEncryptionEntity.getSu());
        logger.info("su : {}", su);
 
        // 跟 nonce 获取 publickey 省略
        Optional<String> sp = RSAUtils.decryptByPublic(paramEncryptionEntity.getSp(), secretkey);
        logger.info("sp : {}", sp.orElse(""));
        // 比对参数
        String signStr = sp.orElse("");
        String password = null;
        if (signStr.contains(paramEncryptionEntity.getServiceTime() + "\t" + paramEncryptionEntity.getNonce() + "\n")) {
            String[] split = signStr.split("\n");
            password = split[split.length - 1];
        }
 
        //TODO 与数据库比对账号密码,一致登陆成功
        logger.info("username :{} and password : {}", su, password);
 
 
        ReturnData ret = ReturnData.newInstance();
        ret.setSuccess();
        return ret;
    }
 

一些问题和解决方法

java调用js可能会提示:navigator is not defined 或者 window is not defined

参考:如果运行的环境不支持Windows和导航器,则不能使用这两个对象。

原因:java调用js,有些参数是浏览器支持的,所以会出现有些参数没有被定义

解决方法:在调用js前,先定义这些参数,并在加载在最前面。

var navigator = { appName: 'Netscape', userAgent: '', };
var window = {};

调用:

window.JSEncrypt = JSEncrypt;
 
window. // 需要这样调用
/**
* RSA 解密,使用密钥解密
* @param str 需要解密的内容
* @param key 密钥
* @returns {WordArray|PromiseLike<ArrayBuffer>|null|*|undefined}
*/
function decryptRSAByPrivateKey(str, key) {
    Encrypt = new window.JSEncrypt();
    Encrypt.setPrivateKey(key);
    return Encrypt.decrypt(str);
}

问题2: 使用java调用js 应该不能发送http请求(ajax)

因为ajax 使用的是浏览器的一些对象元素

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值