前后端数据加密传输(附go语言实现)
创始人
2024-06-02 08:42:03
0

前言

一般的对外服务都是需要服务器支持 https 传输的,那既然有了 https ,数据已经加密了,为什么还要做这个事情呢?

现在大多数应用服务都是使用的前后端分离的方式来开发的,以后端提供的接口来进行业务数据交互。相信有过 web 开发经验的都打开过浏览器的调试控制台,在 Network 中能够看到当前页面发过哪些请求,而且能够看到请求的参数和返回值。

这里就是希望我们的传输/返回数据不被别人看到,直接给参数和返回值加密,主要不是为了防止数据传输过程中被第三方拦截而做的,而是为了防止不法分子调试获取到数据,对服务器进行恶意攻击。

加解密

简单说一下加解密相关的知识,本篇我们只需要了解下 对称加密非对称加密 即可:

  • 对称加密:只有一个密钥,使用同一个密钥对数据加解密,常见的有 DESAES 等。
  • 非对称加密:两个密钥,一个公钥,一个私钥,公钥用于加密数据,私钥解密数据。常常将公钥发给前端,私钥保存在后端(一定不能泄露),例如 RSADSA 等。

思路

这里提供两种思路供大家参考,相应的变种很多,可以自己根据需求实现。

第一版

客户端服务端使用对称加密传输数据,客户端服务端定好传输密钥,直接写死在代码里。
客户端加密 urlquery 参数(即问号后面那串 id=xxx&name=xxx)或者传输的 body 内容(序列化后加密传输),服务端进行相应的解密操作。

在这里插入图片描述

模拟一下客户端服务器的代码,对称加密算法使用 AES,密钥为 4335dfgeredhfdsd

服务端使用 gin 框架中添加中间件 middlewareDecryptReq,用来解析客户端加密后的数据,并使用统一的数据返回入口 EncryptWriter 函数来做数据加密返回。

示例代码 如下:

package mainimport ("bytes""encoding/base64""fmt""github.com/duke-git/lancet/v2/cryptor""github.com/gin-gonic/gin""io""io/ioutil""net/http"
)var (aesKey = []byte("4335dfgeredhfdsd")
)func main() {engine := gin.Default()engine.Use(middlewareDecryptReq())engine.GET("/g", func(c *gin.Context) {EncryptWriter(c, []byte(c.Request.URL.RawQuery))})engine.POST("/p", func(c *gin.Context) {buf, err := c.GetRawData()if err != nil {c.String(http.StatusInternalServerError, err.Error())return}EncryptWriter(c, buf)})engine.Run(":4780")
}func middlewareDecryptReq() func(c *gin.Context) {return func(c *gin.Context) {if c.Request.URL.RawQuery != "" {res, err := AesCbcDecryptBase64([]byte(c.Request.URL.RawQuery), aesKey)if err != nil {c.String(http.StatusBadRequest, err.Error())c.Abort()return}c.Request.URL.RawQuery = string(res)}data, err := ioutil.ReadAll(c.Request.Body)if err != nil {c.String(http.StatusBadRequest, err.Error())c.Abort()return}defer c.Request.Body.Close()if len(data) == 0 {c.Next()return}plainBuf, err := AesCbcDecryptBase64(data, aesKey)if err != nil {c.String(http.StatusBadRequest, err.Error())c.Abort()return}r := bytes.NewBuffer(plainBuf)rd := io.NopCloser(r)c.Request.Body = rd}
}func EncryptWriter(c *gin.Context, data []byte) {cipherBuf := AesCbcEncryptBase64(data, aesKey)c.String(http.StatusOK, string(cipherBuf))
}func AesCbcEncrypt(plainText, secretKey []byte) []byte {return cryptor.AesCbcEncrypt(plainText, secretKey)
}func AesCbcDecrypt(cipherText, key []byte) []byte {return cryptor.AesCbcDecrypt(cipherText, key)
}func AesCbcEncryptBase64(plainText, secretKey []byte) (cipherTextBase64 []byte) {encryBytes := AesCbcEncrypt(plainText, secretKey)cipherTextBase64 = make([]byte, base64.StdEncoding.EncodedLen(len(encryBytes)))base64.StdEncoding.Encode(cipherTextBase64, encryBytes)return
}func AesCbcDecryptBase64(cipherTextBase64, key []byte) (res []byte, err error) {plainTextBytes := make([]byte, base64.StdEncoding.DecodedLen(len(cipherTextBase64)))n, err := base64.StdEncoding.Decode(plainTextBytes, cipherTextBase64)if err != nil {return}res = AesCbcDecrypt(plainTextBytes[:n], key)return
}func tServerGet() {queryData := []byte("id=xxx&name=xxx")cipherBuf := AesCbcEncryptBase64(queryData, aesKey)resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:4780/g?%s", string(cipherBuf)))if err != nil {fmt.Println(err)return}fmt.Println(resp.StatusCode)buf, err := io.ReadAll(resp.Body)if err != nil {fmt.Println(err)return}fmt.Println("原始数据:", string(buf))plainBuf, err := AesCbcDecryptBase64(buf, aesKey)if err != nil {fmt.Println(err)return}fmt.Println("解密成功:", string(plainBuf))
}func tServerPost() {data := []byte(`{"id":"xxx","name":"法外狂徒"}`)cipherBuf := AesCbcEncryptBase64(data, aesKey)resp, err := http.Post("http://127.0.0.1:4780/p", "application/json", bytes.NewReader(cipherBuf))if err != nil {fmt.Println(err)return}fmt.Println(resp.StatusCode)buf, err := io.ReadAll(resp.Body)if err != nil {fmt.Println(err)return}fmt.Println("原始数据:", string(buf))plainBuf, err := AesCbcDecryptBase64(buf, aesKey)if err != nil {fmt.Println(err)return}fmt.Println("解密成功:", string(plainBuf))
}

