Nếu bạn giống như tôi, bạn bước vào nghề này vì yêu thích việc xây dựng các ứng dụng tuyệt vời. Nếu bạn đã đủ lâu trong lĩnh vực phát triển, cuối cùng bạn sẽ phải làm việc trên những ứng dụng tuyệt vời đó những thứ không cảm thấy tuyệt vời chút nào. Bảo mật có thể là một trong những thứ đó. Việc đối xử nghiêm túc với bảo mật Rails rất quan trọng, dù framework Rails đã gánh phần lớn công việc nặng nhọc.
Trước khi đi sâu vào chi tiết bảo mật Ruby on Rails, hãy dành chút thời gian suy ngẫm về những lúc vui vẻ.

... và bây giờ quay lại thực tế.
Thật tệ khi bạn phải lo lắng về các mối đe dọa bảo mật. Thật đáng ghét khi có những người thức dậy mỗi sáng và cố gắng phá vỡ những gì bạn đang xây dựng.
Nhưng những người này tồn tại. họ nỗ lực tìm lỗ hổng trong bảo mật của bạn. Và khi họ thành công, đó là lỗi của bạn.
Khách hàng của bạn không thể bảo vệ mã nguồn giúp bạn. Quản lý của bạn cũng không thể làm thay. Mọi thứ đều là trách nhiệm của bạn. Vì vậy bạn nhất định phải biết mình đang làm gì.
Tại sao bạn nên quan tâm đến bảo mật Rails?
- Bạn có cố gắng tuân thủ các thực hành tốt nhất về bảo mật nhưng không thực sự hiểu chúng?
- Bạn có mơ hồ về cách hoạt động của XSRF, MITM và XSS?
- Bạn có nghĩ rằng vì ứng dụng của mình nhỏ hoặc chỉ dùng nội bộ nên không bị rủi ro?
Nếu bất kỳ điều nào trên đúng, thì hướng dẫn này chắc chắn dành cho bạn. Bảo mật quan trọng đối với mọi ứng dụng, và việc bỏ qua đầu tư vào nó chỉ ổn cho đến khi nó không còn ổn nữa. Rails giúp giảm thiểu một số rủi ro bảo mật lớn nhất của ứng dụng web trở nên cực kỳ đơn giản, nên bạn sẽ ngu ngốc nếu không khép kín khoảng trống đó.
Bạn nghĩ ứng dụng của mình không phải là mục tiêu?
Bạn có lẽ đang sai. Nếu nó trên internet, nó đang bị vây hãm. Các hệ thống tự động đang quét tìm ứng dụng web dễ bị tấn công liên tục 24/7.
Dù là tội phạm có tổ chức hay bot, internet đầy rẫy những kẻ xấu muốn giành được dữ liệu người dùng của bạn.
Tội phạm có tổ chức là thực tế
Bạn có lưu thẻ tín dụng trong cơ sở dữ liệu? Hash mật khẩu không có salt? Tôi hy vọng là không! Nếu có, bạn đã xong. Rails giúp tránh những sai lầm như vậy một cách tương đối nhẹ nhàng.
Botnet sẽ khiến bạn sập

