无人谈论的API漏洞:数据过度暴露的安全隐患

本文深入探讨API安全中常被忽视的数据过度暴露问题,分析其危害性和普遍存在的原因,通过实际案例展示这种漏洞如何与其他安全问题叠加造成严重后果,并提供防护建议。

数据过度暴露:沉默的安全威胁

在讨论API安全与Web应用安全的差异时,一个突出的观点是:API通常存在数百个小问题,这些问题会随时间累积,而不是一个单一的重大漏洞。

SQL注入是严重的,这一点众所周知。但那些"按设计"就交出敏感数据的API呢?

这并非说它比SQL注入更糟糕,但它可能更加隐蔽,因为它会放大你拥有的每一个其他漏洞。

数据过度暴露的模式

这种模式随处可见。比如一个端点 GET /api/users/123 返回如下数据:

1
2
3
4
5
6
{
    "user_id": 42,
    "name": "Joviane",
    "email": "myemail@gmail.com",
    "role": "student"
}

但同时它也返回:

1
2
3
4
5
6
{
   "internal_user_id": 64, 
   "full_address": "Secure Street, 403", 
   "ssn_last_4": 1234, 
   "phone_number": "73737-7373"  
}

以及许多你本不打算暴露的数据。前端只显示姓名和电子邮件,但API却返回了数据库中的一切。

你可能会想:“但只有经过身份验证的用户才能调用此端点,所以没问题!“确实如此。但当攻击者入侵任何用户帐户时会发生什么?当开发人员意外记录完整响应时?当浏览器扩展抓取数据时?当响应被缓存在不应存在的位置时?所有这些敏感数据就在那里等着被利用。

最糟糕的是?这会与其他漏洞叠加。假设你有一个BOLA漏洞,用户可以通过更改ID访问其他用户的数据。如果你的API只返回公共字段,影响将是有限的。但如果它泄露PII、内部ID或敏感业务数据,那么这个BOLA漏洞就可能演变成大规模数据泄露。

为什么这种情况无处不在

这不是恶意的,通常是为了方便。返回整个对象比过滤字段更快。ORM也没有帮助,除非显式使用投影或选择特定字段,否则它们默认返回所有内容。有时团队试图聪明地"面向未来"设计API,包含以后可能需要的字段。有时?这只是复制粘贴。一个端点这样做,其他端点就跟着做了。

从开发速度的角度来看,这是有道理的。在压力下发布功能时,我自己也这样做过。你编写一个快速端点,测试前端是否正确显示,然后发布。API返回20个字段但UI只使用3个?因为功能正常,没人注意到。

实际影响

举一个代码审查中的具体例子。一个在线学习平台有一个端点 GET /api/courses/{courseId}/students,返回学生注册数据。教师查看他们的学生是有道理的,对吧?但它返回的不仅仅是姓名和进度百分比。它还返回完整的电子邮件地址、注册日期、支付状态、带有时间戳的测验尝试历史、讨论论坛活动指标,甚至学生访问课程的设备信息。

前端只显示学生姓名和课程完成百分比。如果你是一名学生?在UI中你只能看到自己的状态。但任何注册学生都可以直接调用该端点,更改课程ID,并从其他课程中提取数据。有人可以通过迭代课程ID来构建完整的数据库,了解谁在修什么课程、支付模式、学习行为和个人联系信息。他们不需要破坏任何东西或找到巧妙的漏洞利用方法。API只是把一切都交了出来。

幸运的是,这在进入生产环境之前被发现了,但由于该功能在UI和API中工作正常,这很容易被忽略并进入生产环境。

再谈谈PII的影响。那些泄露的学生数据?我们谈论的是全名、电子邮件地址、电话号码、实际地址,可能还有支付信息。在许多司法管辖区,这是等待发生的GDPR违规或等效问题。即使攻击者从未恶意使用这些数据,你也已经面临监管罚款、强制违规通知和公关噩梦。所有这一切都是因为API返回了15个实际上没人需要的额外字段。商业情报泄露对竞争原因来说当然很糟糕。但PII暴露?这种事情会让你因为所有错误的原因登上技术频道的头条。

另一个常见模式:分页端点泄露过多信息。你调用 GET /api/students?page=1&limit=100 期望得到学生列表,你返回的不仅仅是学生,还有他们的哈希密码、API密钥、内部权限、最后登录时间、IP地址……所有这些都不应该离开后端。

规模问题

SQL注入是一个漏洞。你可以找到它,修复它,然后就完成了。数据过度暴露?那是数百个端点,每个都泄露一点数据,随时间累积。

攻击者更容易利用哪一个?存在于每个端点中的那个。他们不需要找到巧妙的注入载荷。他们只需要迭代遍历你的API并收集你免费提供给他们的所有内容。而且因为它在"技术上按设计工作”,它甚至可能不会触发你的安全监控。没有失败的请求,没有可疑的载荷,只是正常的API调用返回了过多的信息。

其他"无聊"但重要的漏洞

还有Mass Assignment——用户发送 {"name": "Deckan", "isAdmin": true} 而API就……接受两个字段。没有验证什么是可更新的。突然之间,普通用户变成了管理员。

或者不恰当的速率限制。密码重置没有限制?通过暴力破解接管账户。OTP验证没有限制?再见了2FA。搜索没有限制?恭喜,有人刚刚抓取了你整个数据库。

还有经典问题:可预测的资源ID/api/invoices/1001/api/invoices/1002……你明白这意味着什么。攻击者只是迭代并收集一切。经典的BOLA。

为什么这很困难

这些不是那些成为头条新闻的性感的零日漏洞。它们是嵌入到数十或数百个端点中的架构问题。找到它们意味着真正理解每个端点的作用。你需要知道每个端点返回什么,需要返回什么,以及哪些只是额外的负担。然后将其乘以API中的每个端点。这很繁琐,但很重要。

这就是为什么API安全测试很棘手。你不是在寻找一个大漏洞。你是在检查每个单一端点的这些模式。数据在不应该的地方泄露,缺失的身份验证检查,不存在的速率限制。所有这些问题无处不在,并且相互叠加。

你的团队如何处理这个问题?

我们想了解的一个难题是:当你构建新端点时,如何确保开发人员只返回必要的字段?代码审查?自动检查?强制显式字段选择的响应DTO?

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计