服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - 编程技术 - 使用WebAssembly对前端API请求进行签名

使用WebAssembly对前端API请求进行签名

2021-12-27 23:33RolloTomasi 编程技术

本文主要为大家讲解使用WebAssembly对前端API请求进行签名的方法,有需要的朋友可以参考下

使用WebAssembly对前端API请求进行签名

背景

server端处理WebAPI请求的安全问题:

  1. 请求重放 (eg. 月饼抢购场景中,程序员通过脚本直接访问接口)
  2. 参数篡改 (eg. 会话劫持场景中,将应该抢购到的月饼归属人改为自己)
  3. 脚本攻击 (eg. 综合前两种场景,使用技术手段构建的请求进行攻击,如信息窃取,漏洞攻击)
  4. 可信客户端请求 (eg. 以上所有场景根因均为访问客户端不可信并不可证伪)

解决方案

  1. 对请求参数+cnonce (客户端生成的一次性随机字符串) 进行hash签名
  2. 以secret作为盐值
  3. 将签名作为header值传递给server端
  4. server端在redis中查验是否已有重复签名,如有重复直接拒绝请求(防止请求重放)
  5. server端对签名值进行校验
  6. 校验通过之后将该签名值作为key值,存入redis

总体流程如下图所示:

使用WebAssembly对前端API请求进行签名

代码示例

