API接口设计:防参数篡改+防二次请求(防重放)

本文详细介绍了API接口设计中的安全机制,包括防参数篡改与防重放攻击的策略。阐述了HTTPS的重要性,以及如何通过签名、nonce和timestamp组合使用来确保请求的唯一性和安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

API接口由于需要供第三方服务调用,所以必须暴露到外网,并提供了具体请求地址和请求参数

为了防止被第别有用心之人获取到真实请求参数后再次发起请求获取信息,需要采取很多安全机制

 

1.首先: 需要采用https方式对第三方提供接口,数据的加密传输会更安全,即便是被破解,也需要耗费更多时间

2.其次:需要有安全的后台验证机制【本文重点】,达到防参数篡改+防二次请求

 

主要防御措施可以归纳为两点:

  • 对请求的合法性进行校验
  • 对请求的数据进行校验

 

防止重放攻击必须要保证请求仅一次有效
需要通过在请求体中携带当前请求的唯一标识,并且进行签名防止被篡改。
所以防止重放攻击需要建立在防止签名被串改的基础之上。

请求参数防篡改

采用https协议可以将传输的明文进行加密,但是黑客仍然可以截获传输的数据包,进一步伪造请求进行重放攻击。如果黑客使用特殊手段让请求方设备使用了伪造的证书进行通信,那么https加密的内容也将会被解密。
在API接口中我们除了使用https协议进行通信外,还需要有自己的一套加解密机制,对请求参数进行保护,防止被篡改。
过程如下:

  1. 客户端使用约定好的秘钥对传输参数进行加密,得到签名值signature,并且将签名值也放入请求参数中,发送请求给服务端
  2. 服务端接收客户端的请求,然后使用约定好的秘钥对请求的参数(除了signature以外)再次进行签名,得到签名值autograph。
  3. 服务端对比signature和autograph的值,如果对比一致,认定为合法请求。如果对比不一致,说明参数被篡改,认定为非法请求。

因为黑客不知道签名的秘钥,所以即使截取到请求数据,对请求参数进行篡改,但是却无法对参数进行签名,无法得到修改后参数的签名值signature。
签名的秘钥我们可以使用很多方案,可以采用对称加密或者非对称加密。

