Ruby on Rails Security: Best Practices
Framework được thiết kế với mục đích chính là để giúp các web developer xây dựng các ứng dụng web. Mặc dù có rất nhiều Framework có sẵn nhưng khản năng phát hiện ra các lỗ hổng như CSRF hoặc giả mạo nội dung (chúng ta không nói về XML Entity và Paramater Pollution) là rất cao. Mặc dù, một số ...
Framework được thiết kế với mục đích chính là để giúp các web developer xây dựng các ứng dụng web. Mặc dù có rất nhiều Framework có sẵn nhưng khản năng phát hiện ra các lỗ hổng như CSRF hoặc giả mạo nội dung (chúng ta không nói về XML Entity và Paramater Pollution) là rất cao.
Mặc dù, một số framework giúp các developer bảo vệ ứng dụng web, nhưng các developer không nên quá dựa dẩm vào các tính năng bảo mật được xây dựng trong các framework, để đảm bảo ứng dụng không bị truy cập trái phép. Thành thật mà nói, sự đảm bảo an toàn cho một ứng dụng web là một quá trình liên tục. Do đó, không có cơ chế bảo vệ nào đủ tốt để tạo ra một ứng dụng bất khả xâm phạm.
Bài viết này nói về các trường hợp thực tế có thể xảy ra cùng với một số tính năng tích hợp sẵn trong Ruby on Rails và cũng giải thích tại sao chúng ta không nên chỉ dựa dẫm vào các tính năng bảo mật được xây dựng sẵn. Những hướng dẫn này sẽ chỉ ra cho các developer có thể sử dụng gem trong Rails giống như secure_headers của Twitter để giảm thiểu rủi ro. Bài viết này cũng chỉ ra các cách tấn công vào các ứng dụng Ruby và cách để các Webmaster có thể giảm thiểu việc chiếm quyền điều khiển session và chèn SQL.
Phần cuối cùng liên kết đến resource bảo mật trong Ruby on Rails và các blog để giúp các developer nâng cao kiến thức của họ liên quan đến bảo mật trong ứng dụng Ruby.
Tổng quan về Ruby on Rails
Ruby on rails đơn giản chỉ là một web framework để xây dựng các ứng dụng web trong ruby và framework này giúp các web developer bảo vệ ứng dụng Ruby. Hơn nữa, nó làm cho các web developer bớt căng thẳng hơn vì nó cho phép các developer viết code ngắn gọn hơn. Cuối cùng, Rails có quy ước trong việc cấu hình, nó bao gồm các nguyên tắc hướng dẫn sau:
Nguyên tắc DRY
Nguyên tắc Don’t Repeat Yourself (DRY) chỉ đơn giản là các developer không nên viết lặp đi lặp lại nhiều lần cùng một đoạn code, như vậy sẽ dễ dàng hơn cho việc bảo trì (maintain).
Quy ước về cấu hình
Nguyên tắc này cho chúng ta biết rằng Rails chỉ đơn giản là một opinionated framework. Rails cho rằng các tốt nhất để làm nhiều thứ là mặc định theo quy ước của rails chứ không phải là truy cập vào các file cấu hình.
Tổng quan về tích hợp các tính năng bảo mật trong Ruby on Rails
Trước tiên, Ruby on Rails được bảo mật theo mặc định. Nhưng như đã nói ở trên, dựa hoàn toàn vào các tính năng được xây dựng sẵn là không nên.
Ví dụ, mặc dù Framework cung cấp Active Record, một thư viện cung cấp sự truy cập cơ sở dữ liệu trừu tượng, một vài query method và một vài tùy chọn như Caculation Method, Delete All Method, Destroy All Method, Exists? Method, Joins Method, Order Method,....nhưng lại không làm sạch hoặc kiểm tra các raw SQL argument. Những method này có thể dễ dàng bị tấn công SQL injection. Hãy cẩn thận khi dùng những method này.
Mặc dù, một vài method trong Ruby on Rails có sanitize đối với raw SQL argument thì các web developer vẫn có thể tối ưu các helper method thông minh của Ruby on Rails để giảm thiểu tấn công SQL injection. Cũng có các gem có thể thực hiện tốt hơn các tính năng được xây dựng trong Rails. Mặc dù Framework có thể giúp các developer xây dựng các ứng dụng bảo mật, nhưng bảo mật không phải là plug-and-play. Bảo mật phụ thuộc vào các developer và các phương pháp phát triển được sử dụng.
1. Model View Controller (MVC)
Cross-site scripting được khai thác khi trang Web không làm sạch input (có thể là HTML, javascript hoặc VBScript) từ những người dùng. Trong Rails, rất dễ dàng để làm sạch input của người dùng với Model Controller View bởi bất kỳ dữ liệu nào lấy ra hoặc lưu trữ đều phải truyền qua model.
Chúng ta có thể làm sạch input hoặc output trong view của chúng ta bằng cách sử dụng phương thức sanitize. Phương thức sanitize loại bỏ các element và attribute nguy hiểm từ nội dung html.
Giả sử chúng ta có các XSS payloads phổ biến, chẳng hạn như:
<img src=x onerror=prompt(1)> <img/src="%00" onerror=this.onerror=confirm(1) <img src="%00"&Newline; onerror=alert(1)

