前言
本文通过探究一种前端加解密方式, 前端使用WebAssembly(wasm)提供公钥加密,后端使用私钥解密,能大大提高破解难度。 wasm使用支持多种语言,我们这里使用golang开辟,后端解密使用openresty + lua解密,如许后端应用就不必要做任何修改了。
WebAssembly简介
WebAssembly字面意思是web的汇编语言, 是⼀种新兴的⽹⻚虚拟机标准。现在已经作为W3C规范在各大主流浏览器得到支持。
Go 语言内置了 syscall/js 包,可以在 Go 语言中直接调用 JavaScript 函数,包括对 DOM 树的操作,能用更高的性能提供更好的用户体验。
非对称加密
生成公钥和私钥, 我们这里基于openssl来生成,没有的必要安装下。
我们来生成2048位密钥对, 这个密钥对最多加密245字节文本(2048/8-11=245),
- openssl genrsa -out rsa_private_key.pem 2048
- openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
复制代码
- go mod init go-wasm-rsa
- GOOS=js GOARCH=wasm vim main.go
复制代码
- package main
- import (
- "crypto/rand"
- "crypto/rsa"
- "crypto/x509"
- "encoding/base64"
- "encoding/pem"
- "syscall/js"
- )
- func init() {
- document := js.Global().Get("document")
- // 仅支持web
- if document.IsUndefined() {
- panic("forbid")
- }
- // 我们可以在这里做一些有意思的事,例如做域名检查等
- allowedDomain := "www.mydoman.com"
- host := document.Get("location").Get("host").String()
-
- if host != allowedDomain {
- panic("forbid")
- }
- }
- func main() {
- c := make(chan struct{}, 0)
- js.Global().Set("enc", js.FuncOf(enc))
- <-c
- }
- //现实中公钥需要做一个切割,用来隐藏。这里没做处理
- const pubKey = `-----BEGIN PUBLIC KEY-----
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1MDDhgSkSYKWBXlghk/P
- NlAXxxL66zIOTdLnnyDlDcNGFMXVyjXdRPg86rr/MMhxBuzc/amGD4zEzF0xgLxA
- 2OrqCfHaVmr9M3Lailu1lE9piafUqfoPc65wzo+DdJmM/7xFX1FpaSqaYGsW8pyI
- GDF1VGrnckVu7T/t+XEP7KBotEPAj6QCYy1DJRCq0KaThLnpgYv7Qshck/4qgVi7
- Itr1KgqrRujSgdCWJy3IV5dU+ryiDr+tzPb47F7lb9cZo7uari9RfJvNwYFIYO75
- AmvB90EUe43r2VysWVTPe2diOHNZXMIjGYi48M0HW31rWru7pFy5KBh7UmcZHZ5a
- CQIDAQAB
- -----END PUBLIC KEY-----`
- func enc(this js.Value, args []js.Value) interface{} {
- authorization := []byte(args[0].String())
- block, _ := pem.Decode([]byte(pubKey))
- publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
- if err != nil {
- panic("forbid")
- }
- //类型断言
- publicKey := publicKeyInterface.(*rsa.PublicKey)
- //对明文进行加密
- cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, authorization)
- if err != nil {
- panic("forbid")
- }
- return base64.StdEncoding.EncodeToString(cipherText)
- }
复制代码 Makefile
- LDFLAGS='-w -s'
- build:
- CGO_ENABLED=0 GOOS=js GOARCH=wasm go build -trimpath -ldflags $(LDFLAGS) -o enc.wasm
复制代码
拷贝胶水代码: wasm和js的桥梁, JavaScript 支持文件,加载 wasm 文件时必要,肯定要保证编译的wasm和js文件是用的同一个版本golang。
- make build
- cp $(go env GOROOT)/misc/wasm/wasm_exec.js .
复制代码 构建后wasm文件差不多400k, nginx必要配置gzip压缩哦,差不多100k了,很满足哦
- ├── Makefile
- ├── go.mod
- ├── go.sum
- ├── index.html
- ├── main.go
- └── wasm_exec.js
复制代码 做一个index.html 在浏览器里测试下效果:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Go + WebAssembly</title>
- <script src="wasm_exec.js"></script>
- </head>
- <body>
- <h2>待加密字符串 </h2>
- <input id="preprocess" type="text" size="200"
- value="L1co6ahggacqa$ozhbkhOXI1N5sC/l5KqSZIJu6IBoTyK0R30HWtcVPqlCy8Nm6VZK2kdwMc0eO/yIC6T2lNEX2MHbkI5MMqN6luKeIhNpBvg9EfdsqPkQZjeezJIc/7W/oakpIvtTfA1EsNBiz2Iq5t91o=" />
- <br>
- <br>
-
- <input type="button" id="btn-input" value="加密Encrypt" size="100">
- <br>
- <br>
- <h2>加密结果</h2>
- <textarea cols="80" rows="10" id="result"> </textarea>
- <script>
- (async function loadAndRunGoWasm() {
- const go = new Go();
- const response = await fetch("enc.wasm");
- const buffer = await response.arrayBuffer();
- const result = await WebAssembly.instantiate(buffer, go.importObject);
- go.run(result.instance)
- })()
- const btnInput = document.getElementById('btn-input')
- btnInput.addEventListener('click', async () => {
- const bearer = document.getElementById("preprocess").value
- const s = enc(bearer)
- document.getElementById("result").value = s
- })
- </script>
- </body>
- </html>
复制代码- go get -u github.com/shurcooL/goexec
- 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 里
- local require = require
- local resty_rsa = require "resty.rsa"
- local base64_decode=ngx.decode_base64
- local authorization=ngx.req.get_headers()['authorization']
- if authorization==nil then
- return
- end
- local rsa_priv_key="LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBc1Z2ZzFoTlFsdncwNTBBQWRwVUJycWJ6TkJGaHl0ZXVoWm9QTVpsZlh6a0Npbm1QCjUwb2tuVzlYU052czRYdGpxR0NsVXhJckozQmFhREZoTzJTN2tuK1N6Wm1iNXVRamxQSGhjNEJwbFRmMGVsUE8KTGRwdUxKcnZ4enVSRlJualU3ZWVvZmVjNDJHVlBad0QydDI5bU1KbkpENzZRMlBqMmxnSldBMHZHM2tkYTdwUwpXbnFDdVVYR2RadVF1VHRneG5ERXVvL1JicDV1MjRwSE82RUg1NUJQRElHY0ZJQUJkZlYyNjBWZlo4aDg2WDhQCjhLbkRlSTZVYStxMmFPaUdhaHI5b25kTHVhL1NuS1BUSVNDZjZCVzdueTRsL1Y0c1JIMm1xWXYySWNkMVF0c1YKNlJqaEh5Tm4yVzkyTlgzT0lsRHo1Tnl0VXU1THlibkhYVHhZa1FJREFRQUJBb0lCQUg3SmthYzMwNHE3N1EzTApnUWxFYUJsMG03T0RJWWVpTzg2aVhXNDFtQ280VlFxczhDU0ZxanNwbHhvc3JlQmJGdGtOamVJZXdON0d3THB2ClluVFZCQW9zVE1QUnBkT2ZENWl3ZVZ6YVZhQW9pZ3JRMGptUlJ1VjROU1VWL2hjNWxIc0tic3FXZW45S0NTZ3IKMmMyaWFxRkRoL3d2VVRUUHVka2l5anM1NFkwZkRUUGYwSEQ3MEQ0ZTB6L2NOb2dKa3RqZnZwb1dMWWlId0lnbAplcjRtUFUzQ0hVY0RtcEJHNWErN3VyVnJHOEVYMmJ1U1R6SnVabUF6d3NhSFVDUHorYU1TSHp2bTlPR3gyQlpwCjJVRDd4WGlwRmpQUHMrazQ3TlNUaHRPemxzUUNLcnBkZ1Awb3V0UC9oTDgvaktVZ0pLYWZTay9pamc3cVhOekwKTW8yakdra0NnWUVBMVQ3djhFeEFCWjNmWGZ1dUQ1Y1orMFJNTlpoMFBFVkR1NGI3V2RNV0ZNaWZIWHUxTW9zZwoybS90NEdEQWl0eUZ3Yk9xRHB2eUttMDNsTGJaZ2xLL3ZHVGVleTBFRkxmUXJPWmladk5sKzl0dEh4TStEbzdZCmhGdmdVVDNIZVBJV1ZZRytYTmdFbkNQVTVGZ1U3K3AwYWR5cjg3c1p5VkxzR0h1WWp2TjV1SjhDZ1lFQTFPc0IKb3NKRFd4NnBiTkp6bFlDdTU1N2UzdmVCRStKdXFUdWhYcXhlM2Z3MElzdUl5ZjMrRzR1UmNBdHVzWkphL08vNgpGTldRKzZqbExGaysyUjhzdWNPWjVGV1NFcG5XNENsZG9YNkVrR3RsQy8yU3A0ZWhscjRpTWRUQlhHUmZnWW9qCi9tL3JEdVIvUFdKRmQwamJqRUZXeW8xc3AzaFJidWNqcVRabDhNOENnWUFGN29WQUd1N2crUjVBZ0FLOGZraUQKdThlZTZnbTVyM2VOM05oYkRFc2Q4dUt5TUVHL0VTMnR4ZFZKRzRmZmxQakhoWmJpWnlZYVZnVm94cGxRVGJyMQpvNXlvc256ZGtxdGtVOWhDNHR4Z1lCOHQ4UndWelpWcVFTQUJRb1dzOEpiOGMrcDJySytjSkVjRXZ3cCtEZmlGCkJWVm5Kem8xWm5BWTBqOVJJcWF0SXdLQmdRQ05BVjNCOWprNVBTTWpDSFMzaTlOSlhYTm40aTIvaDNPVjdBSEEKZXhNUW5CZkMrMXdKdVlYeHBBcWJVMWJwam0xbm1WM2JNbHlqN1lSb1RHcE16RktJYTd1YzlmYVpEdnk0MDJ4SQpxVXNOZ2JJWHNNVFE0Z2ZubHQ5NmROWGhaQy9EMEVKcUhLQms2bnBCb3JVeWZETzV2UVBIZk1WNld6cEM0aHhCCjBkN05EUUtCZ1FDN3kzM0RCTkRteHZ5eEJUdksrWE5BaHVwSU5MU296STMrbUthaG1IS1QrdVplK3hZK21hdUcKb0hUMUZYTFdrTWd5eGF4SEE0SkQzTk95cDJBYTl0YVMvQjM2dVN4RjJoRFJNUmpNZnkvQVVRSG4yNUVncjdBZgpISW9iTnQyc3VVY2QrVjNtSHRIZ1ZwWlR5T1gzQzF6ZlgweTlIKzZ3TUVIbitTZ1lQOC9wTFE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQ=="
- local encrypted=base64_decode(authorization)
- if not encrypted then
- ngx.log(ngx.ERR, "careful dec not work")
- ngx.status = ngx.HTTP_FORBIDDEN
- ngx.exit(ngx.HTTP_OK)
- end
- local priv, err = resty_rsa:new({ private_key = base64_decode(rsa_priv_key) })
- if not priv then
- ngx.status = ngx.HTTP_UNAUTHORIZED
- ngx.exit(ngx.HTTP_OK)
- return
- end
- local decrypted = priv:decrypt(encrypted)
- ngx.req.set_header("authorization", decrypted)
复制代码 Nginx 压缩wasm
mime.types
nginx.conf
- gzip_types ...省略其他.. application/wasm;
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |