记一次react项目报文全局加密过程

前言

在常见前端SAP项目中,浏览器端通过HTTP请求获取数据,这个过程可以通过打开浏览器控制台看到结果,
平时开发过程中也会有通过工具模拟HTTP请求的过程,例如postman等工具,可以通过工具直接获得数据
公司新项目上线后,安全组的同事对系统进行扫描排查,提出了一堆敏感信息问题...

存在问题

解决思路

因为之前有爆出说客户信息泄露的问题,为了防微杜渐,系统还是有必要对敏感信息进行加密
解决方案可以按安全组同事的建议对提出的某些字段在代码上进行单个加密后解密
但是这样的话会存在一个问题,需要改动到很多地方的代码,另外自己在排查的时候也会增加难度
后续如果增加需要继续加密的字段的话又要改动代码,后续维护的成本过高
如果能在同一的请求方法那里对请求参数进行加密,在服务端统一进行解密,那就不用改动到代码
后续安全组继续提出敏感信息问题的话也可以不用改动到代码

项目架构

前端 react + axios
服务端 koa2

react + axios改造

引入加解密模块crypto-browserify

npm i --save crypto-browserify

加解密方法封装

const cryptoConfig = {
    algorithm: 'aes-128-ecb',
    key: 'abcd1234abcd1234', // 16 B 128 bits
}
export default {
    encrypt: function (data) {
    try {
      if (!data) {
        return data;
        // throw '加密参数错误';
      }
      let jsonData = JSON.stringify(data);
      if (Object.prototype.toString.call(jsonData) !== "[object String]") {
        return data;
        // throw '加密参数类型错误, 只能是 String 类型';
      }
      let cipher = crypto.createCipheriv(cryptoConfig.algorithm, cryptoConfig.key, '');
      let encrypted = cipher.update(jsonData, 'utf8', 'hex');
      encrypted += cipher.final('hex');
      console.log(encrypted);
      return encrypted;
    } catch (e) {
      console.error(e);
      return data;
    }
  },
  decrypt: function (data) {
    try {
      if (!data) {
        // throw '加密参数错误';
        return data;
      }
      if (Object.prototype.toString.call(data) !== "[object String]") {
        // throw '加密参数类型错误, 只能是 String 类型';
        return data;
      }
      const decipher = crypto.createDecipheriv(cryptoConfig.algorithm, cryptoConfig.key, '');
      let decrypted = decipher.update(data, 'hex', 'utf8');
      decrypted += decipher.final('utf8');
      let parseResp = decrypted;
      if (decrypted) {
        parseResp = JSON.parse(decrypted);
      }
      return parseResp;
    } catch (e) {
      console.error(e);
      return data;
    }
  }
};

对axios请求方法进行改造,以post方法为例

const fetch = (url, options) => {
  const { method = 'get', data } = options
  switch (method.toLowerCase()) {
   ...
    case 'post':
     return cryptoPost(url, data);
    default:
      return axios(options)
  }
}

// 统一对请求参数进行加密,返回结果进行解密
const cryptoPost = (url, data) => {
  return new Promise(async (resolve, reject) => {
    let resp;
    let encryptParam = Utils.encrypt(data);
    resp = await axios.post(url, {encryptParam}, { headers:{'content-type': 'application/json'}, 'withCredentials':  true})
    if (resp && resp.data) {
      resp.data = Utils.decrypt(resp.data);
    }
    resolve(resp);
  });
}

koa2加解密改造

加解密方法封装(基本同前端一样)