缺点:

  1. 客户端密钥容易找,拿到了密钥,那也就能对数据进行加解密操作,可以篡改数据内容。
  2. 密钥写死在代码里,如果要改个密钥比较麻烦。

第二版

在第一版的基础上,添加了一些复杂性。借鉴了 https 的实现原理,同时使用 非对称加密对称加密

客户端先向服务器要 非对称加密公钥,然后根据时间戳或者其他算法生成 对称加密密钥,然后将这个 对称加密密钥 使用 非对称加密公钥 加密后传给服务器。
服务器解密后,再将返回数据使用 对称加密密钥 加密后返回给客户端,客户端再解密获取数据。

在这里插入图片描述

示例代码 如下:

package mainimport ("bytes""crypto/rand""crypto/rsa""crypto/x509""encoding/base64""encoding/pem""errors""fmt""github.com/duke-git/lancet/v2/cryptor""github.com/gin-gonic/gin""io""io/ioutil""net/http""sync"
)var (onceGenerateKey sync.OncersaPubKey       []bytersaPrivateKey   []byte
)const ginAesKey = "ginAesKey"func main() {engine := gin.Default()engine.GET("/rsa_key", func(c *gin.Context) {onceGenerateKey.Do(func() {private, pub, er := GenRsaKey()if er != nil {fmt.Println(er)return}rsaPubKey = pubrsaPrivateKey = private})c.String(http.StatusOK, string(rsaPubKey))})cryptRouter := engine.Group("", middlewareDecryptReq())cryptRouter.GET("/g", func(c *gin.Context) {EncryptWriter(c, []byte(c.Request.URL.RawQuery))})cryptRouter.POST("/p", func(c *gin.Context) {buf, err := c.GetRawData()if err != nil {c.String(http.StatusInternalServerError, err.Error())return}EncryptWriter(c, buf)})engine.Run(":4780")
}func middlewareDecryptReq() func(c *gin.Context) {return func(c *gin.Context) {// aes 密钥放在 query 参数里,没有值直接报错if c.Request.URL.RawQuery == "" {c.String(http.StatusBadRequest, "参数错误")c.Abort()return}res, err := RsaDecryptBase64([]byte(c.Request.URL.RawQuery), rsaPrivateKey)if err != nil {c.String(http.StatusBadRequest, err.Error())c.Abort()return}c.Request.URL.RawQuery = string(res)ak := c.Query("aesKey")if ak == "" {c.String(http.StatusBadRequest, "参数错误")c.Abort()return}c.Set(ginAesKey, ak)data, err := ioutil.ReadAll(c.Request.Body)if err != nil {c.String(http.StatusBadRequest, err.Error())c.Abort()return}defer c.Request.Body.Close()if len(data) == 0 {c.Next()return}plainBuf, err := RsaDecryptBase64(data, rsaPrivateKey)if err != nil {c.String(http.StatusBadRequest, err.Error())c.Abort()return}rd := io.NopCloser(bytes.NewBuffer(plainBuf))c.Request.Body = rd}
}func EncryptWriter(c *gin.Context, data []byte) {cipherBuf := AesCbcEncryptBase64(data, []byte(c.GetString(ginAesKey)))c.String(http.StatusOK, string(cipherBuf))
}func Base64Encrypt(data []byte) []byte {res := make([]byte, base64.StdEncoding.EncodedLen(len(data)))base64.StdEncoding.Encode(res, data)return res
}func Base64Decrypt(data []byte) ([]byte, error) {res := make([]byte, base64.StdEncoding.DecodedLen(len(data)))n, err := base64.StdEncoding.Decode(res, data)return res[:n], err
}func AesCbcEncrypt(plainText, secretKey []byte) []byte {return cryptor.AesCbcEncrypt(plainText, secretKey)
}func AesCbcDecrypt(cipherText, key []byte) []byte {return cryptor.AesCbcDecrypt(cipherText, key)
}func AesCbcEncryptBase64(plainText, secretKey []byte) (cipherTextBase64 []byte) {encryptBytes := AesCbcEncrypt(plainText, secretKey)return Base64Encrypt(encryptBytes)
}func AesCbcDecryptBase64(cipherTextBase64, key []byte) (res []byte, err error) {plainTextBytes, err := Base64Decrypt(cipherTextBase64)if err != nil {return}res = AesCbcDecrypt(plainTextBytes, key)return
}func GenRsaKey() (prvkey, pubkey []byte, err error) {// 生成私钥文件privateKey, err := rsa.GenerateKey(rand.Reader, 1024)if err != nil {return}derStream := x509.MarshalPKCS1PrivateKey(privateKey)block := &pem.Block{Type:  "RSA PRIVATE KEY",Bytes: derStream,}prvkey = pem.EncodeToMemory(block)publicKey := &privateKey.PublicKeyderPkix, err := x509.MarshalPKIXPublicKey(publicKey)if err != nil {return}block = &pem.Block{Type:  "RSA PUBLIC KEY",Bytes: derPkix,}pubkey = pem.EncodeToMemory(block)return
}// 公钥加密
func RsaEncrypt(data, keyBytes []byte) ([]byte, error) {//解密pem格式的公钥block, _ := pem.Decode(keyBytes)if block == nil {return nil, errors.New("public key error")}// 解析公钥pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)if err != nil {return nil, err}// 类型断言pub := pubInterface.(*rsa.PublicKey)//加密ciphertext, err := rsa.EncryptPKCS1v15(rand.Reader, pub, data)if err != nil {return nil, err}return ciphertext, nil
}
func RsaEncryptBase64(data, keyBytes []byte) ([]byte, error) {encryptBuf, err := RsaEncrypt(data, keyBytes)if err != nil {return nil, err}res := Base64Encrypt(encryptBuf)return res, nil
}// 私钥解密
func RsaDecrypt(ciphertext, keyBytes []byte) ([]byte, error) {//获取私钥block, _ := pem.Decode(keyBytes)if block == nil {return nil, errors.New("private key error!")}//解析PKCS1格式的私钥priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)if err != nil {return nil, err}// 解密data, err := rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)if err != nil {return nil, err}return data, nil
}
func RsaDecryptBase64(ciphertext, keyBytes []byte) ([]byte, error) {buf, err := Base64Decrypt(ciphertext)if err != nil {return nil, err}return RsaDecrypt(buf, keyBytes)
}func serverGetRsaKey() (pubKey []byte, err error) {resp, err := http.Get("http://127.0.0.1:4780/rsa_key")if err != nil {return}return io.ReadAll(resp.Body)
}func tServerGet() {pubKey, err := serverGetRsaKey()if err != nil {fmt.Println(err)return}//随机生成 aes keyaes_key := "4335dfgeresdheud"queryData := []byte("id=xxx&name=xxx&aesKey=" + aes_key)cipherBuf, err := RsaEncryptBase64(queryData, pubKey)resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:4780/g?%s", string(cipherBuf)))if err != nil {fmt.Println(err)return}fmt.Println(resp.StatusCode)buf, err := io.ReadAll(resp.Body)if err != nil {fmt.Println(err)return}fmt.Println("原始数据:", string(buf))plainBuf, err := AesCbcDecryptBase64(buf, []byte(aes_key))if err != nil {fmt.Println(err)return}fmt.Println("解密成功:", string(plainBuf))
}func tServerPost() {pubKey, err := serverGetRsaKey()if err != nil {fmt.Println(err)return}//随机生成 aes keyaes_key := "4335dfgeresdheud"queryCipher, err := RsaEncryptBase64([]byte("aesKey="+aes_key), pubKey)if err != nil {fmt.Println(err)return}data := []byte(`{"id":"xxx","name":"法外狂徒"}`)cipherBuf, err := RsaEncryptBase64(data, pubKey)if err != nil {fmt.Println(err)return}resp, err := http.Post("http://127.0.0.1:4780/p?"+string(queryCipher), "application/json", bytes.NewReader(cipherBuf))if err != nil {fmt.Println(err)return}fmt.Println(resp.StatusCode)buf, err := io.ReadAll(resp.Body)if err != nil {fmt.Println(err)return}fmt.Println("原始数据:", string(buf))plainBuf, err := AesCbcDecryptBase64(buf, []byte(aes_key))if err != nil {fmt.Println(err)return}fmt.Println("解密成功:", string(plainBuf))
}

这样,就算攻击者获取到密钥了,也不能解密数据,但是还是防止不了反编译代码,打断点调试获取数据。

攻击者也可以自己在中间模拟生成 非对称加密密钥,当客户端发起请求获取服务器 公钥 时,可以将自己模拟的 公钥 发给客户端,保存下服务器传回的 公钥。然后在客户端发起请求时,可以先用自己生成的 私钥 解密数据,然后再篡改数据,使用刚刚保存的服务器 公钥 加密后传给客户端。

前端

前端 js 可以使用 jsencrypt 库,本篇不做详细介绍了。相关的客户端加密解密代码都用 go 实现了,贴在上面的 示例代码 的末尾。

总结

这里做的加密传输,仅仅只能添加破解的复杂性,不能真的保证数据不泄露

所以一般的服务在做数据交互时也并不会刻意去做这种前后端的数据加密,一般性的是做服务器的 session 或者客户端的 cookie 校验,来保证数据不泄露,不被篡改

但你要是说我做 session 校验的 token 在前端泄露了,那这种也是用户自己的问题(进入黑客网站或其他做了鉴权),服务防不了这种,顶多是增加校验复杂性来让其更加繁琐。所以说在类似付款的操作的时候,都会再进行一次校验(输入密码/验证指纹等),来再做一次权限校验。

本篇前后端做数据加密,主要的一个场景就是:我有一个平台服务,这时候我又做了一个配套的单机应用,单机应用是可以随意给其他人使用的,为了保证我的软件不被别人轻易的破解获取数据而做的一层防护。

相关内容

热门资讯

ip是安卓系统吗,通过IP地址... 你有没有想过,那个陪伴你每天刷剧、玩游戏、办公的IP,它是不是安卓系统呢?别急,今天就来揭开这个谜底...
安卓系统谁负责升级,揭秘幕后负... 你有没有想过,你的安卓手机为什么有时候会突然收到系统更新的通知呢?是不是好奇,是谁在背后默默地为你的...
安卓系统需要降级吗,安卓系统升... 你有没有发现,你的安卓手机最近有点儿“老态龙钟”了呢?运行速度慢吞吞的,有时候还卡个不停。这时候,你...
性价比手机安卓系统,盘点安卓系... 你有没有想过,在这个手机更新换代如此迅速的时代,如何用最少的钱,买到最满意的手机呢?没错,我要说的是...
虚拟大师安卓2.0系统,安卓新... 你有没有听说最近虚拟大师安卓2.0系统火得一塌糊涂?这可不是空穴来风,而是真的让不少手机用户都跃跃欲...
谷歌同步安卓10系统,智能体验... 你知道吗?最近谷歌又放大招了,安卓10系统正式上线啦!这可是个大新闻,咱们得好好聊聊。想象你的手机瞬...
米9安卓系统最高,小米9安卓系... 你有没有发现,最近你的手机是不是有点儿不给力了?别急,别急,让我来给你揭秘为什么你的小米9安卓系统最...
五菱安卓系统下载,开启智能出行... 你有没有听说最近五菱汽车也要玩儿高科技了?没错,就是那个我们平时在路上随处可见的“神车”——五菱宏光...
华为安卓6.0系统特点,创新与... 你知道吗?华为的安卓6.0系统最近可是火得一塌糊涂呢!作为一个紧跟科技潮流的数码达人,我必须得给你好...
安卓系统指令怎么用,或者根据标... 你有没有想过,你的安卓手机里那些神秘的系统指令其实就像是一把神奇的钥匙,能帮你解锁手机的各种隐藏功能...
最好用安卓系统排行,2023年... 你有没有想过,为什么安卓系统这么受欢迎呢?没错,就是那个几乎无处不在的操作系统。今天,就让我带你一起...
红辣椒刷安卓系统,深度解析与全... 你有没有想过,你的安卓手机里竟然也能装上红辣椒的系统?没错,就是那个让无数游戏玩家热血沸腾的红辣椒!...
surface 3跑安卓系统,... 你有没有想过,如果你的Surface 3也能跑安卓系统,那会是怎样的场景呢?想象你手中的平板瞬间变成...
安卓系统自带手机壁纸,探索安卓... 亲爱的手机控们,你是否曾好奇过,为什么你的安卓手机里会有那些精美的壁纸?今天,就让我带你一探究竟,揭...
安卓屏幕装鸿蒙系统,跨平台新体... 你知道吗?最近在手机圈子里,有个话题可是火得一塌糊涂,那就是——安卓屏幕装鸿蒙系统。是不是听起来有点...
安卓电视改造系统教程,轻松打造... 亲爱的电视迷们,你是否厌倦了安卓电视那千篇一律的系统界面?想要给它来个焕然一新的改造?别急,今天我就...
优品才子安卓系统,引领智能生活... 你知道吗?在手机操作系统界,最近可是掀起了一股“优品才子”安卓系统的热潮呢!这款系统不仅功能强大,而...
小米电视安装安卓系统,打造智能... 亲爱的读者们,你是否也像我一样,对小米电视的强大性能和智能体验情有独钟?但你是否知道,小米电视其实可...
安卓系统路由表,揭秘网络数据传... 你有没有想过,你的安卓手机里那些看似复杂的设置,其实背后隐藏着不少小秘密呢?比如,今天咱们就要来聊聊...
安卓系统的广告拦截,守护您的手... 你有没有发现,手机里的安卓系统越来越智能了,但随之而来的广告也越来越多,简直让人头疼不已。今天,就让...