接口未授权如何校验

wen 开源项目 31

本文目录导读:

接口未授权如何校验

  1. 核心两步走:认证(你是谁)+ 授权(你能做什么)
  2. 基于 Token 的校验流程(最常用,JWT 为例)
  3. 基于 Session/Cookie 的校验(传统服务端渲染)
  4. 三种常见的未授权漏洞及防范
  5. 更好的实践方案
  6. 总结一张表

接口未授权校验是Web安全中最基础也最重要的环节,就是在允许用户访问某个接口或执行某个操作之前,系统必须确认“你是谁”以及“你有没有权限做这件事”

如果校验不严,攻击者可以直接访问敏感数据或执行管理操作,下面我来介绍校验方法、实现方式以及常见误区。

核心两步走:认证(你是谁)+ 授权(你能做什么)

未授权校验通常分两个阶段:

  1. 身份认证:确认用户身份,常见方式有 Session/Cookie、JWT(JSON Web Token)、OAuth2、API Key。
  2. 权限校验:确认用户有权限,常见模型有 RBAC(基于角色的权限控制)、ABAC(基于属性的权限控制)。

基于 Token 的校验流程(最常用,JWT 为例)

这是目前前后端分离项目的标准做法。

流程图:

用户登录 -> 服务端验证账号密码 -> 生成JWT并签名 -> 返回给客户端 -> 客户端存储(localStorage/Header) -> 每次请求携带Token(Authorization: Bearer <token>) -> 服务端拦截器/中间件校验Token签名和有效期 -> 解析出用户ID和角色 -> 进入业务逻辑前进行权限判断 -> 通过则执行,失败则返回401/403

核心校验代码实现(Node.js Express 中间件为例):

const jwt = require('jsonwebtoken');
// 1. Token 验证中间件(身份认证)
function authenticateToken(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
  if (token == null) {
    return res.status(401).json({ error: '未提供认证令牌' }); // 401 Unauthorized
  }
  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) {
      // 常见错误:Token过期、签名不匹配、Token被篡改
      if (err.name === 'TokenExpiredError') {
        return res.status(401).json({ error: '令牌已过期' });
      }
      return res.status(403).json({ error: '无效的令牌' }); // 403 Forbidden
    }
    // 将解析出的用户信息挂载到请求对象上,供后续使用
    req.user = user;
    next(); // 继续执行后续中间件或路由
  });
}
// 2. 权限校验中间件(授权)
function requireRole(role) {
  return (req, res, next) => {
    // 这里假设Token中包含了用户角色信息,由authenticateToken解析后的user对象提供
    if (!req.user || req.user.role !== role) {
      return res.status(403).json({ error: `需要[${role}]角色权限` }); // 403 Forbidden
    }
    next();
  };
}
// 3. 在路由中使用
// 公开接口:无需校验
app.get('/api/public', (req, res) => { res.send('公开数据'); });
// 需登录接口:只校验Token
app.get('/api/profile', authenticateToken, (req, res) => {
  res.send(`你好, ${req.user.name}`);
});
// 管理员接口:校验Token + 校验角色
app.delete('/api/users/:id', authenticateToken, requireRole('admin'), (req, res) => {
  // 只有admin才能删除用户
  res.send('用户已删除');
});

状态码规范:

  • 401 Unauthorized:未提供Token或Token无效/过期,客户端应重新登录。
  • 403 Forbidden:已认证但无权限,客户端应提示无访问权限,而不是重新登录。

基于 Session/Cookie 的校验(传统服务端渲染)

流程: 用户登录 -> 服务端创建Session并存储(内存/Redis) -> 下发Session ID到Cookie -> 客户端请求自动携带Cookie -> 服务端中间件根据Cookie找Session -> 判断是否登录 -> 判断角色权限。

关键点: Session ID 必须随机且足够复杂(防猜测),使用 HttpOnlySecureSameSite 属性保护 Cookie。


三种常见的未授权漏洞及防范

IDOR(不安全的直接对象引用)

  • 漏洞示例:访问 /api/order/12345 就能看到别人的订单。
  • 根因:只校验了“是否登录”,没校验“这个订单是否属于当前用户”。
  • 防范:必须校验资源归属,在代码中增加 if (req.user.id !== order.userId) 的判断。

未校验用户状态(如“权限变更”)

  • 漏洞示例:用户登录后,管理员后台将其降权或封禁,但该用户当前有效的Token仍能访问管理员API,直到Token过期。
  • 防范
    • 短Token + 刷新机制(推荐):Token过期时间短(15分钟),每次用Refresh Token刷新,每次刷新时检查用户最新状态。
    • 黑名单:在Redis中维护一个Token黑名单,每次请求查询。

缺少对“操作”类型的校验(垂直越权)

  • 漏洞示例:普通用户通过修改请求方法(如GET改DELETE)或参数来访问管理接口。
  • 防范:明确接口的HTTP方法(GET/查询、POST/创建、DELETE/删除)和角色要求。

更好的实践方案

  1. 统一校验框架:不把校验逻辑散落在每个控制器里,而是使用拦截器(Interceptor)或中间件统一处理。
  2. 白名单机制:默认所有接口都是“需要认证”的,只有显式声明的公开接口才放行。
  3. 最小权限原则:用户/服务只被授予完成其任务所需的最小权限,上传文件的接口应只校验文件类型,而不能同时允许删除服务器文件。
  4. 日志与监控:记录所有未授权访问尝试(特别是403),分析模式以发现可能的攻击行为。
  5. 安全第三方库:使用成熟框架和库,Spring Security(Java)、ASP.NET Core Identity、Passport.js(Node)、Django REST Framework(Python)。

总结一张表

校验层面 检测点 返回码 典型处理
是否携带凭证 Header有无Token,Cookie有无Session ID 401 提示“请先登录”
凭证是否有效 Token签名、是否过期、是否被篡改 401 提示“登录已过期,请重新登录”
凭证对应的身份是否被禁用 用户状态(是否被踢下线、封号) 403 提示“您的账号已被禁用”
是否有权执行该操作 角色是否匹配、资源是否属于自己 403 提示“您没有权限执行此操作”

最后记住两点:

  1. 永远不要在客户端做权限校验。(不能让前端隐藏一个按钮就以为后端安全了)
  2. 所有未授权校验失败,最终都要落到两个状态码上:401表示“你是谁”有问题,403表示“你能做什么”有问题。

抱歉,评论功能暂时关闭!