概述
配景
之以是想跟大家分享怎么做好基础的接口安全,一方面是工作需要,之前这部分的工作是我在负责,以是正好有这部分的履历;二是我之前也接触过许多做后端的同学,对于这一块大部分涉及不深或不太相识,也不知道怎么样做才算相对安全。以是把我的想法写出来,供大家参考和讨论,共同学习和进步。
固然了,我分享的也只是我的个人履历,自然有许多的不足之处,也并不是说我后续叙述的一些观点和实现就一定是安全的,大家要加入本身的判定,并且混入一些定制化的东西
适用范围
本章节内容,主要说的HTTP接口的安全设计,涉及内容包括防窃听、防篡改、防重放、密钥传输安全、密钥存储安全、敏感数据处理等。适用于负责后端、测试或信息接口安全的同学
防篡改
数据加签
为相识决这类标题,前端可以给数据加上一个署名,好比接纳MD5最简朴的署名方式,可以把哀求参数看成待署名的数据,计算一个MD5值,然后传给服务端,服务端同样用参数生成MD5值进行比对,发现不一样,那阐明数据被篡改过了。
数据加签的机制是没有标题的,并且确实可以解决防篡改的标题,但是算法和机制要选好。
无论是MD5、HMAC、还是别的类型的摘要算法,都有一个标题,就是篡改者要是知道了算法,那还是可以篡改。以是要选用非对称算法来做数据的加签与验签,如许就算篡改者知道了算法,但是没有私钥也篡改不了,由于非对称的机制就是用私钥署名,公钥验签,公钥可以公开出去。
好比客户端用客户端的私钥对哀求参数进行署名,并把公钥给到服务端,服务端在收到哀求后,用客户端的公钥进行验签。就算篡改者知道了客户端的公钥,也无法对哀求数据进行篡改,此时客户端的重心就转移到了,如何保护它的私钥标题上了,而不用担心数据会被别人篡改
署名是只能由私钥生成,公钥验签。公钥是无法生成署名,同时使用公钥验的。这就是非对称算法的利益。
双签(双向传输加密)
同样的,服务端把本身的公钥给到客户端,服务端在响应数据的时候,进行署名,客户端进行验签,这就是双签。即客户哀求时,使用客户端的私钥署名,服务端验,服务端响应时,使用服务端的私钥署名,客户端验。来确保数据交互时哀求和响应都不会被篡改。
常用算法
常用的算法有RSA1024、RSA2048、SM2,固然另有一些别的ECC类算法。早期大家用的根本是RSA1024,如今大部分都用2048或更长的密钥来生成署名了。固然也有效国密的,不外用的少,像我们公共交通行业用的多,另有就是国企。我如今设计体系,根本是天下密体系了,除了一些要给第三方调用的,会做RSA+SM2,就是任选其一。
SM2有个利益就是生成的署名是64字节的,RSA的话,密钥越长,署名值就越长,计算复杂度也越高,体系负载固然也会跟着上去。国密应用这么多年了,安全性还是不用担心的。
接下来,我们就用国密来实现数据的加签,实战一波。
加签数据设计
GET哀求
对于GET类的哀求,我们可以将参数先按字母进行一个排序,然后把他们拼装起来,好比:
- curl -v 'http://127.0.0.1:8080/testGet?p=testp&ab=123&x=456'
复制代码 那么参数排序,并拼装后变成:
POST哀求
POST如果是FORMDATA形式,我们也可以像GET哀求一样,先将参数进行排序,再拼装,好比:
- curl -v -H 'Content-Type: application/x-www-form-urlencoded;charset=UTF-8' --data-binary "p=test1&ab=123&x=456" 'http://127.0.0.1:8080/testPostFromdata'
复制代码 同时按照GET的参数拼装方式,拼装后变成:
POST BODY
对于传输的数据是BODY形式的,我们可以约定一种拼装方式,把BODY原封不动的作为entity的值来拼接,如:
- curl -v -H 'Content-Type: application/json' --data-binary '{
- > "pInt": 1,
- > "pBoolean": false,
- > "pString": "ssss"
- > }' 'http://127.0.0.1:8080/testPostBody'
复制代码 拼接后变成:
- entity={"pInt": 1,"pBoolean": false,"pString": "ssss"}
复制代码 这里entity的值就是BODY的内容,原封不动,有换行也是要体现,这里为了方便大家查看,换行符我就去掉了。
标题一
有了这个规则之后呢,要署名的数据是有了,但大家有没发现一个标题,如果哀求参数是空的,咋办,没有数据可签肯定也不行。
标题二
如果两个不同的方法参数个数是一样的,参数名也一样,那是不是有了署名值,别的老六就可以作文章了。
标题解决方案
以是呀,为相识决上面的两个标题,我们可以把要哀求的接口地点也加到待署名的数据里去。好比:
- curl -v 'http://127.0.0.1:8080/testGet?p=testp&ab=123&x=456'
复制代码 可以约定接口地点的键为path,并且不参与排序,直接拼在后面,那么上述哀求的待签数据是:
- ab=123&p=testp&x=456
- &path=/testGet
复制代码 如许是不是即解决了参数可能为空,又避免了别人用同样的参数和署名值去访问别的接口的标题。
署名值存放
关于署名值放置的位置,可以根据大家的需要,放哀求头Header里或公共参数都可以。
关于算法
关于SM2署名算法有几点跟大家提一提。
- 署名肯定都是私钥署名,公钥验签。
- 验签的复杂度比署名高,由于验签有两次点乘一次点加,而署名是一次点乘。
- 署名如果只有公钥的话,要多一次点乘,即从私钥生成公钥,以是服务器写署名算法,就把公私钥都生存起来,可以省掉一次点乘。
- 署名算法在计算z的时候,要传入userid,默认都是1234567812345678。这里如果是非标的话,可以定制和作文章,加大安全性。
- 署名需要随机生成大数 in [1, n - 1],不要偷懒,固然别人封装好的,一般都会处理。
集群部署
通常为满足集群部署,水平扩展的要求,我们需要把用户用于验签相干的内容缓存到内存中(好比Redis),以减少数据库这方面的查询压力。
注意事项
- 对于GET类的哀求,通常的参数的值可能需要颠末encodeURI处理,这里指的是拼装待署名数据的时候,哀求传参时不需要
- 对于POST类哀求,在写拦截器的时候,要定制InputStream,由于拦截验签的时候,会先把BODY读出来,以是处理完要写回去,或接纳别的雷同方法处理
- 这类非功能性需求,如果条件允许,可以将缓存用户公钥这类服务独立出来,增长服务的可用性。
- 前端和后端加密的参数一定要包管每个参数的数据类型一致,否则末了加密的结果可能不同(博主亲身踩的坑)
降级处理
针对Redis不可用的降级处理呢,我个人想法是如许的,可以事先根据用户的公钥,使用服务端的私钥生成一个证书,下发给客户端,好比证书内容为:
序号字段名字段类型长度(单位字节)备注1用户标识ANSIN用户唯一标识2证书签发时间HEX4UTC时间戳,单位秒3证书失效时间HEX4UTC时间戳,单位秒4密钥索引HEX1使用服务端的哪个私钥签发的5客户端公钥HEX33压缩SM2公钥6数字署名HEX64使用指定索引下的私钥对1-5进行署名 一旦发生Redis不可用,防篡改这块业务需要降级处理的话,就让客户端在哀求时,将证书也发过来,服务端先验证书,证书有效的情况下,用证书里的公钥验客户端署名。
这种方法呢,会增长客户端的流量、服务端流量以及服务器的负载
防重放一
上一章节,为了防篡改,我们给第一个哀求增长了署名和验签,如许就可以解决数据防篡改标题。你以为如许就安全了吗?我们看,同一接口,同样的参数,同样的署名值,是不是每次调用,都是可以成功的。如果被恶意使用,非法用户疯狂的调用这一个接口呢,服务器资源是不是就浪费了,如果这个接口业务比较复杂,还要操纵数据库,正好操纵数据库也耗时,那是不是有可能给搞的当机了。
那有没有办法解决这个标题呢,固然有。好比再给每一个哀求加上一个时间戳,并且时间戳也加入署名防篡改,是不是可以先验证这个哀求是什么时候发起的,有没有超过指定时间(好比3分钟),超过就直接丢弃。
好比这个接口:
- curl -v -H 'X-TimeStamp: 1680503150' 'http://127.0.0.1:8080/testGet?p=testp&ab=123&x=456'
复制代码 我们在Header里,多传了一个X-TimeStamp,值为距离1970-01-01的UTC秒数,为了防止别人篡改呢,也需要将其作为署名的数据,好比固定参数名为timestamp,放置在path后面,也不参与排序,那么待署名数据就变成了:
- ab=123&p=testp&x=456
- &path=/testGet×tamp=1680503150
复制代码 遗留标题
- 加入时间戳后,可以有效的防止重放,但有效期内哀求仍然有效,以是需要加入别的方案来增强,好比限流。又好比后续讲到的nonce(随机串)。
- 时间戳有一个客户端与服务器时间可能会不同步的标题,以是多少分钟内有效,要根据业务需要来定。另外也可以在服务器加入获取服务器当前时间的接口,来同步客户端与服务端的时间。
- 如果加了获取服务器时间这种无状态的接口,也要考虑加入署名及验签机制,或者限流机制,防止攻击。
防重放二
上面讲到了,为了防止接口重放,我们给每一个哀求加上一个时间戳,但是如许并没有完全地解决防重放标题,有效期内还是可以多次调用。
以是为相识决这一标题呢,我们可以在每一个哀求上,再加上一个随机串nonce,客户端每个哀求的nonce我都记录下来,那么下一次再次哀求的时候,就先根据nonce查一下,有没有哀求过,哀求过就丢弃,没有就正常处理。
好比这个接口:
- curl -v -H 'X-TimeStamp: 1680503150' -H 'X-Nonce: wX5krzyVHuaYS8Ta' 'http://127.0.0.1:8080/testGet?p=testp&ab=123&x=456'
复制代码 我们在Header里,多传了一个X-Nonce,值为wX5krzyVHuaYS8Ta(随机生成的),为了防止别人篡改呢,也需要将其作为署名的数据,好比固定参数名为nonce,放置在timestamp后面,也不参与排序,那么待署名数据就变成了:
- ab=123&p=testp&x=456
- &path=/testGet×tamp=1680503150&nonce=wX5krzyVHuaYS8Ta
复制代码 注意事项
- 如果客户端超时重传,需要产生新的Nonce,要不可能会被拒
- Nonce需要共同Timestamp一起,好比将Nonce存放到Redis,给Nonce设置一个逾期时间,这个逾期时间可以为Timestamp的有效期
- 如果条件允许,可以将Nonce缓存独立出来,避免故障引起服务不可用
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |