flawopen.com/path-traversal/Ruby

● CWE-918 · Высокий
Исследования · FlawOpen

Анализ уязвимости: Path Traversal in Ruby

How File.join in Ruby leaves relative paths unresolved, and how to verify boundaries using File.expand_path.

💡 Простыми словами (ELI5)

Представьте электронный замок в отеле, запрограммированный открывать только номера на втором этаже. Если гость вводит на панели '../../master-safe', уязвимый замок выходит в коридор и открывает главный сейф управляющего.

Ключевые понятия и термины

Web Application Security
Технический термин (Web Application Security): Core architecture component affected by CWE-918.
CWE-918
Технический термин (CWE-918): Standard Common Weakness Enumeration classification for path-traversal-ruby.
Defense-in-Depth
Технический термин (Defense-in-Depth): Multi-layered engineering verification and runtime boundary isolation.

Пошаговый сценарий атаки

Step 1

Передача пути к файлу клиентом

Конечная точка принимает имя файла или путь к документу через параметр HTTP-запроса.

Step 2

Внедрение последовательностей обхода каталогов

Злоумышленник передает последовательности '../', '..%2f' или абсолютные пути для выхода из базового каталога.

Step 3

Преодоление границ файловой системы

Бэкенд конкатенирует путь без канонизации и проверки того, что результирующий путь находится внутри базовой папки.

Step 4

Несанкционированное чтение системных файлов

Сервер считывает и передает клиенту конфиденциальные файлы операционной системы (например, /etc/passwd).

Исходный код: Уязвимый vs Защищённый вариант

✕ УЯЗВИМАЯ РЕАЛИЗАЦИЯ
# VULNERABLE: File.join does not resolve traversal dots
class DownloadsController < ApplicationController
  BASE_DIR = Rails.root.join('storage', 'public').to_s

  def show
    # Attacker input: "../../config/master.key"
    file_path = File.join(BASE_DIR, params[:file])
    
    # Leaks private Rails master encryption key!
    send_file file_path
  end
end
✓ БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
# HARDENED: Canonicalize with File.realpath and enforce prefix
class DownloadsController < ApplicationController
  BASE_DIR = File.realpath(Rails.root.join('storage', 'public').to_s)

  def show
    user_file = params[:file].to_s
    return head :bad_request if user_file.blank?

    # 1. Expand path relative to BASE_DIR
    target_path = File.expand_path(user_file, BASE_DIR)

    # 2. Strict boundary check with trailing separator
    allowed_prefix = BASE_DIR.end_with?('/') ? BASE_DIR : "#{BASE_DIR}/"
    unless target_path.start_with?(allowed_prefix)
      return render plain: 'Forbidden: Path Traversal detected', status: :forbidden
    end

    return head :not_found unless File.file?(target_path)

    send_file target_path
  end
end

Чек-лист по защите системы для инженеров

References