Khi vận hành botnet, càng nhiều bot thì càng nhiều tiền. Vì vậy bạn mua gói exploit, thiết lập nó để cài malware khi trình duyệt cũ truy cập payload, sau đó hack một số trang web hợp pháp để phân phối payload cho nhiều người dùng.
Chính phủ nước ngoài đang nhắm vào bạn
Nghe có vẻ như đùa, nhưng chính phủ Trung Quốc có hàng nghìn người hack hệ thống để tìm bí mật thương mại, tình báo chính phủ, thậm chí thông tin về đối thủ chính trị.
Nghe có vẻ như một công việc "siêu vui" phải không?
Trung Quốc không phải là quốc gia duy nhất điều phối nguồn lực hack trên toàn cầu. Các chính phủ trên thế giới đều tham gia, vì vậy đừng nghĩ ứng dụng của bạn không đủ quan trọng để xem trọng các vấn đề bảo mật.
Hacktivist đang ẩn nấp trong bóng tối
Bạn có bán đạn uranium cạn cho Bộ Quốc phòng? Khi đó bạn nên có đủ tiền thuê người am hiểu. Hacktivist thường xuyên nhắm vào các trang web phù hợp với mục tiêu hoạt động của họ.
Tài liệu bắt buộc đọc
Đây là hướng dẫn chưa đủ đầy đủ. Bạn có mong đợi nó toàn diện không? Tối thiểu bạn cũng nên đọc:
- Hướng dẫn bảo mật Rails
- Trang bảo mật Ruby chính thức
Tám lỗ hổng bảo mật Ruby (hay tám cách dễ dàng bị "pwn")
Bây giờ chúng ta đã cùng trang về mức độ nghiêm trọng của bảo mật (dù cho ứng dụng nhỏ), bạn có thể đang thắc mắc làm thế nào để đảm bảo ứng dụng Ruby của mình an toàn.
Chúng ta sẽ đề cập đến tám loại lỗ hổng mà các nhà phát triển web cần hiểu.
- Phi kỹ thuật
- Cross-Site Scripting (XSS)
- Cross-Site Request Forgery (CSRF, XSRF)
- Man in the Middle (MITM)
- SQL Injection (SQLI)
- Mass Assignment & Parameter Injection
- Denial of Service (DOS, DDOS)
- Platform Attacks (Khai thác host chạy ứng dụng)
Các khai thác bảo mật phi kỹ thuật có thể ảnh hưởng đến Rails
Đôi khi, những vấn đề rõ ràng nhất lại khó nhận ra nhất. Bạn có thể không thấy những sai lầm này trong danh sách kiểm tra, nhưng việc dùng một chút sự bình thường là đáng giá.
Số lượng và loại hình những việc ngốc nghếch bạn có thể làm là vô tận, nên chúng ta chỉ chỉ ra một vài ví dụ.
Đừng:
- Gửi mật khẩu dạng plaintext qua email
- Dùng cùng một mật khẩu cho mọi nơi
- Tin tưởng những người xuất hiện tại văn phòng của bạn với bánh quy
- Mang dữ liệu nhạy cảm trên laptop
Cross-site scripting (XSS)
Có một loại lỗ hổng cho phép kẻ xấu lợi dụng người dùng của bạn khi họ sử dụng ứng dụng.
Nếu tôi là kẻ xấu, tôi có thể khá quan tâm đến cách chèn một số JavaScript của tôi vào trang của bạn.
Khi làm được điều đó, tôi có thể đánh cắp cookie phiên hoạt động, gắn exploit kit vào DOM, thậm chí làm trang nói những điều kỳ quặc. Điều đó nghe có vẻ không quá hại, nhưng ngay cả việc chèn nội dung độc hại cũng có thể gây thiệt hại uy tín.

