IDOR 是授权的失败,不是认证的失败
一句话总结
认证是“确认你是谁”,认可是“判断这个人是否可以对这个资源进行这个动作”。大多数实际思维都源于后者。
为什么需要这个?
登录设置得很好,但发生错误的情况很多。用户被正常认证,令牌也有效。但是/api/orders/1042的数字1043换成的话可以看到别人的订单。
这就是IDOR(Insecure Direct Object Reference)。虽然认证通过了,但没有获得批准。“这个令牌的主人是这个订单的拥有者吗”没有人确认。
怎么行动
需要养成将两个问题分开在代码中思考的习惯。
认证在请求进入点进行一次。验证代币签名,报告到期,移除主体(sub)。这一步骤可以作为中间件处理,与资源无关。
授权是每个访问资源的点上进行的。而且需要知道资源的所有者或属性才能判断,因此仅凭中间件是不够的。“订单1043的owner_id是否与当前用户的sub相同”只有在读取该订单后才能判断。
这里有一个实际操作规则:在查询查询本身中加入所有者条件。SELECT * FROM orders WHERE id=? AND owner_id=?用的话,别人的咒语本来就没有结果。比起读完后比较的方式,更容易避免犯错。
角色和权限也值得区分。角色是附在人身上的标签(例如:order-admin),权限是附在动作上的名称(例如:order:refund)。仅凭角色编写授权的话,角色数量就会爆炸,仅凭权限编写授权的话,管理就很难。实际操作通常将权限与角色结合起来,并赋予用户角色。
把印鉴放在哪里呢?
把同样的规则分散在多个层面上,总有一天会出错。确定其中一个三位数 其余部分只用作辅助。
| 位置 | 擅长的事情 | 不擅长的事情 |
|---|---|---|
| 网关 | 路由·方法单元封锁、认证验证 | 不知道资源的拥有者 |
| 服务代码 | 根据所有者·状态的判断 | 规则散落在代码中 |
| 政策引擎(OPA等) | 将规则集中在一个地方并进行审核 | 再来一次往返 |
实际操作的基本形式是在门户网站上认证,在服务上批准。门户网站是 只看到代币有效,知道这个命令的人是否能看到这个命令。 服务决定。
不忘记权限检查的结构
依靠人的注意力的话,总有一天会掉落的。用结构来阻止。
**将基础设置为拒绝。**如果创建新的端点,没有任何装饰器时 应该是403。如果允许是基础,遗漏的地方就是漏洞。
@router.get("/orders/{oid}")
@requires("order:read") # 없으면 라우터 등록 단계에서 거부
def get_order(oid: int, user=Depends(current_user)):
# 조회 자체에 소유자 조건을 넣는다 — 읽은 뒤 비교하는 것보다 실수하기 어렵다
row = db.one("select * from orders where id=%s and owner_id=%s", (oid, user.sub))
if not row:
raise HTTPException(404) # 403 이 아니라 404 — 존재 여부도 흘리지 않는다
return row
最后一行很重要。如果给别人的资源403,就会告诉“那个号码存在” 是给的。在使用顺序ID的系统中,仅凭这个就能泄露整个规模。 最好像没有一样给404。
**列表查询也必须包含同样的条件。**虽然只看到了详细信息,但列表全部 返回的错误很常见。所以让查询函数本身接受所有者参数, 写不接受函数的办法是完全不写函数的方法。
在多租户中再加一层
如果多个组织使用一个系统的话owner_id仅仅这样是不够的。在同一个组织内的其他
因为有人们应该能够看到的资源。tenant_id在所有票上写上,
添加强制附加到所有查询的层。如果是PostgreSQL,则为Row Level Security。
DB也可以直接强制执行。
alter table orders enable row level security;
create policy tenant_isolation on orders
using (tenant_id = current_setting('app.tenant_id')::uuid);
即使应用程序出错,DB也会阻止。代价是每次连接set_config叫的
这是为了避免麻烦和在连接池中设置不更新而注意的事项。
在现场相遇的样子
在将角色存储在令牌中和在每个请求服务器上查询之间也有选择。将角色存储在令牌中快捷,但即使恢复角色,令牌也有效直到到期。因此,在授权方面,将访问令牌设置为短时间也很重要。
另一个经常出错的地方。在前台隐藏按钮不是授权。UI是方便,判断必须在服务器上重新做。“只有管理员才能看到的画面,但API是打开的”这种情况在渗透测试中首先出现。
在下次确认中看到的东西
首先在测验中确认是否可以区分认证成功和资源批准成功。在下一个模块中整理OAuth2授权类型的地形图后,进入实际的Keycloak。