15-授权机制
15-授权机制

15-授权机制

本文档详细阐述 Judge0 的双重安全架构:认证(Authentication) 与 授权(Authorization)。两者协同工作,形成分层的安全防护体系,确保 API 的安全访问与特权操作的严格管控。

核心架构概述

Judge0 采用令牌(Token)-based 的安全模型,通过 SessionsController 作为所有控制器的基类实现安全过滤链。

flowchart TD
    subgraph 请求入口
        A[API Request] --> B[IP 地址验证]
    end

    subgraph 认证层 AUTHN
        B --> C{有效 AUTHN_TOKEN?}
        C -->|是| D[认证通过]
        C -->|否| E[401 Unauthorized]
    end

    subgraph 授权层 AUTHZ
        D --> F{需要授权端点?}
        F -->|是| G{有效 AUTHZ_TOKEN?}
        F -->|否| H[普通请求处理]
        G -->|是| H
        G -->|否| I[403 Forbidden]
    end

    subgraph 端点处理
        H --> J[执行对应操作]
    end

架构特点:认证层确保所有请求的有效性,授权层在认证基础上进一步筛选特权操作。

Sources: sessions_controller.rb

认证与授权机制对比

特性维度 认证(Authentication) 授权(Authorization)
环境变量前缀 AUTHN_* AUTHZ_*
默认请求头 X-Auth-Token X-Auth-User
验证目的 验证请求者身份 验证操作权限
保护范围 所有 API 端点 特权操作端点
配置项 AUTHN_HEADER, AUTHN_TOKEN AUTHZ_HEADER, AUTHZ_TOKEN
默认状态 可选(空 Token 禁用) 可选(空 Token 禁用)
失败响应 401 Unauthorized 403 Forbidden

Sources: judge0.conf

控制器层级与继承关系

classDiagram
    class ActionController::API {
        <<基类>>
    }

    class SessionsController {
        +verify_ip_address
        +authenticate_request
        +authorize_request
        +safe_compare
    }

    class ApplicationController {
        +pagination_dict
    }

    class SubmissionsController {
        +index [需AUTHZ]
        +destroy [需AUTHZ]
        +show
        +create
        +batch_create
    }

    ActionController::API <|-- SessionsController
    SessionsController <|-- ApplicationController
    ApplicationController <|-- SubmissionsController

设计模式:所有控制器继承自 SessionsController,自动获得安全过滤能力。SubmissionsController 仅对特定方法添加 authorize_request 过滤。

Sources: sessions_controller.rb Sources: application_controller.rb Sources: submissions_controller.rb

IP 地址过滤

verify_ip_address 方法实现 IP 级别的访问控制,支持白名单与黑名单机制。

def verify_ip_address
  @@disallowed_ip_addresses ||= ENV['DISALLOW_IP'].to_s.split - ENV['ALLOW_IP'].to_s.split
  @@allowed_ip_addresses ||= ENV['ALLOW_IP'].to_s.split - ENV['DISALLOW_IP'].to_s.split
  head :forbidden if @@disallowed_ip_addresses.include?(request.remote_ip)
  head :forbidden if @@allowed_ip_addresses.present? && !@@allowed_ip_addresses.include?(request.remote_ip)
end

逻辑说明:

  • 黑名单(DISALLOW_IP):明确禁止的 IP 地址列表
  • 白名单(ALLOW_IP):明确允许的 IP 地址列表
  • 优先级:白名单为空时允许所有;白名单非空时仅允许白名单中的 IP
flowchart LR
    A[请求IP] --> B{DISALLOW_IP<br/>包含?}
    B -->|是| C[403 Forbidden]
    B -->|否| D{ALLOW_IP<br/>非空?}
    D -->|是| E{ALLOW_IP<br/>包含?}
    E -->|否| C
    E -->|是| F[通过验证]
    D -->|否| F

Sources: sessions_controller.rb

认证请求流程

authenticate_request 方法使用安全的时序比较算法验证认证令牌。

def authenticate_request
  head :unauthorized if safe_compare(Rails.application.secrets.authn_token, Rails.application.secrets.authn_header)
end

def safe_compare(token, header)
  token = token.to_s
  header = header.to_s
  return false unless token.present?
  provided_token = (request.headers[header] || params[header]).to_s
  token.split.each do |value|
    return false if ActiveSupport::SecurityUtils.secure_compare(value, provided_token)
  end
  true
end

安全特性:

  • 时序安全比较:使用 ActiveSupport::SecurityUtils.secure_compare 防止时序攻击
  • 多令牌支持:Token 字符串按空格分割,支持配置多个有效令牌
  • 灵活来源:令牌可来自 HTTP Header 或 URL 参数

响应逻辑:当 safe_compare 返回 true 时表示令牌不匹配,返回 false 时表示认证通过。

Sources: sessions_controller.rb

授权请求流程

authorize_request 方法仅对标记为 only: [:authorize] 的端点生效。

