class SessionsController < ApplicationController
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
# Vulnerable: User controls redirect destination
redirect_url = params[:redirect_url] || root_path
redirect_to redirect_url # Open redirect vulnerability
else
flash[:error] = 'Invalid credentials'
render :new
end
end
def logout
session.clear
# Vulnerable: External redirect after logout
return_url = params[:return_url]
redirect_to return_url || login_path
end
end
class ApplicationController < ActionController::Base
def redirect_after_action
# Vulnerable: Dynamic redirect without validation
next_page = request.referer || params[:next]
redirect_to next_page
end
end
class SessionsController < ApplicationController
ALLOWED_REDIRECT_HOSTS = [
'app.example.com',
'secure.example.com',
'api.example.com'
].freeze
def validate_redirect_url(url)
return root_path if url.blank?
begin
uri = URI.parse(url)
# Allow relative URLs
if uri.relative?
# Ensure it's a path, not a protocol-relative URL
return url if url.start_with?('/')
return "/#{url}" unless url.start_with?('//')
raise ArgumentError, 'Protocol-relative URLs not allowed'
end
# For absolute URLs, check host allowlist
if uri.absolute?
unless uri.scheme.in?(%w[http https])
raise ArgumentError, 'Invalid URL scheme'
end
unless ALLOWED_REDIRECT_HOSTS.include?(uri.host)
raise ArgumentError, 'Host not in allowlist'
end
return url
end
raise ArgumentError, 'Invalid URL format'
rescue URI::InvalidURIError
raise ArgumentError, 'Malformed URL'
end
end
def safe_redirect_to(url, fallback = root_path)
begin
safe_url = validate_redirect_url(url)
redirect_to safe_url
rescue ArgumentError => e
Rails.logger.warn "Redirect validation failed: #{e.message} for URL: #{url}"
redirect_to fallback
end
end
def create
user = User.authenticate(params[:email], params[:password])
if user
session[:user_id] = user.id
# Secure: Validate redirect URL
redirect_url = params[:redirect_url]
safe_redirect_to(redirect_url, dashboard_path)
else
flash[:error] = 'Invalid credentials'
render :new
end
end
def logout
session.clear
# Secure: Validate return URL
return_url = params[:return_url]
safe_redirect_to(return_url, login_path)
end
end
class ApplicationController < ActionController::Base
def redirect_after_action
# Secure: Use internal routes only
begin
# Check if next parameter is a valid internal route
next_page = params[:next]
if next_page.present?
# Try to recognize as internal route
Rails.application.routes.recognize_path(next_page)
redirect_to next_page
else
redirect_to root_path
end
rescue ActionController::RoutingError
# Invalid route, redirect to safe default
redirect_to root_path
rescue => e
Rails.logger.error "Redirect error: #{e.message}"
redirect_to root_path
end
end
# Alternative: Use permitted redirect paths
SAFE_REDIRECT_PATHS = [
'/dashboard',
'/profile',
'/settings',
'/help'
].freeze
def safe_internal_redirect
requested_path = params[:path]
if SAFE_REDIRECT_PATHS.include?(requested_path)
redirect_to requested_path
else
redirect_to root_path
end
end
end