WebAssembly前后端加密实践

打印 上一主题 下一主题

主题 865|帖子 865|积分 2595

前言

本文通过探究一种前端加解密方式, 前端使用WebAssembly(wasm)提供公钥加密,后端使用私钥解密,能大大提高破解难度。 wasm使用支持多种语言,我们这里使用golang开辟,后端解密使用openresty + lua解密,如许后端应用就不必要做任何修改了。
WebAssembly简介

WebAssembly字面意思是web的汇编语言, 是⼀种新兴的⽹⻚虚拟机标准。现在已经作为W3C规范在各大主流浏览器得到支持。
Go 语言内置了 syscall/js 包,可以在 Go 语言中直接调用 JavaScript 函数,包括对 DOM 树的操作,能用更高的性能提供更好的用户体验。

非对称加密

生成公钥和私钥, 我们这里基于openssl来生成,没有的必要安装下。
我们来生成2048位密钥对, 这个密钥对最多加密245字节文本(2048/8-11=245),
  1. openssl genrsa -out rsa_private_key.pem 2048
  2. openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem​​​​​​​ 
复制代码



  1. go mod init go-wasm-rsa 
  2. GOOS=js GOARCH=wasm vim main.go
复制代码

  1. package main
  2. import (
  3.         "crypto/rand"
  4.         "crypto/rsa"
  5.         "crypto/x509"
  6.         "encoding/base64"
  7.         "encoding/pem"
  8.         "syscall/js"
  9. )
  10. func init() {
  11.         document := js.Global().Get("document")
  12.     // 仅支持web
  13.         if document.IsUndefined() {
  14.                 panic("forbid")
  15.         }
  16.     // 我们可以在这里做一些有意思的事,例如做域名检查等
  17.         allowedDomain := "www.mydoman.com"
  18.         host := document.Get("location").Get("host").String()
  19.    
  20.     if host != allowedDomain {
  21.         panic("forbid")
  22.     }
  23. }
  24. func main() {
  25.         c := make(chan struct{}, 0)
  26.         js.Global().Set("enc", js.FuncOf(enc))
  27.         <-c
  28. }
  29. //现实中公钥需要做一个切割,用来隐藏。这里没做处理
  30. const pubKey = `-----BEGIN PUBLIC KEY-----
  31. MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1MDDhgSkSYKWBXlghk/P
  32. NlAXxxL66zIOTdLnnyDlDcNGFMXVyjXdRPg86rr/MMhxBuzc/amGD4zEzF0xgLxA
  33. 2OrqCfHaVmr9M3Lailu1lE9piafUqfoPc65wzo+DdJmM/7xFX1FpaSqaYGsW8pyI
  34. GDF1VGrnckVu7T/t+XEP7KBotEPAj6QCYy1DJRCq0KaThLnpgYv7Qshck/4qgVi7
  35. Itr1KgqrRujSgdCWJy3IV5dU+ryiDr+tzPb47F7lb9cZo7uari9RfJvNwYFIYO75
  36. AmvB90EUe43r2VysWVTPe2diOHNZXMIjGYi48M0HW31rWru7pFy5KBh7UmcZHZ5a
  37. CQIDAQAB
  38. -----END PUBLIC KEY-----`
  39. func enc(this js.Value, args []js.Value) interface{} {
  40.         authorization := []byte(args[0].String())
  41.         block, _ := pem.Decode([]byte(pubKey))
  42.         publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
  43.         if err != nil {
  44.                 panic("forbid")
  45.         }
  46.         //类型断言
  47.         publicKey := publicKeyInterface.(*rsa.PublicKey)
  48.         //对明文进行加密
  49.         cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, authorization)
  50.         if err != nil {
  51.                 panic("forbid")
  52.         }
  53.         return base64.StdEncoding.EncodeToString(cipherText)
  54. }
复制代码
Makefile
  1. LDFLAGS='-w -s'
  2. build:
  3.         CGO_ENABLED=0 GOOS=js GOARCH=wasm go build -trimpath -ldflags $(LDFLAGS) -o enc.wasm
复制代码

拷贝胶水代码: wasm和js的桥梁, JavaScript 支持文件,加载 wasm 文件时必要,肯定要保证编译的wasm和js文件是用的同一个版本golang。 

  1. make build
  2. cp $(go env GOROOT)/misc/wasm/wasm_exec.js .
