Sửa lỗi bảo mật File Access Vulnerability trong Ruby on Rails
Lỗi File Access Vulnerability là gì? Lỗi File Access Vulnerability là một lỗi bảo mật cho phép kẻ tấn công có thể sử dụng các lời gọi để thực hiện thêm, sửa hoặc xóa file trên server hoặc trên hệ thống file mà app đang sử dụng (ví dụ như S3) mà đáng ra họ không có quyền xử lý đến. Dưới đây là một ...
Lỗi File Access Vulnerability là gì?
Lỗi File Access Vulnerability là một lỗi bảo mật cho phép kẻ tấn công có thể sử dụng các lời gọi để thực hiện thêm, sửa hoặc xóa file trên server hoặc trên hệ thống file mà app đang sử dụng (ví dụ như S3) mà đáng ra họ không có quyền xử lý đến. Dưới đây là một ví dụ về một lời gọi có thể cho phép kẻ tấn công có thể kết nối đến file dữ liệu trong thư mục public của server Rails:
# http://domain.com?payload=config/database.yml payload = params[:payload] path = Rails.root.join(payload) id = SecureRandom.uuid File.link(path, "public/#{id}") redirect_to "/#{id}"
Mặc dù trông không có gì khả nghi, nhưng đoạn code trên là một ví dụ hoàn hảo cho kiểu tấn công File Access. Kẻ tấn công có thể lợi dụng đoạn code này để kết nối đến một file mà ta không muốn để lộ ra.
Vấn đề khó khăn ở đây là có một số lượng lớn các method có thể bị lợi dụng để thực hiện tấn công File Access. Dựa trên source code của brakeman, ta có thể liệt kê một danh sách các method nguy hiểm sau:
# Dir: Dir[] Dir.chdir Dir.chroot Dir.delete Dir.entries Dir.foreach Dir.glob Dir.new Dir.open Dir.rmdir Dir.unlink # File File.delete File.foreach File.lchmod File.lchown File.link File.new File.open File.read File.readlines File.rename File.symlink File.sysopen File.truncate File.unlink # FileUtils FileUtils.cd FileUtils.chdir FileUtils.chmod FileUtils.chmod_R FileUtils.chown FileUtils.chown_R FileUtils.cmp FileUtils.compare_file FileUtils.compare_stream FileUtils.copy FileUtils.copy_entry FileUtils.copy_file FileUtils.copy_stream FileUtils.cp FileUtils.cp_r FileUtils.getwd FileUtils.install FileUtils.link FileUtils.ln FileUtils.ln_s FileUtils.ln_sf FileUtils.makedirs FileUtils.mkdir FileUtils.mkdir_p FileUtils.mkpath FileUtils.move FileUtils.mv FileUtils.pwd FileUtils.remove FileUtils.remove_dir FileUtils.remove_entry FileUtils.remove_entry_secure FileUtils.remove_file FileUtils.rm FileUtils.rm_f FileUtils.rm_r FileUtils.rm_rf FileUtils.rmdir FileUtils.rmtree FileUtils.safe_unlink FileUtils.symlink FileUtils.touch # IO IO.foreach IO.new IO.open IO.read IO.readlines IO.sysopen # Kernel Kernel.load Kernel.open Kernel.readlines # Net::FTP Net::FTP.new Net::FTP.open # Net::HTTP Net::HTTP.new # PStore PStore.new # Pathname Pathname.glob Pathname.new # Shell Shell.new # YAML YAML.load_file YAML.parse_file
Sử dụng một method trong danh sách dài đến đáng sợ phía trên cho user input cũng đông nghĩa với việc mở ra một lỗ hổng để tấn công vào hệ thống.
Để dễ dàng theo dõi, ta phân loại các nhóm lệnh dựa trên khả năng tấn công của chúng vào hệ thống. Và độ nguy hiểm của từng loại tấn công cũng khác nhau:
STT | Phương thức tấn công | Tên method |
---|---|---|
1 | Làm đầy dung lượng | FileUtils.copy, FileUtils.cp, File.new, IO.new, PStore.new |
2 | Di chuyển file đến một địa chỉ cho phép download | File.rename, FileUtils.move |
3 | Kết nối file đến một link cho phép download | File.link, File.symlink, FileUtils.link, FileUtils.ln |
4 | Xóa file (DoS) | Dir.delete, FileUtils.rm |
5 | Thay đổi quyền truy cập file (DoS) | File.chmod, File.chown, FileUtils.chmod, FileUtils.chown |
6 | Đổi tên file | File.rename, FileUtils.move, FileUtils.mv |
7 | Làm lộ file path | FileUtils.pwd |
8 | Tải file lạ vào server | Net::FTP.new, Net::HTTP.new |
9 | Tấn công website khác | Net::FTP.new, Net::HTTP.new |
Làm sao đến sửa lỗi File Access Vulnerability?
Cách đúng nhất, hiệu quả nhất để chống lại File Access Vulnerability là không cho nó xuất hiện ngay từ đầu và hạn chế mọi hoạt động không cần thiết ở mức độ hệ thống.
Hết bài! Bạn có thể ngừng đọc ở đây!
Nếu vẫn tiếp tục đọc thì có thể bạn đã nhận ra rằng lời khuyên phía trên mặc dù rất đúng, nhưng nó hoàn toàn vô nghĩa và vô dụng trong các trường hợp thực tế. Thay vào đó, ta phải áp dụng các kỹ thuật khác vào để chống lại tấn công File Access trong khi làm việc với các file trong hệ thống.
Sử dụng Identifier
Cách đầu tiên có thể sử dụng là dùng một Identifier để trỏ đến một file trong ổ đĩa. Việc này chỉ đơn giản là cấp cho file một ID, hash hoặc GUID.
# HTML <select name="file_guid"> <option value="690e1597-de8d-4912-ac04-d0e626f806f4">file1.log</option> <option value="2e157fa3-ea1e-4b46-931e-c0f8b10bfcb2">file2.log</option> <option value="fffb938b-07bc-472c-a48f-383123a9f04d">file3.log</option> </select> # Controller download = FileDownload.find_by(file_guid: params[:file_guid]) send_file(download.path, filename: download.name, type: "text/plain")
Ở đoạn code trên ta gửi lên server một mã GUID, chứ không phải là tên file thật. Điều này sẽ khiến kẻ tấn công không thể tải file không được phép, và không sợ thao tác thay đổi tên file hay đường dẫn. Kỹ thuật này có thể dùng tốt trong việc di chuyển, sửa, đổi tên, xóa và gửi file trong trường hợp biết trước được tên file và đường dẫn. Đây là cách tốt nhất để bảo vệ hệ thống.
Hạn chế một phần
Về lý tưởng thì ta không cần đến bất cứ kỹ thuật nào khác để bảo vệ ngoài Identifier, nhưng đời không như là mơ. Đôi khi ta không có đủ thông tin cần thiết để sử dụng Identifier, trong trường hợp này ta vẫn phải cho user một sự tự do nhất định và hạn chế tối đa quyền truy cập vào các file hệ thống:
# HTML <select name="file_name"> <option value="file1.log">file1.log</option> <option value="file2.log">file2.log</option> <option value="file3.log">file3.log</option> </select> # Controller file_name = sanitize(params[:file_name]) # if possible current_user.download_directory should be an identifier # and controlled 100% by the server. download_path = "downloads/#{current_user.download_directory}/#{file_name}" if File.exists?(download_path) send_file download_path, filename: file_name, type: "text/plain" else # return an error message end
Ở đây ta có thể dùng thêm hàm sanitize để "làm sạch" param[:file_name] khỏi những ký tự nguy hiểm. Bằng cách này ta có thể kiểm soát được các truy cập vào file hệ thống.
Sử dụng filter
Kỹ thuật tiếp theo để hạn chế lỗi File Access là chỉ chấp nhận một số loại file nhất định. Ta phải tạo một "whitelist" gồm các kiểu file mà user được phép truy cập, ví dụ như chỉ chấp nhận file .pdf ở ví dụ dưới đây:
payload = sanitize(params[:filename]) if payload =~ /.pdf$/ send_file("downloads/#{payload}", filename: 'report.pdf', type: "application/pdf") else raise "Unknown file format requested" end
Hàng phòng ngự này giúp ta không bị lộ ra những thông tin nhạy cảm như file database.yml. Và tất nhiên là ta phải sử dụng cùng với hàm sanitize để bảo vệ.
Việc giới hạn các kiểu file có thể bị vượt qua nếu kẻ tấn công có khả năng di chuyển hoặc đổi tên file. Ví dụ như họ có thể thêm một phần mở rộng .pdf vào file database.yml thì họ có thể tải về file database.yml.pdf mà không gặp cản trở gì.
Lưu file của user ở một server khác
Vào thời đại này thì dung lượng lưu trữ rất rẻ. Một cách khá tốt để bảo vệ hệ thống là giới hạn những dữ liệu được quyền lưu trên server. Thay vào đó ta sử dụng những công cụ như Amazon S3, DreamHost’s Dream Objects để lưu trữ file, tạo report... trên server bên ngoài được kết nối đến server chính.
Tất nhiên sử dụng phương pháp này bạn vẫn có thể tự bắn vào chân mình và trao quyền truy cập cho kẻ tấn công vào những file được lưu trữ bên ngoài. Việc lưu trữ file bên ngoài chỉ đơn giản là tạo ra một ranh giới giữa các file của hệ thống và của user, nhờ vậy mà việc tác động vào các file trong lưu trữ không ảnh hưởng đến web server.
Điều này có lợi ích thêm là việc này giúp giảm tải cho application server mà cắt bớt các thủ tục xử lý file.
Hạn chế sử dụng các method nguy hiểm
Có một tỉ lệ không nhỏ là những kỹ thuật kể trên, và cả những kỹ thuật thần thánh mà bạn tìm được đâu đó trên mạng, không thể áp dụng được vào trường hợp của bạn. Vào những lúc đó thì ta buộc phải quay lại với lời khuyên hàng đầu là không sử dụng những method nguy hiểm để bảo vệ cho app.
Khi nhận được yêu cầu làm một feature liên quan đến file access, bạn có thể feedback bằng một bản báo cáo đầy đủ những lỗi tiềm tàng mà feature mới đó có thể đem lại cho hệ thống.