const crypto = require('crypto');
const cryptoConfig = {
    algorithm: 'aes-128-ecb',
    key: 'abcd1234abcd1234', // 16 B 128 bits
}
exports.encrypt = (data) => {
  try {
    if (!data) {
      // throw '加密参数错误';
      return data;
    }
    let jsonData = JSON.stringify(data);
    if (Object.prototype.toString.call(jsonData) !== "[object String]") {
      // throw '加密参数类型错误, 只能是 String 类型';
      return data;
    }
    let cipher = crypto.createCipheriv(config.cryptoConfig.algorithm, config.cryptoConfig.key, '');
    let encrypted = cipher.update(jsonData, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
  } catch (e) {
    console.error(e);
    return data;
  }
};

exports.decrypt = (data) => {
  try {
    if (!data) {
      // throw '加密参数错误';
      return data;
    }
    if (Object.prototype.toString.call(data) !== "[object String]") {
      // throw '加密参数类型错误, 只能是 String 类型';
      return data;
    }
    const decipher = crypto.createDecipheriv(config.cryptoConfig.algorithm, config.cryptoConfig.key, '');
    let decrypted = decipher.update(data, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    let parseResp = decrypted;
    if (decrypted) {
      parseResp = JSON.parse(decrypted);
    }
    return parseResp;
  } catch (e) {
    console.error(e);
    return data;
  }
};

在中间件中使用加解密

app.use(async (ctx, next) => {
    // get请求和指定白名单的请求不走这个判断
    if (['/api/checkToken', '/api/autoLogin'].includes(ctx.request.originalUrl) || ctx.method === 'GET') {
      await next();
    } else {
      let needDecryptAndEncrypt = false;
      if (ctx.request.body) {
        if (ctx.request.body.encryptParam) {
          needDecryptAndEncrypt = true;
          let body = decrypt(ctx.request.body.encryptParam);
          ctx.request.body = body;
        }
      }
      await next();
      if (ctx.body && needDecryptAndEncrypt) {
        let respBody = encrypt(ctx.body);
        ctx.body = respBody;
      }
    }
  });

改造结果

请求参数

返回参数

系统使用正常,与之前无异

调试问题

请求参数和返回参数都加密了,这个对前端排查问题来说是一件很痛苦的事,所以最好根据需要
增加一个后门方便前端以明文的方式查看参数
思路是在前端axios方法那边做一些特殊处理......
加了后门以后的方法
const cryptoPost = (url, data) => {
  return new Promise(async (resolve, reject) => {
    let resp;
    if (localStorage.getItem('plaintextMode') == 1) {        // 明文模式不做处理
      resp = await axios.post(url, data, { headers:{'content-type': 'application/json'}, 'withCredentials':  true})
    } else {
      let encryptParam = Utils.encrypt(data);
      resp = await axios.post(url, {encryptParam}, { headers:{'content-type': 'application/json'}, 'withCredentials':  true})
      if (resp && resp.data) {
        resp.data = Utils.decrypt(resp.data);
      }
    }
    resolve(resp);
  });
}
原文链接:juejin.im

上一篇:fullcalendar 严格按照给定顺序排序的一种实现
下一篇:canvas-圆弧形可拖动进度条

相关推荐

  • 🌈 React 函数式组件优化

    1. React 性能优化思路 减少重新 render 的次数。 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。

    1 个月前
  • 🆘 一次理解清楚,为什么使用 React useEffect 中使用 setInterval 获取的值不是最新的

    Intro 这篇文章将通过一个使用 React Hook 常遇到的问题(stale state)入手,尝试理解 Hook 的内部运行逻辑。 废话不多说,直接看示例Sandbox。

    4 个月前
  • 高频数据交换下Flutter与ReactNative的对比

    (标题图片来自网络,侵删) 后端使用go写的socketio服务模拟期货行情数据,每10ms推送10条行情数据 ReactNative已经尽力优化了。 Flutter由于没flutter-socket...

    2 年前
  • 高性能迷你React框架 anu1.3.0 发布

    anujs1.3.0是一款高性能React-like框架,是目前世界上对React16兼容最好的迷你库。 自React16起,相继推出createContext,createPortal, creat...

    3 年前
  • 高德地图 react-amap 实战

    react-amap 是基于 React 的高德地图组件。 1. 获取地图示例 react-amap 作为高德地图在 React 中的实现,实际使用中不可避免的需要通过地图对象调用各种方法,reac...

    1 年前
  • 高品质 React UI 组件

    A high quality UI Toolkit, A Component Library for React 16+. 💘 Installation npm install isui --s...

    3 年前
  • 骚操作!在react中使用vuex

    原文地址 前言 笔者最近在学习使用react,提到react就绕不过去redux。redux是一个状态管理架构,被广泛用于react项目中,但是redux并不是专为react而生,两者还需要react...

    2 年前
  • 项目文档说明:react + Ant Design 的 blog-react-admin

    前言 此 blog-react-admin 项目是基于 蚂蚁金服开源的 ant design pro 之上,用 react 全家桶 + Ant Design 的进行再次开发的,项目已经开源,项目地址...

    2 年前
  • 面试题:Hooks 与 React 生命周期的关系

    React 生命周期很多人都了解,但通常我们所了解的都是 单个组件 的生命周期,但针对 Hooks 组件、多个关联组件(父子组件和兄弟组件) 的生命周期又是怎么样的喃?你有思考和了解过吗,接下来我们将...

    1 年前
  • 面试官:请你在React中引入Vue3的@vue/reactivity,实现响应式。

    前言 React的状态管理是一个缤纷繁杂的大世界,光我知道的就不下数十种,其中有最出名immutable阵营的redux,有mutable阵营的mobx,react-easy-state,在hooks...

    9 个月前

官方社区

扫码加入 JavaScript 社区