Có một cách đơn giản để phòng chống XSS
Để ngăn chặn kẻ tấn công lợi dụng XSS, bạn cần escape bất kỳ văn bản nào từ đầu vào người dùng, để JavaScript biến thành văn bản vô hại.
Bạn muốn kết quả này:
<script>alert('I am stealing your cookies! Ha!');</script>
Bạn không muốn kết quả này:
<script>alert('I am stealing your cookies! Ha!');</script>
Đôi khi phòng chống XSS không dễ dàng
Vấn đề là: nếu bạn có ứng dụng Rails cũ chạy mà không có bản vá bảo mật, bạn có thể có lỗ hổng XSS. Quay lại phiên bản Rails rất sớm, bạn phải escape thủ công đầu vào không tin cậy bằng phương thức h().
Các ứng dụng Rails hiện đại escape đầu ra HTML mặc định, ngăn chặn hầu hết lỗ hổng XSS ngay lập tức. Nhưng vẫn dễ mắc lỗi, đặc biệt khi xử lý mã kế thừa, thư viện bên thứ ba, hoặc render HTML động.
Ngay cả trong Rails hiện đại, bạn vẫn có thể "bắn chính mình vào chân". Gọi html_safe trên đầu vào người dùng sẽ bỏ qua việc escape hoàn toàn, nên nếu bạn chèn đầu vào người dùng vào HTML và đánh dấu nó an toàn, bạn cơ bản đang tắt bảo vệ tích hợp của Rails và mời XSS vào ứng dụng.
Đội ngũ Rails công nhận XSS là khai thác phổ biến và gây hại đặc biệt, nên họ dành một phần tài liệu bảo mật để đảm bảo chúng ta có công cụ giảm thiểu rủi ro. Đáng đọc ngay cả khi bạn nghĩ ứng dụng của mình an toàn.
Cross-site request forgery (CSRF)
Một lỗ hổng bảo mật phổ biến khác trong ứng dụng Rails là cross-site request forgery. Hãy xem ví dụ.
Nếu người dùng muốn đổi mật khẩu, họ gửi form đến backend, đúng không? Vậy nếu form đó không nằm trên trang web của bạn thì sao?
Yêu cầu vẫn được xử lý. Kẻ tấn công có thể lợi dụng sự thật này bằng một exploit tinh vi.
Cấu trúc của một cuộc tấn công CSRF
Giả sử tôi là kẻ tấn công muốn khai thác ứng dụng của bạn qua CSRF. Nếu tôi biết bạn đang đăng nhập vào ứng dụng với quyền admin, tôi có thể tạo một form "đổi mật khẩu" ngụy trang thành thứ khác. Bạn sẽ gửi form đó mà không nhận ra và đổi mật khẩu của mình.
Và nó tệ hơn nữa nếu ứng dụng của bạn dùng GET request thay vì POST request.
Tôi thậm chí không cần lừa bạn. Tôi có thể khiến một yêu cầu đã xác thực xảy ra chỉ bằng cách thêm thẻ image vào một trang bạn vừa truy cập.
May mắn thay, Ruby on Rails tránh được loại tấn công này bằng cách cung cấp bảo vệ CSRF tích hợp sẵn. Bạn có thể biết nó như "đoạn văn bản kỳ lạ được đặt trong tất cả form của tôi vì lý do nào đó."
<input type="hidden" name="authenticity_token" value="kM3jN8vBx2QzE-7wRtFpL6aYsI9uZcXmD4oHgK1rVnWe5qA0bUiJlSfTyP3hG2oC8xZ7mBvNkL4wQrEtYuI6pA" autocomplete="off" />
Đoạn văn bản không thể đọc hiểu là một khóa bí mật được lưu trong dữ liệu phiên của người dùng. Nếu người dùng gửi token CSRF không khớp với token trong phiên, ứng dụng của bạn sẽ ném ra ngoại lệ. May thay, Honeybadger có thể giúp bạn tìm và sửa các ngoại lệ này!
Nhưng bạn vẫn có thể làm hỏng nó
Tất nhiên, bạn có thể tắt kiểm tra CSRF bằng một dòng code dường như vô hại trong controller:
skip_before_action :verify_authenticity_token
Bạn cũng có thể tự gây rắc rối bằng cách dùng route GET cho những việc nên dùng POST:
MyApp::Application.routes.draw do match "/launch_all_the_missiles", to: "missiles#launch_all" end
Nhưng bạn không làm vậy, phải không? Vẫn thì, phần đáng sợ về CSRF là nó không nhất thiết phải là ứng dụng của bạn chứa vấn đề. Người dùng có thể truy cập trang khác có exploit CSRF nhúng sẵn, kích hoạt yêu cầu đến ứng dụng của bạn. Ổn vào biện pháp đối phó của Rails có thể giúp bạn bảo vệ khỏi loại chuyện này.
Tấn công Man-in-the-middle và nghe lén gói tin
Tấn công Man-in-the-middle được giải thích tốt nhất bằng ví dụ. Giả sử bạn cần gửi tiền cho chú Vinny xấu xa, không tốt của bạn. Bạn có thể lấy tiền, cho vào phong bì, viết địa chỉ của tên kia, dán tem, và giao phong bì cho người đưa thư.
Một tuần sau, phong bì đến nhà Vinny nhưng không có tiền bên trong. Điều gì đã xảy ra? Ai đó đã can thiệp vào nội dung phong bì giữa bạn và người nhận dự định. Đây là một cuộc tấn công man-in-the-middle đơn giản.
Cookie ứng dụng dễ bị tấn công man-in-the-middle
Câu chuyện trên có vẻ hơi ngớ ngẩn, nhưng điều tương tự có thể xảy ra với cookie của người dùng.
Cookie thường được gửi kèm mọi yêu cầu web. Nếu chúng được gửi dạng plaintext qua mạng công cộng (wifi), thì xong.
Một kẻ xấu chạy thứ gì đó như Firesheep có thể dễ dàng lấy bản sao cookie của người dùng và chiếm đoạt tài khoản của họ.
SSL đến giải cứu
Câu trả lời dễ dàng là dùng HTTPS cho mọi thứ và tuân thủ strict transport security. Mọi người trong quán cà phê vẫn thấy lưu lượng của người dùng, nhưng nó được mã hóa. Câu trả lời không quá dễ nhưng có thể tốt hơn trong một số trường hợp là dùng secure cookie của Rails.
Secure cookie chỉ được gửi đến trình duyệt qua kết nối HTTPS, điều này đã là mặc định từ Rails 3.
Nếu bạn đủ bất hạnh chạy phiên bản Rails cũ hơn đó, secure cookie có thể được gửi dạng plaintext. Không an toàn chút nào, phải không?
SQL Injection
Hầu hết các trang web trên internet hoạt động bằng cách dán các mảnh văn bản lại với nhau để tạo thành những chương trình nhỏ bằng ngôn ngữ gọi là SQL.
Chỉ cần nghĩ về điều đó. Bạn có ý thức được mức độ điên rồ của nó không?
Thích hay không, đó là cách mọi thứ hoạt động. Và dựa trên việc internet tồn tại, nó phải hoạt động khá tốt.
Một tác dụng phụ của việc tung SQL trên internet là nếu hacker có thể thêm mảnh văn bản của họ vào những chương trình SQL nhỏ, dữ liệu của bạn có thể bị lộ.