前端使用示例(TypeScript Vue3 版本):

  1. <script setup> 
  2. import { onMounted } from "vue"
  3. import initWasm, {sign} from "./pkg/sign.js"; // 通过wasm-pack打包生成的二进制包的入口文件 
  4. import { v4 as uuidv4 } from 'uuid'; // 此示例以生成的UUID作为cnonce随机字符串 
  5.   
  6. onMounted(async () => { 
  7.     await initWasm() 
  8. }) 
  9.   
  10. const sendRequest = () => { 
  11.     const cnonce = uuidv4() 
  12.     const params: EncryptedParams = { 
  13.         name'John'
  14.         age: 23, 
  15.         breed: 'dog'
  16.         ts: Date.now() 
  17.     } 
  18.     const wasmSignature = sign(JSON.stringify(params), cnonce); 
  19.     ... 
  20.     axios.post(something); 
  21.   
  22. </script> 

签名机制示例,server端接受到请求时,应该同时获得签名值以及cnonce一次性字符串,按照下面同样的签名顺序进行签名,比对前端传入的签名以及server端生成的签名进行校验:

  1. const encryptedSign = (message: string, cnonce: string): string => { 
  2.   const secret = 'XXXXXXX' // 该签名盐值可以自行生成,生成之后需要重新编译rust应用,生成新的wasm包 
  3.   const hashDigest = sha256(`${cnonce}|${message}`) 
  4.   const hmacDigest = Base64.stringify(hmacSHA512(hashDigest.toString().toUpperCase(), secret)) 
  5.   return hmacDigest.toString().toUpperCase() 

签名机制示例 (rust 版本):

  1. extern crate wasm_bindgen; 
  2.   
  3. use ring::hmac; 
  4. use ring::digest::{Context, SHA256}; 
  5. use data_encoding::BASE64; 
  6. use data_encoding::HEXUPPER; 
  7. use wasm_bindgen::prelude::*; 
  8.   
  9. #[wasm_bindgen] 
  10. pub fn ron_weasley_sign (message: &str, cnonce: &str) -> String { 
  11.     const SECRET: &str = std::env!("SECRET"); 
  12.   
  13.     let mut context = Context::new(&SHA256); 
  14.     context.update(format!("{}|{}", cnonce, message).as_bytes()); 
  15.     let sha256_result = context.finish(); 
  16.     let sha256_result_str = format!("{}", HEXUPPER.encode(sha256_result.as_ref())); 
  17.   
  18.     let key = hmac::Key::new(hmac::HMAC_SHA512, SECRET.as_bytes()); 
  19.     let mac = hmac::sign(&key, sha256_result_str.as_bytes()); 
  20.     let b64_encoded_sig = BASE64.encode(mac.as_ref()); 
  21.     return b64_encoded_sig.to_uppercase(); 

构建rust源代码,并生成对应的二进制包

首先在项目的github地址

https://github.com/swearer23/ron-weasley 下载源代码

之后按照README文件的步骤安装编译环境(以*nix环境为例)

安装cargo

由于我们使用cargo作为rust环境的管理器,所以第一步安装cargo

安装完成后在命令行输入cargo -v 查看是否安装成功

  1. cargo -v # 可能需要重新启动终端 
  2. Rust's package manager 
  3.  
  4. USAGE: 
  5.     cargo [+toolchain] [OPTIONS] [SUBCOMMAND] 
  6.  
  7. OPTIONS: 
  8.     -V, --version                  Print version info and exit 
  9.         --list                     List installed commands 
  10.         --explain <CODE>           Run `rustc --explain CODE` 
  11.     -v, --verbose                  Use verbose output (-vv very verbose/build.rs output) 
  12.     -q, --quiet                    No output printed to stdout 
  13.         --color <WHEN>             Coloring: auto, always, never 
  14.         --frozen                   Require Cargo.lock and cache are up to date 
  15.         --locked                   Require Cargo.lock is up to date 
  16.         --offline                  Run without accessing the network 
  17.         --config <KEY=VALUE>...    Override a configuration value (unstable) 
  18.     -Z <FLAG>...                   Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details 
  19.     -h, --help                     Prints help information 
  20.  
  21. Some common cargo commands are (see all commands with --list): 
  22.     build, b    Compile the current package 
  23.     check, c    Analyze the current package and report errors, but don't build object files 
  24.     clean       Remove the target directory 
  25.     doc, d      Build this package's and its dependencies' documentation 
  26.     new         Create a new cargo package 
  27.     init        Create a new cargo package in an existing directory 
  28.     run, r      Run a binary or example of the local package 
  29.     test, t     Run the tests 
  30.     bench       Run the benchmarks 
  31.     update      Update dependencies listed in Cargo.lock 
  32.     search      Search registry for crates 
  33.     publish     Package and upload this package to the registry 
  34.     install     Install a Rust binaryDefault location is $HOME/.cargo/bin 
  35.     uninstall   Uninstall a Rust binary 
  36.  
  37. See 'cargo help <command>' for more information on a specific command. 

安装wasm-pack

 

要构建二进制包,需要一个额外工具 wasm-pack。它会帮助我们把代码编译成 WebAssembly 并构建出适用于web环境的wasm包。使用下面的命令可以下载并安装:

 

  1. cargo install wasm-pack 

编译wasm

wasm-pack安装成功后,执行下面的命令以编译wasm包

  1. SECRET= wasm-pack build --target=web --release 

<your-secret>替换为你的签名盐值

第一次构建和编译时间会比较长,需要下载依赖的rust库并编译,请耐心等待

如果速度仍然很慢,建议更换cargo国内源

更换 cargo 源

在你的cargo文件夹下新建 config 文件

macos中,文件夹地址在 ~/.cargo

  1. cd ~/.cargo 
  2. touch config 

然后编辑config文件,添加如下内容:

  1. [source.crates-io] 
  2. registry = "https://github.com/rust-lang/crates.io-index" 
  3. replace-with = 'ustc' 
  4. [source.ustc] 
  5. registry = "git://mirrors.ustc.edu.cn/crates.io-index" 

即可更换为ustc的源

集成

执行wasm-pack命令打包会得到一个名为pkg的文件夹,位于项目的根目录下

将其放入要使用的前端项目中,即可以像上面代码示例章节所描述的方式进行集成和调用

附录

  • rust 项目地址:https://github.com/swearer23/ron-weasley
  • vue3 调用方式示例项目地址:https://github.com/swearer23/harry-porter (内含有secret=123456的wasm包,可以用于示例)

原文地址:https://www.toutiao.com/a7045923113728573982/

延伸 · 阅读

精彩推荐