先看现象。开放平台允许用户填回调地址,安全扫描发现填 http://169.254.169.254/latest/meta-data/ 也能保存并触发,服务端从内网发起请求,返回内容被写进了回调日志,云主机的临时凭证差点被读走。
真正麻烦的地方在于,几个原因的现象几乎一样。回调地址没有做协议与目标地址校验。少数情况下是没有拦截私有网段、回环地址与元数据地址。
另外,校验发生在保存时,触发时没有二次解析。
少数情况下是DNS 重绑定可以在校验后把域名解析到内网 IP。
复现一次对元数据地址的请求
curl -s -m 3 http://169.254.169.254/latest/meta-data/ -o /dev/null -w '%{http_code}\n'
随后在保存时解析域名并判定网段
python -c "import ipaddress;print(ipaddress.ip_address('10.0.0.8').is_private, ipaddress.ip_address('169.254.169.254').is_link_local)"
触发前再解析一次并比对
python -c "import socket;print(socket.gethostbyname('example.com'))"
出口侧只放行公网白名单端口
python -c "print({'allow_scheme':['https'],'deny_cidr':['10.0.0.0/8','169.254.0.0/16','127.0.0.0/8']})"
怎么算真的好了,看的是同一批现象。保存内网地址时直接报错,触发阶段解析到私有网段也会被拒,正常公网回调仍然成功且延迟没有变化。
补一句。回调地址是用户可控的出口请求,协议、域名解析、目标网段三层都要校验,只在保存时查一次挡不住 DNS 重绑定。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。