Khi chúng ta gọi đến sanitize
<%= sanitize "img src=x onerror=prompt(1)>" %>
Theo mặc định, Rails chỉ chấp nhận img src=x và có thể loại bỏ các thuộc tính sự kiện. Do đó, kết quả đầu ra sẽ chỉ là:
<img src="x">
2. html_escape() hoặc h()
Trong Rails, html_escape() hoặc h() - một alias của html_escape() - là một phương thức để escaping và mã hóa các ký tự thẻ HTML. Đây là phương thức mã hóa chính thức trong Rails. Đối với những chuỗi mã hóa giống url, bạn có thể sử dụng url_encode(s) hoặc u(s).
Trong ví dụ đầu tiên, ví dụ về XSS payload sử dụng html_escape()
<%= html_escape '<img src=x onerror=prompt(1)>' %>
html_escape sẽ tạo ra một đầu ra như sau:
< ; img src=x onerror=prompt(1)> ;
Phương thức html_escape đã chuyển các ký tự < và > thành các thực thể HTML.
3. Authencity_token
Rails bảo vệ ứng dụng chống lại CSRF-Cross Site Request Forgery bằng cách gắn kèm một mã thông báo có tên là authencity_token trong các phản hồi của HTML. Mã thông báo này được lưu trữ trong session cookie của người dùng. Một session được tạo thành bởi một bảng băm các giá trị và các session ID. Session được nhúng hoặc đính kèm trong cookie. Do đó mọi cookie được gửi đến trình duyệt của người dùng bao gồm session ID - thường là chuỗi 32 ký tự.
Trong Rails, bạn có thể dùng các phương thức sau để lưu và lấy các giá trị:
session[:user_id] = @current_user.id User.find session[:user_id]
Các gem bảo mật trong Ruby on Rails
Mặc dù Rails cung cấp khá nhiều ứng dụng tích hợp sẵn để bảo vệ chống lại các lỗ hổng xác thực đầu vào và các cuộc tấn công dựa trên các web khác, nhưng các cơ chế bảo mật này không thể vượt quá một giới hạn nhất định - chúng bị hạn chế. Cá nhân tôi, tôi không tin vào các tính năng bảo mật được xây dựng sẵn của Rails để bảo vệ các ứng dụng của Ruby. Tôi thích thực hiện các cơ chế bảo mật đa dạng để giảm thiểu việc chiếm quyền kiểm soát session, cross-site request forgery và SQL injection thông qua các gem trong Rails.
Chúng ta hãy cùng tìm hiểu một số gem bảo mật phổ biến nhất có thể bảo vệ các ứng dụng web khỏi chiếm quyền kiểm soát session, cross-site request forget và SQL injection.
Devise
Đây là chứng thực phổ biến cho các ứng dụng Rails. Nó cung cấp một số tính năng như lưu trữ mật khẩu an toàn bằng các sử dụng bcrypt, đăng ký người dùng và quên mật khẩu.
Một số phương thức dùng trong devise
- authenticate_user! phương thức được sử dụng cho các ứng dụng, yêu cầu người dùng phải đăng nhập để có thể truy cập các trang của họ.
- current_user! phương thức chỉ đơn giản trả về đối tượng liên quan đến người dùng đã đăng nhập. Nếu người dùng chưa đăng nhập nó sẽ trả về nil.
- user_signed_in? phương thức truy vấn này chỉ kiểm tra nếu phương thức current_user trả về một giá trị không phải nil.
- sign_in(@user) - phương thức này cho phép người dùng đăng nhập vào một tài khoản mới được tạo.
- sign_out (@user) - phương thức này cho phép người dùng đăng xuất thành công
- user_session - phương thức này trả về một session khi một người dùng đăng nhập
secure_headers
Được phát triển bởi đội bảo mật của Twitter. Đây là một gem thêm các HTTP header liên quan đến bảo mật vào các response HTTP của ứng dụng. secure_headers của Tiwtter bao gồm các header bảo mật, chẳng hạn như chính sách bảo mật nội dung, bảo vệ chống lại cross-site request forgery và HSTPS để hạn chế browser giao tiếp với một máy chủ từ xa chỉ thông qua https.
Gem này sẽ tự động thêm vào các header bảo mật trong các response HTTP của ứng dụng. Dưới đây là một vài các header bảo mật của secure_headers:
- Content Security Policy: header bảo mật này giúp ngăn chặn các cuộc tấn công cross-site scripting chống lại các ứng dụng.
- HTTP Strict Transport Security: cho phép browser giao tiếp với một máy chủ từ xa chỉ thông qua https
- Ngăn chặn nội dung từ frame và có khản năng click-jacked
Bây giờ, chúng ta sẽ tìm hiểu cách một kẻ tấn công thông minh có thể chiếm quyền kiểm soát session, chèn các SQL độc hại và thực hiện giả mạo yêu cầu cross-site trong Rails.
Ngăn chặn chiếm quyền kiểm soát session
Cuộc tấn công chiếm quyền kiểm soát xảy ra khi kẻ tấn công ăn cắp session ID của người dùng hợp pháp để đăng nhập vào ứng dụng Web thông qua tên của nạn nhân. Hình thức tấn công này vẫn có thể xảy ra dù người dùng có kết nối hoặc yêu cầu HTTP tới máy chủ web từ xa qua HTTP hoặc HTTPS. Sự khác biệt duy nhất giữa HTTP và HTTPS là kết nối HTTPS được thiết lập thêm tại thời điểm bắt đầu. Nó đàm phán một kênh an toàn và sau đó sẽ gửi HTTP một cách bình thường qua kênh đó.
Khi người dùng đăng nhập vào một ứng dụng Web, một user ID được lưu để xác thực trong tương lai thông qua session token. Cụ thể là:
session[:user_id] = user
Những kẻ tấn công có thể chiếm các session theo nhiều cách khác nhau như dự đoán session token, tấn công man-in-the-middle hoặc là XSS nổi tiếng.
Chúng ta có thể bảo vệ các cookie của chúng ta bằng cách sử dụng các giá trị ngẫu nhiên. Giả sử kẻ tấn công đăng nhập vào ứng dụng Web, họ có thể dễ dàng dự đoán các cookie tiếp theo hoặc trước đó bằng cách quan sát các ID trước đó. Do đó, ID phải là ngẫu nhiên.
Hơn nữa, các developer web không nên lưu trữ các ID theo các cách sau:
cookies[:secure_session]
Thay vào đó, web developer nên gọi phương thức signed trên cookie để mã hóa giá trị giống như thế này:
cookies.signed[: secure_session]
Mặc dù session ID không thể đoán trước, chúng ta vẫn cần đảm bao cho cái cách mà client tạo ra các request HTTP đến các máy chủ web từ xa. Không có SSL, cookie có thể bị chặn khi chuyển tiếp. Trong Rails có một số cách thực hiện SSL.
Phương pháp tốt nhất để đảm bảo kết nối an toàn cho mọi request HTTP bằng cách thêm dòng sau vào tệp config/application.rb, làm như vậy đảm bảo rằng các cookie truyền đi theo cách mã hóa:
(config.force_ssl = true)
Tấn công Cross-Site Request Forgery và cách phòng tránh:
Trong việc đánh cắp session, cross-site script là một phương pháp được sử dụng bởi kẻ tấn công để ăn cắp cookie và sửa đổi session ID. Không giống như XSS, CSRF, phương pháp này không ăn cắp cookie để đăng nhập qua tên người dùng.
Giả sử có một cuộc tấn công Cross-Site Request Forgery (CSRF): Khi người dùng đăng nhập vào trang www.mybank.com và trang này đang bị tấn công CSRF. Khi người dùng nhấn một đường link có chứa mã độc thì một số tiền của người dùng tự động chuyển khoản sang tài khoản khác. Và mã độc sẽ có dạng như sau:
<iframe src="http://examplebank.com/app/transfermoney? amount=2200&attackersAccount">
Khi người dùng load iframe, trình duyệt sẽ tạo một request POST tới www.mybank.com để xử lý chuyển một số tiền vào tài khoản số: 247890345
Trong Rails chúng ta có ngăn chặn CSRF bằng authenticity_token trong các HTML response. Token này cũng được lưu trong session cookie của người dùng. Form trong rails có thể chứa đoạn code sau:
<input Name= "authenticity_token" type= "hidden" value="ghtyu7asdvnTojibBNYY67BshjyerUA+81+ DD="/>
Khi mà form và authenticity_token được submit, Rails sẽ xác minh request và quyết định request có được xử lý hay không.
Tấn công SQL Injection và cách ngăn chặn
SQL Injection là một phương pháp nổi tiếng được sử dụng bởi kẻ tấn công đê truy cập vào cơ sở dữ liệu của một trang Web. SQL Injection cho phép kẻ tấn công bỏ qua các đăng nhập, chuyển các tập tin/thư mục quan trọng vào hộp thư của kẻ tấn công và các sự kiện độc hại khác.
Giả sử ké tấn công muốn thực hiện một lệnh SQL Injection trên www.example.com. Kẻ tấn công có thể kiểm tra trang web có dễ bị SQL Injection bằng đoạn code sau đây:
http://www.example.com/users?id=2'
Khi web server thông báo có một lỗi xuất hiện hoặc thông báo có lỗi cú pháp trong câu SQL của chúng ta thì điều đó có nghĩa là trang web www.example.com có thể dễ dàng bị SQL Injection.
Trong Rails, các ứng dụng tương tác với cơ sở dữ liệu thông qua activerecord, một object-relational mapping (ORM) mà theo mặc định đi kèm với Rails. Mặc dù ORM cung cấp cơ sở dữ liệu trừu tượng, nhưng nếu xử lý không cẩn thận đầu vào có thể dẫn đến SQL Injection.
Ban đầu tôi khuyên các Web developer nên cẩn thận khi sử dụng Activerecord trong Rails. Bạn có thể không đồng ý với tôi, nhưng bạn có thể kiểm ta ActiveRecord::FinderMethods#exists? để hiểu được tại sao nó là tai họa khi sử dụng Activerecord chống lại SQL Injection.
Kết luận
Bài viết này mình có dịch từ bài viết
https://www.codementor.io/ruby-on-rails/tutorial/ruby-on-rails-security-best-practices
Và mình có tham khảo thêm một số nguồn như:
- Rails SQL Injection
- Ruby on Rails Security Guide
- RequestForgeryProtection
- SanitizeHelper
- html_escape(s)
Mình rất mong nhận được sự đóng góp của các bạn để bài viết có thể hoàn thiện hơn