复制代码
构建后wasm文件差不多400k, nginx必要配置gzip压缩哦,差不多100k了,很满足哦 
  1. ├── Makefile
  2. ├── go.mod
  3. ├── go.sum
  4. ├── index.html
  5. ├── main.go
  6. └── wasm_exec.js
复制代码
做一个index.html 在浏览器里测试下效果:
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.         <meta charset="UTF-8">
  5.         <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6.         <title>Go + WebAssembly</title>
  7.         <script src="wasm_exec.js"></script>
  8. </head>
  9. <body>
  10.         <h2>待加密字符串 </h2>
  11.         <input id="preprocess" type="text" size="200"
  12.                 value="L1co6ahggacqa$ozhbkhOXI1N5sC/l5KqSZIJu6IBoTyK0R30HWtcVPqlCy8Nm6VZK2kdwMc0eO/yIC6T2lNEX2MHbkI5MMqN6luKeIhNpBvg9EfdsqPkQZjeezJIc/7W/oakpIvtTfA1EsNBiz2Iq5t91o=" />
  13.         <br>
  14.         <br>
  15.        
  16.         <input type="button" id="btn-input" value="加密Encrypt" size="100">
  17.         <br>
  18.         <br>
  19.         <h2>加密结果</h2>
  20.         <textarea cols="80" rows="10" id="result"> </textarea>
  21.         <script>
  22.                 (async function loadAndRunGoWasm() {
  23.                         const go = new Go();
  24.                         const response = await fetch("enc.wasm");
  25.                         const buffer = await response.arrayBuffer();
  26.                         const result = await WebAssembly.instantiate(buffer, go.importObject);
  27.                         go.run(result.instance)
  28.                 })()
  29.                 const btnInput = document.getElementById('btn-input')
  30.                 btnInput.addEventListener('click', async () => {
  31.                         const bearer = document.getElementById("preprocess").value
  32.                         const s = enc(bearer)
  33.                         document.getElementById("result").value = s
  34.                 })
  35.         </script>
  36. </body>
  37. </html>
复制代码
  1. go get -u github.com/shurcooL/goexec
  2. goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'
复制代码




OpenResty  

