注意场景:
综合以上三点。为什么在 Https 的保护下,还要额外做签名验证?
主要疑问,如 stripe 的退款接口
curl https://api.stripe.com/v1/refunds -u "$密钥:" -d charge=$charge_id
就可以发起一笔订单的退款或者扣款。按照某些论述的话,没有签名的 Stripe 岂不是非常危险?
签名指的是
data = { "a":"1", "b":2}
data = sort(data) # 按照一定规则对data进行排序
signString = getSignString(data) # 按照一定规则将数据拼成一个字符串
sign = sign(signString) #按照指定算法如md5/shaX等计算hash得到最终的『签名』
data.sign = sign # 最终请求数据
初步总结:
在使用HTTPS的前提下,已经保证了足够的安全性。完全没有必要再额外做一套『签名机制』。
防重放攻击,依靠的是API接口本身的逻辑设计,如下场景,订单相关:
// 根据KEY找到user
$user = Db::table('key_user')->where('key', $request->get('key'))->find();
// 验证key、user是否合法、有权限、balabalabala
// 验证key、user的请求频率
// 如上两步都可以作为中间件全局统一处理
// 查: 无所谓、随便查,无所谓是否重放攻击
return new Response(balabalabala)
// 改: 下单、关闭、结算等balabala
// 下单:订单是否已经存在,存在报错已存在不存在入库
// 关闭:验证订单状态,能关闭就关闭,不能关闭报错
如上PHP代码逻辑完全可以有效防止『重放』带来的『损失』,而且还可以加入一个timestamp
参数,可以有效防止TCP数据包重放
第三次总结:
公开API的接口设计,在有HTTPS的前提下,签名负数『非必要』可以不使用签名。
要注意的是:
20210414-0320总结:
收集罗列了一些国内外的一些大型API服务使用单纯的HTTPS而没有『签名』参与的厂商和认证模式。
国外已知服务
总结: 国内还是偏向一定要有『签名』的多,不知为何。
最终总结
https+签名 ¹ 会『更』安全。
但是,https自身的安全程度足够,没有再额外增加签名 ¹ 机制的必要性。
安全问题主要出在客户端自身网络环境安全。
结贴
1: 签名指的是类似 md5( sortedString + key ) 的附加请求参数
1
screen 2021-04-12 19:52:18 +08:00 via iPhone 4
大多数攻击来自客户端的截取、伪造而非信息传递中
|
2
xmumiffy 2021-04-12 19:53:11 +08:00 via Android 4
是没必要
|
5
iyaozhen 2021-04-12 19:56:22 +08:00 1
签名我记得是两边约定一个 token,把参数和 token 签一下,更安全
一般用在开放接口上吧,就是接口参数大家都知道,你别的地方不小心泄露参数了,就有风险 |
7
des 2021-04-12 20:04:23 +08:00 1
我猜测可能是这几个原因
一是方便用 http,http 也方便调试 二是即使是服务端对服务端,走的是统一的网关,不想再搞一个域名 三是不少人对 https 认识不那么够,不会搞证书认证,毕竟资料也少,相对于签名 |
9
momocraft 2021-04-12 20:16:58 +08:00 2
签名虽然现在看觉得傻 十年前不失为一个 http 下能加密+防重放攻击的方案
另外市场地位不一样 要是 stripe 敢像企鹅这样一个 QR 码写出不到十种 API 可能活不到上市 |
10
akira 2021-04-12 20:18:01 +08:00 2
你要这么说的话,服务端对服务端 http 就够了,ssl 都不需要上
|
11
ihipop 2021-04-12 20:22:51 +08:00 via Android 1
现在很多方案是前端 HTTPS 网关后面业务系统是 HTTP,如果做使用客户端做 SSL 认证,整个系统都需要要改造,不可能业务系统身份认证全做网关上吧? SSL 只 pin 服务端的证书没法验证客户身份,签名不仅仅可以防篡改,还能客户做身份认证
|
12
dzdh OP @ihipop 还好吧,客户端证书在网关直接验证好的,到后面的 HTTP 业务的时候直接带着 https_s|c_xx 的 header 头的哇而且还伪造不了
|
14
wooyuntest 2021-04-12 20:56:35 +08:00 1
加大直接构造数据向接口发请求的难度(还得逆向 app 拿到签名算法)
|
15
dzdh OP @wooyuntest 找密钥吧。这是客户端场景了,即便如此,仅使用 HTTPS 直接传递 ukey 的话也得逆向啊。
HOOK 方案的话无论任何方案都没辙吧。 |
16
kejialiu 2021-04-12 21:57:13 +08:00 via Android 1
签名是一次性的,只针对这个请求,就算被截获了也伤害不大。密钥不管什么原因被泄露了,后续所有请求都可以伪造,所以能不暴露就不暴露,哪怕是加密通道
|
17
dzdh OP @kejialiu
一次性的根本原因是因为有 nonce 。以此为目的达到防 Replay Attack 。 但是究其根本,HTTPS 本身被抓到已有也是个无法篡改的普通 TCP 数据包。重放攻击也只能将这个数据包无数次发送给对方服务器。也就是说,只要在普通的请求参数中加入任意一个具备 nonce 特性的参数如 timestamp,那这个数据包依然可以被直接拦截。 同时,如果说 HTTPS 不用构造直接可以无限次任意请求的话,签名模式难道就不是吗? |
18
dzdh OP @kejialiu
即便没有 nonce 。如 http://domain/path?order_id=x&sign=n 。是的没错,这个请求无法被篡改,但是我依然可以肆无忌惮的发起查询啊。 而且肯定是要有一个『某 ID 』的参数吧?如商户 ID 、用户 ID 等标记唯一应用的参数吧。那也就是说,只是某一个『用户』或『应用』能构造针对特性对象『 order_id=x 』的请求。 那相应的,HTTPS 的话直接作为 Authentication Header 参数放进来一样的哇。 |
19
Vegetable 2021-04-13 00:09:10 +08:00 1
这个有历史的惯性原因吧,大家都有签名,我这没签名显得不安全。
另一个可能是,对于在网络请求中传递密钥的恐惧?我这两天正在设计一个 s2s 接口,也非常纠结到底要不要做签名 |
20
ch2 2021-04-13 01:00:47 +08:00 via iPhone 1
过滤掉不会签名的傻子,仅此而已
|
21
crab 2021-04-13 03:29:22 +08:00 1
防中间人也要防客户
|
22
skull 2021-04-13 06:42:41 +08:00 via Android 2
瞎回答一下,服务端对服务端是理论场景,放出去的 api 不敢保证对方也是服务端
|
23
ferock 2021-04-13 07:20:16 +08:00 via iPhone 1
防御的场景不同
|
24
wanguorui123 2021-04-13 08:17:28 +08:00 via iPhone 1
AppKey 安全的前提下,签名的过期时间戳有一定防止重放的功能,HTTP 下可以额外防止数据篡改,HTTPS 下除了对过期时间的验证好像没什么用
|
29
dzdh OP @wanguorui123
参见 18 楼。 如 http://domain/path?order_id=x&sign=n 。是的没错,这个请求无法被篡改。 我可以直接不停的直接请求,这就是重放攻击对吧?那就单单是『签名』是怎么防止重放攻击的呢?难道不是 API 的露酒设计来检测 order_id 的请求次数么?或者 sign 的请求次数么?无论是否是签名都需要做这一步啊? |
31
ferock 2021-04-13 09:14:26 +08:00 via iPhone
|
32
wanguorui123 2021-04-13 09:17:21 +08:00
@dzdh xxx×tarp=xxx,timestarp 就是时间戳,通过判断过期时间和上次请求的时间戳过滤请求
|
33
yukiww233 2021-04-13 09:28:58 +08:00
1 签名会把时间戳签进去;
2 签名不能完全避免, 只是增加了一层破解客户端的难度 3 没看懂你那段"业务逻辑"是怎么防止重放的, key 是 server 返回的, 那直接重放 server 返回 key 的请求呢? |
34
pkoukk 2021-04-13 09:46:47 +08:00
提高伪造消息的门槛啊。
作为码农肯定都干过 F12 查 api 地址参数,然后假冒浏览器调用 api 的行为。 有了签名就得逆向代码找到签名逻辑,如果没有签名就直接拿来用了 |
35
dzdh OP @yukiww233 33
@wanguorui123 32 我接受请求啊,因为你的 KEY 是合法的啊( stripe 的 user)。 但是针对某一个资源的操作如 x_id=N,只要我接口保持幂等,无所谓重放不重放吧?对实际业务 0 损失啊?即便有签名,也只是根据 timestamp 和当前服务器时间的差值决定是否拒绝请求,但是请求依然进来了啊。 |
37
dzdh OP |
39
wanguorui123 2021-04-13 09:52:23 +08:00
@dzdh 1 、有些时候需要防止重复下单,通过 timestarp 验证是否和上次相同来避免、2 、有些时候防止文件被盗链,这时候通过 timestarp 的过期时间来验证是否是过期资源,防止迅雷等工具盗刷流量
|
40
dzdh OP @wanguorui123 39
理解。但是: > 注意场景是 Server To Server 1. API 的接口设计的逻辑是否可以要求下单的时候带上一个发起方的 ID 呢?这个 ID 不就是个 nonce 么?根据这个 ID 也可以防止某个内部服务重复下单啊(注意场景是 Server 端到 Server 端,如 stripe 、paypal 、支付宝、微信的预下单接口) 2. 是否可以直接生成一个加密 token 在 token 里直接绑定 ip 、时间戳,然后这个 token 一次有效呢?只能由 openssl 解密(是的,没有仅仅只依靠 HTTPS ) 我指的签名仅仅指的是:按照规则排序然后拼字符串使用 shaX/mdX 的形式 |
41
wanguorui123 2021-04-13 10:01:12 +08:00
@dzdh 其实不考虑特殊情况,携带 Token 或者 Session 加 HTTPS 已经足够安全了
|
42
wy315700 2021-04-13 10:09:58 +08:00
其实写程序的时候,你永远要考虑对面那端的傻逼行为,要在对方傻逼的时候依然尽可能的保证程序接口可用性。
楼主这么想一想,就知道为啥一些接口会有奇奇怪怪的设计了。 你可能觉得 server to server 加上 标准化的 HTTPS 是一个安全的协议。。 但是如果对面不是 server 呢,直接把接口写到客户端了呢,有见过直接客户端直接连接远程 MySQL 的程序。 再或者是,有人嫌麻烦,HTTPS 关闭了证书验证呢,据了解这么做的不在少数。 |
43
keyfunc 2021-04-13 10:10:08 +08:00
首先,你不能保证 ssl 隧道肯定安全,密钥泄漏,使用了不安全的密码套件等都会导致 ssl 存在安全漏洞。
签名挑战机制足够简单,越简单的方法越安全。 |
44
newmlp 2021-04-13 10:29:36 +08:00
tls 只能保证端和端之间是安全的,如果客户端不安全你用 https 没啥用,加个自己的签名可以增加反编译的难度
|
46
wentx 2021-04-13 11:08:28 +08:00
这个应该是取决于对安全性的要求,像 stripe 这用了 HTTPS + 请求 Sign 的 API 应该就是双重保障,毕竟都是跟钱相关的,如果某个用户的本地被加了一层类似于 Charles 的 HTTPS 代理,那它还可以通过 API 签名来防一手的.
|
47
David1119 2021-04-13 11:11:50 +08:00
主要不是为了安全,是为了反爬好吗,防止用户把数据都爬走,大部分都是 so 加密算法,增加客户端破解难度
|
48
timedivision 2021-04-13 11:14:15 +08:00
安全没有绝对的,为了更安全,谁都愿意多加几道锁就好比我把钱放保险柜里,我保险柜不还得放家里吗?难道我把保险柜放外面?
|
49
kejialiu 2021-04-13 12:10:36 +08:00
@dzdh 一个设计合理的请求签名算法中,所有有含义的请求参数都是被签名的对象,任何参数的修改都会导致签名失效,所以被拦截了也就只能重放那一个特定请求而已。更别说还有时间戳限制有效时间。总体原则就是,如果拦截不可避免,也要把损失降到最小
|
50
borisz 2021-04-13 12:19:02 +08:00
防抵赖
|
51
kejialiu 2021-04-13 12:23:49 +08:00 via Android
安全从来都是相对的,一般我们是可以认为 https 是可以防中间人的,但这也是有前提的:
- https 服务端的密钥是怎么管理的,都有谁经手了,不小心泄露了呢? - 浏览器这样的客户端是否可信任呢?里面会不会装了莫名其妙的插件钩子呢? - 签发 https 证书的 CA 是不是足够可信任呢? 根据你的应用的敏感级别,这些可能都是需要考虑的因素。比如 Google 的安全原则是所谓 n+1,意思就是你总是要多做一层防护,让不明真相的程序员在做错了 n 件事的情况下仍能保证安全。 |
52
borisz 2021-04-13 12:27:38 +08:00
https 只能表示传输的数据时正确的, 但是能保证传输的数据对于系统时无害的,
例如平台内部用户伪造支付请求, 或者内部大量无效请求, 测试需求发送到业务服务器. |
53
Telegram 2021-04-13 12:32:16 +08:00 1
这是概念混淆啊,照你意思是用了 https,用户就不需要账号密码了?
|
54
no1xsyzy 2021-04-13 12:54:38 +08:00
SSL/TLS 不能确定对方是否对你的证书进行了验证。
双边证书使用相对麻烦。 然后还有遗留问题和习惯问题。 |
55
VHacker1989 2021-04-13 13:01:54 +08:00
任何客户端做的校验都是多此一举,逆向,动态调试,xposed hook 都能破解和获得密钥,因为客户端完全掌握在用户手里
|
56
Marinaaaa 2021-04-13 13:21:18 +08:00
给不怀好意的人增加更多破解成本。如
|
57
Marinaaaa 2021-04-13 13:23:19 +08:00
如果是服务端对服务端,且都是内部系统,内网访问的话,感觉没太必要。如果非内网访问的话可能还是有点风险吧
|
58
hxndg 2021-04-13 13:31:29 +08:00 1
只能说明两个现象:
1 很多人对于 TLS 的理解是一知半解的,TLS 本身是非常灵活的,可以根据需求变更 auth/enc/verify 等功能,好多人只知道个 enc 就完了,建议重新研读下 RFC,上面的回复里面有明显的错误。 2 TLS 层安全开发做的不到位,很多的功能原本是明文的,为了一些安全的功能采用了打补丁的方式,而 TLS 加入以后处于兼容性功能或其他方面考虑没能去掉这些补丁。 毕竟国内做业务的和我们这些做基础服务的关注的点不同。 |
59
walpurgis 2021-04-13 13:31:33 +08:00 via Android
@Telegram 因为传输层可以保证安全的话,密码可以直接明文传了,不需要将密码再套一层 HMAC 了
|
60
3dwelcome 2021-04-13 13:36:04 +08:00
"Pinned Pub Key 还怎么中间人呢?防止客户的什么呢?"
能做到这种验证毕竟是少数,大部分服务器就校验了一下对方 CERT 有效性,因为 CERT 会过期,客户定期会换,你又没办法一直保证服务器存有对方最新的 KEY 和 CERT 来校验。 而且我看所有有钱有关的网络支付,都带签名。 这其实和数据库是不是存用户明文密码是同一个问题:反正用户又看不到服务器上具体有什么,不存 hash,直接存明文是不是一样的?(学一下楼主额外描述:服务器不可能被攻破,因此黑客攻击不在考虑范围内) |
61
hxndg 2021-04-13 13:36:09 +08:00
|
62
hxndg 2021-04-13 13:40:50 +08:00
@3dwelcome
那东西叫做公钥钉扎,防备中间人,已经废除了。 此外 签名和数据库存储明文密码根本不是一个概念,每个步骤都是有其基本目的的,签名是用来防抵赖,抗修改+重放的 数据库不能存储明文用 hash 是对抗内部攻击,泄漏的 |
63
3dwelcome 2021-04-13 13:54:41 +08:00 1
@hxndg
"那东西叫做公钥钉扎,防备中间人,已经废除了。" 不不,楼主说的是 Server to Server 上的 PubKey 验证,就是直接在 TLS 握手阶段,读取证书里的 pubkey,看是不是和数据库里的一致,服务器要额外写一些入侵式代码。 你说的废除,只是 chrome 浏览器里定下的规范。楼主这里没有浏览器的参与。 |
64
xuanbg 2021-04-13 13:56:36 +08:00
SSL 是可以保证数据传输的安全,但也仅仅是保证数据传输过程安全而已。数据在传输前是不是伪造的?这个 SSL 可没法保证。
|
65
hxndg 2021-04-13 14:06:56 +08:00
@3dwelcome
1 首先公钥钉扎,或者说公钥固定( whatever )不单纯针对浏览器,任何涉及到 TLS 的都会做,我们实现了钉扎,但是国内没人用,最后没进正式 build,代码被废除了。国内没有一个运营商采用这东西,几大银行更是提都不提。 2 chrome 废除了这个确实,这点我确实说错了。 |
66
AlisaDestiny 2021-04-13 14:18:29 +08:00 1
其实这就像登录密码和支付密码,登录密码只能确定付款方身份是你,但这并不代表这次的支付请求是由你本人发起(比如你室友趁你睡觉的时候拿你手机给自己转账),所以需要你输入支付密码确认。
|
68
dzdh OP @AlisaDestiny 这个场景『签名机制』也没用,因为如果『支付密码』你泄露了呢?安全上两个方案一样的,甚至不如 HTTPS
|
69
dzdh OP @xuanbg HTTPS 能够保证传输安全已经足够了,真正的 API 请求安全(身份鉴别)不是依靠其传递的参数实现的吗?直接 HTTPS+HTTP Authenctication 一样的啊?
|
70
dzdh OP |
73
dzdh OP |
74
dzdh OP @wentx 46
Charles 是依靠『中间人』来进行抓包的。 其次,如果真的使用 Charles,那是不是就意味着其已经控制了『物理机』?在控制『物理机』的前提下还有什么安全方案是可以保证『全安』的吗? 并不能证明 HTTPS 环境下,签名模式的『必要性』 |
75
dzdh OP @David1119 47
接口设计要求 CleintID,爬虫场景完全可以屏蔽 ClientID 。 其次,标题是 Server 端到 Server 端。即便是用户不小心暴露 API,依然可以屏蔽 ClientID,中断爬虫行为。该理由不成立。 |
76
dzdh OP |
77
dzdh OP |
78
dzdh OP @kejialiu 51
HTTPS 防止中间人有 SSLPinning 的方案,足矣无限接近 100%的保障安全(请提出反对意见) - https 服务端的密钥是怎么管理的,都有谁经手了,不小心泄露了呢? 回:签名模式的密钥怎么管理的?都谁经手了,不小心泄露了呢? - 浏览器这样的客户端是否可信任呢?里面会不会装了莫名其妙的插件钩子呢? 回:Server 端到 Server 端,签名模式就能规避『莫名其妙』的钩子是吗? - 签发 https 证书的 CA 是不是足够可信任呢? 回:本地指定 CA 文件验证 根据你的应用的敏感级别,这些可能都是需要考虑的因素。比如 Google 的安全原则是所谓 n+1,意思就是你总是要多做一层防护,让不明真相的程序员在做错了 n 件事的情况下仍能保证安全。 回:所以是 HTTPS 足矣保证安全,仅仅只是为了多加一层兜底手段,对吗? |
80
dzdh OP @no1xsyzy 54
- SSL/TLS 不能确定对方是否对你的证书进行了验证。 回: ??? - 双边证书使用相对麻烦。 回:签名排序参数使用密钥拼接参数字符串不麻烦吗? - 然后还有遗留问题和习惯问题。 回:如? |
81
dzdh OP |
84
jhdxr 2021-04-13 16:04:12 +08:00
Server 端到 Server 端 这一点是如何保证的?就是说,我提供了一个接口,我预期对方应该也是 server 端,但我如何可以保证这一点?
|
85
dzdh OP |
86
ashuai 2021-04-13 16:25:18 +08:00
https 能保证信息的完整传输吗?
例如我给服务器发 123456,因为网络抖动,结果服务器有可能只收到 1234 有以上可能吗?如果有,这就是签名的意义 |
89
abersheeran 2021-04-13 16:42:34 +08:00 1
把 Token 加入签名内容的签名,可以防止 Token 明文传输。把 Timestamp 加进签名内容可以防重放攻击。
不过如果依赖于 HTTPS (这里需要注意证书),这两都可以不要。我觉得可能现在还这么做的大厂,惯性更多一点吧,毕竟代码已经写好了,可以跑了,改设计,没必要。 |
90
3dwelcome 2021-04-13 16:45:09 +08:00
"所有人的质疑是否代表着 Stripe 和 Paypal 现在是极度危险状态?"
你用国外网站举例没意义,国外信用卡支付没密码是安全的,放国内基本不可能。 同理国外电子支付没有签名是安全的,国内设计的 API 没签名?想都别想,和钱有关的,必须加码,管他有没有用。 这和软件加壳一个道理,你不知道破解者的能力和权限(说不定就是你隔壁同事),越套一层防范,单纯从理论上来说,就能阻断一部分破解者的尝试,也就变相代表着越安全。 就安全系数来说,有签名的 API 和没签名的 API PK 一下,有签名的得分高。光刷公司的 KPI,也必须加。 |
91
3dwelcome 2021-04-13 16:47:07 +08:00
@abersheeran “我觉得可能现在还这么做的大厂,惯性更多一点吧,毕竟代码已经写好了,可以跑了,改设计,没必要。”
为了支付系统上出 BUG 可以甩锅,小码农根本赔不起。“看,API 设计够好了,还有签名!” |
92
LeeReamond 2021-04-13 16:54:06 +08:00
@akira 显然不对,tcp 只保证连接可靠,不保证数据可靠,单纯 http 肯定是有缺陷的,居然还有两个人点赞
|
93
LeeReamond 2021-04-13 16:57:56 +08:00
@dzdh 必要性是没有,但应该也没有非必要性。很难有 api 在设计层面说脱离开 tls 我干脆就禁用,大部分是保留支持能力的,那如果已经写好了逻辑,就没有删除的理由。哈希算法成本很低,hs256 也仅在百纳秒的数量级,而不以抗哈希攻击为目的的快速哈希可以做到远更快
|
94
timedivision 2021-04-13 18:34:21 +08:00 1
@dzdh 所以为了更安全,任何提高安全性的手段都是必要的呀
|
96
dzdh OP |
97
dzdh OP @3dwelcome 90
> 管他有没有用 所以并不是出于安全考虑,就是为了『加』而『加』? > 你用国外网站举例没意义,国外信用卡支付没密码是安全的,放国内基本不可能 公共网络的、任何人都可以进行调用的、处于公网环境的 HTTPS 接口,甚至有详细的文档。为什么他就『安全』呢?支付宝接口如果不用『签名』设计就『不安全』了?比如如果支付宝现在开始不用签名了,会导致什么问题呢?包括但不限于下单、退款、关闭订单、转账等接口,就直接像 stripe 一样 `curl -u $key https://api.alipay.com/api/transfer?to=xxx&amount=1000` 。 |
99
dzdh OP |
100
catchexception 2021-04-13 19:23:06 +08:00 2
大多数回答都不在点子上。一个是传输层,一个是业务层。
|