( 古月: 关于怎么签名可以参考阿里的文档

请求签名说明文档_用户指南(调用 API)_API 网关-阿里云
https://help.aliyun.com/document_detail/29475.html?spm=a2c4g.11186623.2.3.nRdYli

接口鉴权 - 标签 - 文档平台 - 腾讯云
https://cloud.tencent.com/document/product/651/18355   )

 

防止重放攻击

基于timestamp的方案

每次HTTP请求,都需要加上timestamp参数,然后把timestamp和其他参数一起进行数字签名。因为一次正常的HTTP请求,从发出到达服务器一般都不会超过60s,所以服务器收到HTTP请求之后,首先判断时间戳参数与当前时间相比较,是否超过了60s,如果超过了则认为是非法的请求。

一般情况下,黑客从抓包重放请求耗时远远超过了60s,所以此时请求中的timestamp参数已经失效了。
如果黑客修改timestamp参数为当前的时间戳,则signature参数对应的数字签名就会失效,因为黑客不知道签名秘钥,没有办法生成新的数字签名。

但这种方式的漏洞也是显而易见的,如果在60s之后进行重放攻击,那就没办法了,所以这种方式不能保证请求仅一次有效。

基于nonce的方案

nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不同,所以该参数一般与时间戳有关,我们这里为了方便起见,直接使用时间戳的16进制,实际使用时可以加上客户端的ip地址,mac地址等信息做个哈希之后,作为nonce参数。
我们将每次请求的nonce参数存储到一个“集合”中,可以json格式存储到数据库或缓存中。
每次处理HTTP请求时,首先判断该请求的nonce参数是否在该“集合”中,如果存在则认为是非法请求。

nonce参数在首次请求时,已经被存储到了服务器上的“集合”中,再次发送请求会被识别并拒绝。
nonce参数作为数字签名的一部分,是无法篡改的,因为黑客不清楚token,所以不能生成新的sign。

这种方式也有很大的问题,那就是存储nonce参数的“集合”会越来越大,验证nonce是否存在“集合”中的耗时会越来越长。我们不能让nonce“集合”无限大,所以需要定期清理该“集合”,但是一旦该“集合”被清理,我们就无法验证被清理了的nonce参数了。也就是说,假设该“集合”平均1天清理一次的话,我们抓取到的该url,虽然当时无法进行重放攻击,但是我们还是可以每隔一天进行一次重放攻击的。而且存储24小时内,所有请求的“nonce”参数,也是一笔不小的开销。

基于timestamp和nonce的方案

nonce的一次性可以解决timestamp参数60s的问题,timestamp可以解决nonce参数“集合”越来越大的问题。
防止重放攻击一般和防止请求参数被串改一起做,请求的Headers数据如下图所示。
 

我们在timestamp方案的基础上,加上nonce参数,因为timstamp参数对于超过60s的请求,都认为非法请求,所以我们只需要存储60s的nonce参数的“集合”即可。
 

最终实现代码(网关层验证):

复制代码


 
  1. 1 public class APIAuth extends ZuulFilter {
  2. 2 @Override
  3. 3 public String filterType() {
  4. 4 return "pre";
  5. 5 }
  6. 6
  7. 7 @Override
  8. 8 public int filterOrder() {
  9. 9 return 0;
  10. 10 }
  11. 11
  12. 12 @Override
  13. 13 public boolean shouldFilter() {
  14. 14 return true;
  15. 15 }
  16. 16
  17. 17 @Override
  18. 18 public Object run() {
  19. 19 RequestContext ctx = RequestContext.getCurrentContext();
  20. 20 HttpServletRequest request = ctx.getRequest();
  21. 21
  22. 22 String token = request.getHeader("token");
  23. 23 String timestamp = request.getHeader("timestamp");
  24. 24 String nonce = request.getHeader("nonce");
  25. 25 String sign = request.getHeader("sign");
  26. 26
  27. 27 //时间限制配置
  28. 28 int timeLimit = 60;
  29. 29
  30. 30 ctx.setSendZuulResponse(false);
  31. 31
  32. 32 //请求头参数非空验证
  33. 33 if (StringUtils.isEmpty(token) || StringUtils.isEmpty(timestamp) || StringUtils.isEmpty(nonce) || StringUtils.isEmpty(sign)) {
  34. 34 ctx.setResponseBody(JSON.toJSONString(new Result("-1", "请求头参数不正确")));
  35. 35 return null;
  36. 36 }
  37. 37
  38. 38 //请求时间和现在时间对比验证,发起请求时间和服务器时间不能超过timeLimit秒
  39. 39 if (StringUtils.timeDiffSeconds(new Date(), timestamp) > timeLimit) {
  40. 40 ctx.setResponseBody(JSON.toJSONString(new Result("-1", "请求发起时间超过服务器限制")));
  41. 41 return null;
  42. 42 }
  43. 43
  44. 44 //验证用户信息
  45. 45 UserInfo userInfo = UserInfoUtil.getInfoByToken(token);
  46. 46 if (userInfo == null) {
  47. 47 ctx.setResponseBody(JSON.toJSONString(new Result("-1", "错误的token信息")));
  48. 48 return null;
  49. 49 }
  50. 50
  51. 51 //验证相同noce的请求是否已经存在,存在表示为重复请求
  52. 52 if (NoceUtil.exsit(userInfo, nonce)) {
  53. 53 ctx.setResponseBody(JSON.toJSONString(new Result("-1", "重复的请求")));
  54. 54 return null;
  55. 55 } else {
  56. 56 //如果noce没有在缓存中,则需要加入,并设置过期时间为timeLimit秒
  57. 57 NoceUtil.addNoce(userInfo, nonce, timeLimit);
  58. 58 }
  59. 59
  60. 60 //服务器生成签名与header中签名对比
  61. 61 String serverSign = SignUtil.getSign(userinfo, token, timestamp, nonce, request);
  62. 62 if (!serverSign.equals(sign)) {
  63. 63 ctx.setResponseBody(JSON.toJSONString(new Result("-1", "错误的签名信息")));
  64. 64 return null;
  65. 65 }
  66. 66
  67. 67 ctx.setSendZuulResponse(true);
  68. 68 return null;
  69. 69 }
  70. 70 }
<think>好的,用户现在问的是前端传输加密加上对敏感字段的AES二次加密。之前我们讨论了Spring Boot项目如何集成腾讯云SDK实现短信登录,并提到了敏感数据加密,比如AES。现在用户可能想进一步了解如何在前端进行加密,特别是结合传输层加密二次加密。 首先,我需要确定用户的使用场景。可能是在注册或登录过程中,用户需要提交敏感信息如手机号、身份证号等,用户希望这些数据在传输过程中即使被截获也难以解密。他们可能担心即使使用了HTTPS,仍然希望额外加密敏感字段,以增加安全性。 用户的真实需求可能包括:确保数据在传输过程中双重加密防止中间人攻击,以及存储时的数据安全。深层需求可能涉及合规要求,比如某些行业标准要求对特定数据进行加密处理,或者用户对数据隐私有特别高的要求。 接下来,我需要整理前端加密的常用方法。HTTPS是基础,必须的。然后在前端使用AES加密敏感字段,这样即使HTTPS被破解(虽然可能性低),数据仍然被加密。需要注意的是,AES的密钥管理是关键,如何安全地传递密钥给前端,或者使用非对称加密来传递密钥。 可能的挑战包括:前端加密的性能影响,密钥的安全存储和传输,以及后端解密处理的正确性。同时,需要考虑如何在不影响用户体验的情况下实现这些加密步骤。 然后,要分步骤介绍实现方式。首先强调必须使用HTTPS,然后生成AES密钥,可能通过后端接口动态获取,或者使用RSA加密AES密钥。前端使用CryptoJS或Web Cryptography API进行加密,后端解密并处理数据。同时,二次加密可能指的是对某些字段在存储前的再次加密,比如数据库中的手机号用AES加密存储。 还需要提到安全注意事项,比如避免硬编码密钥,使用安全的密钥交换机制,定期更换密钥,以及防止重放攻击。此外,日志中不应记录敏感数据,加密后的字段在传输和存储中的处理方式。 最后,可能需要给出代码示例,比如前端如何加密,后端如何解密,以及存储时的二次加密。确保用户能够按照步骤实施,同时理解每一步的安全考量。</think>在前后端交互中,通过**前端传输加密 + 敏感字段二次加密**的组合方案,可以有效提升数据安全性。以下是具体实现方案和技术细节: --- ### 一、整体加密流程 ```mermaid sequenceDiagram participant 前端 participant 后端 前端->>后端: HTTPS传输(TLS加密) Note over 前端: 对敏感字段(如手机号/身份证)进行AES加密 后端->>后端: 解密AES数据后,再对敏感字段二次加密存储 ``` --- ### 二、核心设计原则 1. **分层加密**: - **第一层**:HTTPS(TLS)保证传输通道安全 - **第二层**:敏感字段在前端使用AES加密 2. **密钥管理**: - AES密钥通过安全渠道获取(如登录后动态下发) - 避免硬编码密钥到前端代码 3. **防重放攻击**: - 加密数据加入时间戳/随机数 - 服务端校验数据时效性 --- ### 三、前端加密实现(以Vue+CryptoJS为例) #### 1. 安装CryptoJS ```bash npm install crypto-js ``` #### 2. AES加密工具类 ```javascript import CryptoJS from 'crypto-js' // 加密方法(CBC模式+Base64编码) function encryptData(data, key, iv) { const encrypted = CryptoJS.AES.encrypt( CryptoJS.enc.Utf8.parse(data), CryptoJS.enc.Utf8.parse(key), { iv: CryptoJS.enc.Utf8.parse(iv), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ) return encrypted.toString() } // 示例:加密手机号 const phone = '13800138000' const aesKey = '16/24/32字节密钥' // 从后端接口动态获取 const iv = '16字节初始化向量' const encryptedPhone = encryptData(phone, aesKey, iv) ``` --- ### 四、后端解密与二次加密(Java实现) #### 1. 接收并解密数据 ```java public String decryptAES(String encryptedData, String key, String iv) { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes()); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] decodedBytes = Base64.getDecoder().decode(encryptedData); byte[] decryptedBytes = cipher.doFinal(decodedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); } ``` #### 2. 二次加密存储(使用独立密钥) ```java // 数据库存储加密(密钥与前端不同) public String reEncryptForStorage(String data) { String storageKey = "独立存储密钥"; // ...类似解密流程,使用不同密钥加密 return encryptedData; } ``` --- ### 五、密钥安全方案 1. **动态密钥获取**: ```javascript // 前端登录后获取临时密钥 axios.get('/api/getEncryptKey').then(res => { sessionStorage.setItem('aesKey', res.data.key) sessionStorage.setItem('aesIv', res.data.iv) }) ``` 2. **密钥生成接口**(Java): ```java @GetMapping("/getEncryptKey") public Map<String, String> generateKey() { String key = RandomStringUtils.randomAlphanumeric(32); // AES-256 String iv = RandomStringUtils.randomAlphanumeric(16); redisTemplate.opsForValue().set( "KEY:" + getCurrentUser(), key + ":" + iv, 5, TimeUnit.MINUTES ); return Map.of("key", key, "iv", iv); } ``` --- ### 六、安全增强措施 1. **篡改校验**: - 在加密数据中加入HMAC签名 ```javascript // 前端添加HMAC const signature = CryptoJS.HmacSHA256(encryptedData, key).toString() ``` 2. **请求时效控制**: ```java // 后端校验时间戳 if (System.currentTimeMillis() - timestamp > 5000) { throw new RuntimeException("请求已过期"); } ``` 3. **敏感数据脱敏**: ```java // 日志过滤(使用@JsonIgnore) public class UserDTO { @JsonIgnore private String phone; } ``` --- ### 七、优缺点分析 | **优势** | **挑战** | |-----------------------------------|-----------------------------------| | 1. 即使HTTPS被破解仍保护敏感数据 | 1. 前端加密代码可能被逆向分析 | | 2. 符合GDPR等数据隐私法规要求 | 2. 加解密增加前后端计算开销 | | 3. 防止内部人员窥探传输数据 | 3. 密钥轮换机制需要完善设计 | --- 通过组合使用HTTPS传输加密和敏感字段二次加密,可构建纵深御体系。需注意:**安全性与便利性需要平衡**,建议只对身份证号、银行卡号等高敏感字段实施二次加密
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值