flawopen.com/path-traversal/Ruby
How File.join in Ruby leaves relative paths unresolved, and how to verify boundaries using File.expand_path.
호텔 2층 객실만 열 수 있도록 프로그래밍된 도어락을 비유로 들어보겠습니다. 만약 투숙객이 키패드에 '../../master-safe'를 입력하면, 결함이 있는 잠금장치가 복도를 벗어나 지배인의 메인 금고 문을 열어버리는 현상입니다.
Web Application SecurityCWE-918.CWE-918CWE-918): Standard Common Weakness Enumeration classification for path-traversal-ruby.Defense-in-Depth엔드포인트가 HTTP 매개변수를 통해 사용자가 지정한 파일명이나 리소스 경로를 전달받습니다.
공격자가 파일명에 '../', '..%2f' 등의 상대 경로 또는 절대 경로 우회 문자열을 삽입합니다.
백엔드가 표준화(Canonical) 경로 검증 없이 기본 디렉토리와 문자열을 결합하여 경계를 이탈합니다.
서버가 /etc/passwd, 환경 변수 파일 등 민감한 시스템 파일을 읽어 공격자에게 반환합니다.
# 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