编写Lua解密代码(这里之以是用lua是历史遗留问题,不让后端过多参与,对后端完全透明)
下载:lua-resty-rsa/lib/resty at master · spacewander/lua-resty-rsa · GitHub
编写dec.lua, 配置在access_by_lua_file 里

  1. local require = require
  2. local resty_rsa = require "resty.rsa"
  3. local base64_decode=ngx.decode_base64
  4. local authorization=ngx.req.get_headers()['authorization']
  5. if authorization==nil then
  6.     return
  7. end
  8. local rsa_priv_key="LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBc1Z2ZzFoTlFsdncwNTBBQWRwVUJycWJ6TkJGaHl0ZXVoWm9QTVpsZlh6a0Npbm1QCjUwb2tuVzlYU052czRYdGpxR0NsVXhJckozQmFhREZoTzJTN2tuK1N6Wm1iNXVRamxQSGhjNEJwbFRmMGVsUE8KTGRwdUxKcnZ4enVSRlJualU3ZWVvZmVjNDJHVlBad0QydDI5bU1KbkpENzZRMlBqMmxnSldBMHZHM2tkYTdwUwpXbnFDdVVYR2RadVF1VHRneG5ERXVvL1JicDV1MjRwSE82RUg1NUJQRElHY0ZJQUJkZlYyNjBWZlo4aDg2WDhQCjhLbkRlSTZVYStxMmFPaUdhaHI5b25kTHVhL1NuS1BUSVNDZjZCVzdueTRsL1Y0c1JIMm1xWXYySWNkMVF0c1YKNlJqaEh5Tm4yVzkyTlgzT0lsRHo1Tnl0VXU1THlibkhYVHhZa1FJREFRQUJBb0lCQUg3SmthYzMwNHE3N1EzTApnUWxFYUJsMG03T0RJWWVpTzg2aVhXNDFtQ280VlFxczhDU0ZxanNwbHhvc3JlQmJGdGtOamVJZXdON0d3THB2ClluVFZCQW9zVE1QUnBkT2ZENWl3ZVZ6YVZhQW9pZ3JRMGptUlJ1VjROU1VWL2hjNWxIc0tic3FXZW45S0NTZ3IKMmMyaWFxRkRoL3d2VVRUUHVka2l5anM1NFkwZkRUUGYwSEQ3MEQ0ZTB6L2NOb2dKa3RqZnZwb1dMWWlId0lnbAplcjRtUFUzQ0hVY0RtcEJHNWErN3VyVnJHOEVYMmJ1U1R6SnVabUF6d3NhSFVDUHorYU1TSHp2bTlPR3gyQlpwCjJVRDd4WGlwRmpQUHMrazQ3TlNUaHRPemxzUUNLcnBkZ1Awb3V0UC9oTDgvaktVZ0pLYWZTay9pamc3cVhOekwKTW8yakdra0NnWUVBMVQ3djhFeEFCWjNmWGZ1dUQ1Y1orMFJNTlpoMFBFVkR1NGI3V2RNV0ZNaWZIWHUxTW9zZwoybS90NEdEQWl0eUZ3Yk9xRHB2eUttMDNsTGJaZ2xLL3ZHVGVleTBFRkxmUXJPWmladk5sKzl0dEh4TStEbzdZCmhGdmdVVDNIZVBJV1ZZRytYTmdFbkNQVTVGZ1U3K3AwYWR5cjg3c1p5VkxzR0h1WWp2TjV1SjhDZ1lFQTFPc0IKb3NKRFd4NnBiTkp6bFlDdTU1N2UzdmVCRStKdXFUdWhYcXhlM2Z3MElzdUl5ZjMrRzR1UmNBdHVzWkphL08vNgpGTldRKzZqbExGaysyUjhzdWNPWjVGV1NFcG5XNENsZG9YNkVrR3RsQy8yU3A0ZWhscjRpTWRUQlhHUmZnWW9qCi9tL3JEdVIvUFdKRmQwamJqRUZXeW8xc3AzaFJidWNqcVRabDhNOENnWUFGN29WQUd1N2crUjVBZ0FLOGZraUQKdThlZTZnbTVyM2VOM05oYkRFc2Q4dUt5TUVHL0VTMnR4ZFZKRzRmZmxQakhoWmJpWnlZYVZnVm94cGxRVGJyMQpvNXlvc256ZGtxdGtVOWhDNHR4Z1lCOHQ4UndWelpWcVFTQUJRb1dzOEpiOGMrcDJySytjSkVjRXZ3cCtEZmlGCkJWVm5Kem8xWm5BWTBqOVJJcWF0SXdLQmdRQ05BVjNCOWprNVBTTWpDSFMzaTlOSlhYTm40aTIvaDNPVjdBSEEKZXhNUW5CZkMrMXdKdVlYeHBBcWJVMWJwam0xbm1WM2JNbHlqN1lSb1RHcE16RktJYTd1YzlmYVpEdnk0MDJ4SQpxVXNOZ2JJWHNNVFE0Z2ZubHQ5NmROWGhaQy9EMEVKcUhLQms2bnBCb3JVeWZETzV2UVBIZk1WNld6cEM0aHhCCjBkN05EUUtCZ1FDN3kzM0RCTkRteHZ5eEJUdksrWE5BaHVwSU5MU296STMrbUthaG1IS1QrdVplK3hZK21hdUcKb0hUMUZYTFdrTWd5eGF4SEE0SkQzTk95cDJBYTl0YVMvQjM2dVN4RjJoRFJNUmpNZnkvQVVRSG4yNUVncjdBZgpISW9iTnQyc3VVY2QrVjNtSHRIZ1ZwWlR5T1gzQzF6ZlgweTlIKzZ3TUVIbitTZ1lQOC9wTFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQ=="
  9. local encrypted=base64_decode(authorization)
  10. if not encrypted then
  11.     ngx.log(ngx.ERR, "careful dec not work")
  12.     ngx.status = ngx.HTTP_FORBIDDEN
  13.     ngx.exit(ngx.HTTP_OK)
  14. end
  15. local priv, err = resty_rsa:new({ private_key = base64_decode(rsa_priv_key) })
  16. if not priv then
  17.    ngx.status = ngx.HTTP_UNAUTHORIZED
  18.    ngx.exit(ngx.HTTP_OK)
  19.    return
  20. end
  21. local decrypted = priv:decrypt(encrypted)
  22. ngx.req.set_header("authorization", decrypted)
复制代码
Nginx  压缩wasm

mime.types
  1.         application/wasm                 wasm;
复制代码
 nginx.conf
  1. gzip_types ...省略其他.. application/wasm;
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

兜兜零元

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表