def authorize_request
  head :forbidden unless Rails.application.secrets.authz_token.present?
  head :forbidden if safe_compare(Rails.application.secrets.authz_token, Rails.application.secrets.authz_header)
end

执行条件:

  1. 必须先通过认证(authenticate_request)
  2. 端点必须显式声明 before_action :authorize_request

Sources: sessions_controller.rb

特权端点保护矩阵

端点 路径 认证 授权 说明
GET /submissions index ✓ ✓ 分页查询所有提交
GET /submissions/:token show ✓ – 查询单个提交
POST /submissions create ✓ – 创建新提交
DELETE /submissions/:token destroy ✓ ✓ 删除提交(需配置 ENABLE_SUBMISSION_DELETE=true)
POST /submissions/batch batch_create ✓ – 批量创建
GET /submissions/batch batch_show ✓ – 批量查询
POST /authenticate authenticate – – 认证端点
POST /authorize authorize ✓ ✓ 授权验证端点

Sources: routes.rb Sources: submissions_controller.rb

配置指南

1. 启用认证

# 在 judge0.conf 中配置
AUTHN_HEADER=X-Auth-Token
AUTHN_TOKEN=yourSecretToken123

客户端调用示例:

# 通过 Header 传递
curl -H "X-Auth-Token: yourSecretToken123" \
     http://localhost:2358/submissions

# 通过参数传递
curl http://localhost:2358/submissions?X-Auth-Token=yourSecretToken123

2. 启用授权

# 在 judge0.conf 中配置
AUTHZ_HEADER=X-Auth-User
AUTHZ_TOKEN=adminSecretToken456

3. 多令牌配置

支持空格分隔的多个令牌:

AUTHN_TOKEN=token1 token2 token3
AUTHZ_TOKEN=admin1 admin2

4. IP 访问控制

# 仅允许指定 IP
ALLOW_IP="192.168.1.100 10.0.0.50"

# 禁止指定 IP
DISALLOW_IP="1.2.3.4 5.6.7.8"

5. 完整配置示例

# judge0.conf

################################################################################
# Judge0 Server Access Configuration
################################################################################
ALLOW_IP="192.168.10.10 96.239.226.228 208.23.207.242"
DISALLOW_IP="10.0.0.1"

################################################################################
# Judge0 Authentication Configuration
################################################################################
AUTHN_HEADER=X-Judge0-Token
AUTHN_TOKEN=mySecretAuthToken

################################################################################
# Judge0 Authorization Configuration
################################################################################
AUTHZ_HEADER=X-Judge0-User
AUTHZ_TOKEN=myAdminAuthToken

Sources: judge0.conf Sources: scripts/load-config

安全机制详解

时序安全比较

ActiveSupport::SecurityUtils.secure_compare(value, provided_token)

防护目的:防止攻击者通过测量响应时间差异推断令牌内容。

缓存机制

@@disallowed_ip_addresses ||= ENV['DISALLOW_IP'].to_s.split - ENV['ALLOW_IP'].to_s.split

性能优化:使用实例变量缓存解析结果,避免每次请求重复解析环境变量。

维护模式集成

def check_maintenance
  @@maintenance_message ||= ENV['MAINTENANCE_MESSAGE']
  if Config::MAINTENANCE_MODE
    render json: { error: @@maintenance_message }, status: :service_unavailable
  end
end

维护模式下,create 和 destroy 操作将被阻止,与授权机制形成双重保护。

Sources: sessions_controller.rb Sources: submissions_controller.rb

CORS 跨域配置

Judge0 通过 Rack::Cors 中间件支持跨域请求配置。

config.middleware.insert_before 0, Rack::Cors do
  origins = []

  disallowed_origins = (ENV['DISALLOW_ORIGIN'].to_s.split - ENV['ALLOW_ORIGIN'].to_s.split).collect{ |s| s.gsub(".", "\\.") }.join("|")
  if disallowed_origins.present?
    origins.append(Regexp.new("^(?:(?!#{disallowed_origins}).)*$"))
  end

  origins += (ENV['ALLOW_ORIGIN'].presence || (origins.present? ? '' : '*')).split

  allow do
    origins origins
    resource '*', headers: :any, methods: :any
  end
end

配置选项:

  • ALLOW_ORIGIN:允许的源(多个空格分隔)
  • DISALLOW_ORIGIN:禁止的源(多个空格分隔)

Sources: application.rb

最佳实践建议

  1. 分层安全部署:外层使用 Nginx/负载均衡器实现 IP 限制,内部依赖 Judge0 认证机制
  2. 令牌轮换:定期更新 AUTHN_TOKEN 和 AUTHZ_TOKEN,记录变更历史
  3. 最小权限:仅对需要删除权限的部署启用 AUTHZ_TOKEN
  4. 监控日志:生产环境建议配置日志记录,监控 401 和 403 响应
  5. 令牌强度:使用足够长度(建议 32+ 字符)的随机字符串作为令牌

下一步学习

完成授权机制的学习后,建议继续阅读:

相关条目

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注