Rails SQL Injection
Bài đăng này đã không được cập nhật trong 7 năm
Ruby On Rails cung cấp module ActiveRecord, trong đó xây dựng hàng loạt phương thức giúp thao tác với cơ sở dữ liệu một cách dễ dàng. Nhưng cũng cần phải lưu ý khi xử dụng một số phương thức nhận tham số là param từ client gửi lên, bởi vì nó có thể là lỗ hổng để khai thác lỗi SQL Injection.
Ta có thể sử dụng gem Brakeman để giúp phát hiện những truy vấn có thể gây ra lỗi. Nhưng để có thể chủ động viết ra những code an toàn hơn, chúng ta hãy cùng nhau tìm hiểu một số phương thức có thể gây ra lỗi SQL Injection:
Các phương thức tính toán
- average
- calculate
- count
- maximum
- minimum
- sum
Ví dụ
params[:column] = "point) where users.id = 1"
User.calculate(:sum, params[:column])
SELECT SUM(age) FROM users WHERE id = 1';) FROM "orders"
Ở câu lệnh trên, thay vì tính tổng số tuổi của tất cả user, câu lệnh chỉ tính tổng tuổi của một user xác định, có id=1
Tương tự đối với các phương thức còn lại
Phương thức delete_all
Bất kỳ phương thức xóa bản ghi nào đều cần được sử dụng cẩn thận!
Phương thức delete_all
có tham số điều kiện tìm kiếm giống với phương thức find
. Tham số có thể là một string, một array, hoặc một hash các điêù kiện. Sử dụng array hoặc hash để an toàn hơn và nhớ rằng không bao giờ truyền trực tiếp tham số được nhập bởi user.
Ví dụ
Câu lệnh sau sẽ xóa toàn bộ user
params[:id] = "1) OR 1=1--"
User.delete_all("id=#{params[:id]}")
DELETE FROM "users" WHERE ((id = 1) OR 1=1--)
Phương thức destroy_all
Phương thức destroy_all
sẽ an toàn hơn phương thức delete_all
một chút do nó sẽ gọi các call_back tới các model có quan hệ với model đó.
Tương tự phương thức delete_all
, tham số điều kiện có thể là một string, một array hoặc một hash các điều kiện. Sử dụng array hoặc hash sẽ an toàn hơn.
Ví dụ
Câu lệnh sau sẽ xóa tất cả các user
params[:admin] = "') OR 1=1--'"
User.destroy_all(["id = ? AND admin = '#{params[:admin]}", params[:id]])
SELECT "users".* FROM "users" WHERE (id = NULL AND admin = '') OR 1=1--')
Phương thức find_by
Thêm vào trong rails 4, find_by
/find_by!
thực thi tương tự phương thức where(*args).take
. Do đó điều kiện cho where
được áp dụng.
An toàn nhất và cũng hầu hết được sử dụng là truyền một hash các điều kiện.
Ví dụ
Ví dụ sau sẽ tìm users là admin
params[:id] = "admin = 't'"
User.find_by params[:id]
SELECT "users".* FROM "users" WHERE (admin = 't') LIMIT 1
Phương thức from
Phương thức from
chấp nhận tham số là một truy vấn bất kỳ.
Ví dụ
Câu lệnh sau sẽ trả về tất cả các user là admin
params[:from] = "users WHERE admin = 't';"
User.from(params[:from])
SELECT "users".* FROM users WHERE admin = 't';
Phương thức group
Phương thức group
chấp nhận tham số là một truy vấn bất kỳ.
Vi dụ
Mục đích của truy vấn này là group
nhưng users không phải là admin. Thay vào đó lại trả lại tất cả users.
params[:group] = "name UNION SELECT * FROM users"
User.where(:admin => false).group(params[:group])
SELECT "users".* FROM "users" WHERE "users"."admin" = ? GROUP BY name UNION SELECT * FROM users
Phương thức having
Phương thức having
không xử lý tham số đầu vào cho nên dễ dàng để khai thác SQL injection bởi vì nó có xu hướng được gọi ở phần cuối câu truy vấn.
Ví dụ
Câu lệnh sau thay vì trả lại danh sách orders của một user thì trả lại tất cả các orders.
params[:total] = "1 UNION SELECT * FROM orders"
Order.where(:user_id => 1).group(:user_id).having("total > #{params[:total]}")
SELECT "orders".* FROM "orders" WHERE "orders"."user_id" = ? GROUP BY "orders"."user_id" HAVING total > 1 UNION SELECT * FROM orders
Phương thức joins
Phương thức joins
có thể nhận tham số là một mảng các quan hệ hoặc một string.
Ví dụ
Bỏ qua điều kiện where
trả lại tất cả orders thay vì trả lại danh sách order của một user xác đinh.
params[:table] = "--"
Order.joins(params[:table])
SELECT "orders".* FROM "orders" --
Phương thức select
Phương thức select
cho phép truyền vào một string, do vậy tham số đầu vào cần được ascape.
Ví dụ
Câu lệnh sau trả về tất cả user, thay vì trả lại chỉ user thường hoặc admin
params[:column] = "WHERE admin = 't' OR 1=1"
User.select(params[:column])
SELECT * FROM users WHERE admin = 't' OR 1=1"
Trên đây mình đã đưa ra một số phương thức có thể khai thác SQL ịnjection khi dữ liệu đầu vào chưa được kiểm tra. Để hiểu rõ hơn các bạn tìm đọc thêm tại đây:
All rights reserved