Ví dụ về SQL injection
Hãy tưởng tượng nhà thầu gửi cho bạn mã trông như thế này.
Building.where("st_intersects(st_setsrid(st_makebox2d(st_point(#{params[:northeast][:lng]}, #{params[:northeast][:lat]}), st_point(#{params[:southwest][:lng]}, %{params[:southwest][:lat]})), 4326), buildings.location)")
Đây là một sai lầm lớn có thể mở ra lỗ hổng SQL injection. Truy cập params trực tiếp trong truy vấn SQL cho phép client buộc SQL của họ thực thi. Một kẻ tấn công tinh vi có thể làm cho params[:southwest][:lat] chứa mã SQL độc hại như sau:
")), 4326), buildings.location); UPDATE users SET admin=1 WHERE id=666;--"
Và bây giờ user ID của tôi có đặc quyền admin. SQL injection là exploit phổ biến và dễ dàng, nên bất kỳ ai theo dõi lưu lượng trang web đủ lâu cuối cùng cũng sẽ thấy các nỗ lực SQL inject.
Rails bảo vệ chúng ta khỏi SQL injection gần như tự động
Rails bảo vệ khỏi SQL injection trong nhiều trường hợp, miễn là bạn để nó làm. Nếu chúng ta dùng where đúng cách, các tham số sẽ được escape, và các lần thử SQL injection sẽ thất bại.
where("st_intersects(st_setsrid(st_makebox2d(st_point(?, ?), st_point(?, ?)), 4326), #{table_name}.location)", box[:northeast][:lng], box[:northeast][:lat], box[:southwest][:lng], box[:southwest][:lat]).
Rails không lúc nào cũng bảo vệ khỏi SQL injection
Nhưng có nhiều nơi Rails dùng raw SQL. Những nơi bạn có thể dễ dàng bỏ qua.
Ví dụ, bạn có biết rằng trong SomeModel.sum("amount"), chuỗi "amount" được thêm vào truy vấn mà không bị escape?
Vì vậy nếu bạn có báo cáo gọi phương thức sum với truy cập trực tiếp params["col"], bạn đang trong rắc rối.
Đây là ví dụ phiên IRB, cho thấy hậu quả của việc truyền đầu vào không tin cậy vào phương thức sum:
pry(main)> User.sum(%[id) AS sum_id FROM users; update users set admin='t' where id=224;--]) (22.4ms) SELECT SUM(id) AS sum_id FROM users; update users set admin='t' where id=224;--) AS sum_id FROM "users" => "0"
Điều tương tự đúng cho #pluck, cũng như một loạt phương thức khác.
Tấn công Mass Assignment
Ngày 4 tháng 3 năm 2012, hacker người Nga Egor Homakov tiết lộ lỗ hổng mass assignment có thể cho phép kẻ tấn công mạo danh bất kỳ người dùng GitHub nào. Như bạn hình dung, điều này cực kỳ nghiêm trọng.
Cách GitHub bị hack
Tôi打赌 bạn đã viết code như này hàng trăm lần:
User.create(params[:user])
Nhờ tính dễ đọc của Ruby, dễ dàng thấy code này tạo user với các tham số cho trước.
Trước Rails 3.2.3, điều đó có nghĩa là bất kỳ tham số nào được nhập vào sẽ được gán cho model trừ khi bạn chỉ định ngược lại.
Vấn đề này gây ra là rất dễ quên. Vì thế bạn rơi vào thế giới nơi kẻ tấn công chỉ cần gửi đúng POST request để gây ra chuyện này:
User.create({admin: true, ...})
Hoặc trong trường hợp GitHub, thứ gì đó như:
PublicKey.update({user_id: "123"})
Mass assignment dễ dàng khắc phục
Giải pháp ưu tiên cho lỗ hổng mass assignment trong Rails hiện đại là dùng Strong Parameters, mà bạn nên quen thuộc từ trước trong bài này.
Thay vì truyền mù mịt toàn bộ hash params cho User.create, bạn khai báo rõ ràng thuộc tính nào được phép:
def user_params params.require(:user).permit(:name, :email) end User.create(user_params)
Lựa chọn đơn giản này bảo vệ model của bạn bằng cách whitelist chính xác những trường bạn định cập nhật. Đó là bảo vệ mặc định trong Rails 4 và các phiên bản sau. Nếu bạn đang maintain một app thật cũ (như Rails 4 hoặc cũ hơn), việc nâng cấp là đáng giá.
Tấn công từ chối dịch vụ (DoS/DDoS)
Hôm nay bạn đã làm phật lòng bao nhiêu người? Tháng trước thì sao?
Tấn công từ chối dịch vụ (DOS) và phân tán từ chối dịch vụ (DDOS) là phiên bản của cộng đồng an ninh thông tin cho "rage comic". Những tấn công này không đánh cắp dữ liệu hay lan truyền malware. Chúng chỉ muốn làm sập bạn. Điều này vẫn có thể gây thiệt hại tài chính hoặc uy tín lớn cho công ty bạn.
DDoS là tấn công thô sơ nhưng hiệu quả
Để thực hiện tấn công DDoS, chỉ cần gọi 500 người bạn thân nhất, nhờ họ vào trang Amnesty International và liên tục nhấn refresh một giờ. Bot có thể thay thế cho bạn bè.
Nếu bạn gặp vấn đề như vậy, có một số giải pháp. Rails không quá tập trung vào loại tấn công này, nhưng có một số gem giúp bạn nhanh chóng giới thiệu rate limiting. Rails cũng có middleware throttling bạn có thể dùng. Bạn cũng có thể cân nhắc nói với ISP và dùng proxy như Cloudflare, cung cấp tính năng rate-limiting mạnh mẽ. אלו những vấn đề khá nhàm chán nhưng nổi tiếng.
Tấn công độ phức tạp thuật toán
Nghe có vẻ "cool" ghê, phải không?
Những tấn công này lợi dụng điểm yếu cụ thể trong hệ điều hành, ngăn xếp ứng dụng, v.v., gây vấn đề trên host ứng dụng. Chúng có thể dùng để lấp đầy bộ nhớ server, tăng sử dụng CPU, thậm chí lấp đầy ổ đĩa.
Ví dụ, cuộc tấn công SYN flood cổ điển hoạt động bằng cách gửi yêu cầu SYN đến server. Server mở một lượng lớn kết nối không bị đóng. Với đủ kết nối mở, hệ điều hành sẽ nhanh chóng hết bộ nhớ, và chính ứng dụng sẽ crash. Điều này khai thác một thực tế thuật toán để gây ra vấn đề có ý nghĩa có thể làm sập hoàn toàn ứng dụng.
Các khai thác server truyền thống
Đây chính là điều chúng ta nghĩ đến khi nhắc đến hacker, phải không? Những người mờ ám dùng buffer overflow khiến Apache chạy code tùy ý với quyền root?
Nhưng như nhiều công việc nhàm chán, điều này cũng đã được tự động hóa. Nếu bạn nhìn log hệ thống ngay bây giờ, tôi打赌 bạn sẽ thấy bot đang thử brute-force đăng nhập SSH.
Tự động hóa phát hiện tấn công
Bạn có thể dùng công cụ phổ biến như fail2ban để tự động chặn địa chỉ IP của host đang thử các tấn công đã biết đối với bạn.
Làm những việc bạn biết nên làm.
Giữ hệ điều hành được vá lỗi. Không chạy dịch vụ không cần thiết. Không cho phép xác thực mật khẩu cho SSH. Danh sách còn dài. Nếu bạn dùng platform, phần lớn việc này được lo cho bạn.
Đã đến lúc nâng cao bảo mật Rails của bạn
Trước hết, đừng hoang mang! Bảo mật Rails là một phần công việc của bạn dù bạn thích hay không. Bây giờ bạn đang đối xử nghiêm túc với nó, bạn sẽ tiến bộ nhanh trong việc giảm thiểu lỗ hổng. Một chiến thắng được đánh giá thấp nhưng hiệu quả là đảm bảo bạn đang dùng phiên bản Rails vẫn nhận cập nhật bảo mật. Tất cả việc escape SQL trên đời cũng không bảo vệ được bạn nếu framework chạy trên phiên bản có lỗ hổng nghiêm trọng đã được công bố. Tương tự cho các dependency gem.
Một khi framework được hỗ trợ, hãy dùng hướng dẫn này (và hướng dẫn bảo mật Rails!) để giảm bề mặt rủi ro của ứng dụng. Dùng strong parameters. Cấu hình rate limiter và có thể dùng proxy. Tuân thủ quy ước HTTP cho phương thức và buộc traffic phải dùng SSL. Tất cả những điều này sẽ giảm dần khả năng bạn gặp vấn đề bảo mật.
Ghi chú của biên tập viên: Bài viết này lần đầu xuất bản tháng 3 năm 2013 và đã được cập nhật để đảm bảo độ chính xác.