Rails 實作 SQL Injection
本篇內容
- 介紹
- 入侵實作
- 修補方式
介紹
SQL(結構化查詢語言)用於管理資料庫,由於其無法區分值和控制指令, 因此惡意人士可以藉由在值中安插控制指令從而對資料庫進行惡意操作,這種操作稱為 SQL Injection。
入侵實作
本範例使用 Ruby on Rails 6 進行實作。
1. 建立新檔案
使用 Rails 指令新增專案:
rails new demo
cd demo
- gem 'sqlite3', '~> 1.4'
+ gem 'pg'
default: &default
adapter: postgresql
encoding: unicode
development:
<<: *default
database: development
createdb development
rails g scaffold post title content
rails db:migrate
root to: "posts#index"
<%= form_tag(:posts, method: :get) do%>
<%= label_tag(:search)%>
<%= text_field_tag(:search, params[:search])%>
<%= submit_tag("search")%>
<% end%>
def index
if(params[:search])
sql = "SELECT posts.* FROM posts WHERE (posts.title LIKE '%#{params[:search]}');"
result = ActiveRecord::Base.connection.execute(sql)
@posts = result.map { |p| OpenStruct.new p }
else
@posts = Post.all
end
end
之後我們在搜尋框中輸入 apple');DELETE FROM posts; --
送出後查看log:
可以發現它最終組出了以下的 SQL 指令:
SELECT posts.* FROM posts WHERE (posts.title LIKE '%apple');DELETE FROM posts; --');
第一段是正常執行的指令,只不過因為我們在搜尋框中填入了 apple'); 導致第一段程式碼提前中斷,而第二段就是惡意指令,使我們 posts 這個 table 的資料全部被刪除,而最後一段 -- 則是註解,可以讓其之後的指令皆不被執行。
修補方式
其實 Rails 的防止 SQL Injection 機制很完善,雖然並不保證百分之百不會被攻擊,通常 SQL Ingection 都發生在需要搭配變數的情況,常見的做法就是將所有會影響 SQL 的控制字元進行轉義,以下示範:
- sql = "SELECT posts.* FROM posts WHERE (posts.title LIKE '%#{params[:search]}');"
+ keyword = ActiveRecord::Base::connection.quote_string(params[:search])
+ sql = "SELECT posts.* FROM posts WHERE (posts.title LIKE '%#{keyword}');"
result = ActiveRecord::Base.connection.execute(sql)
@posts = result.map { |p| OpenStruct.new p }
另一種更方便的就是用 Array 或是 Hash 傳入:
Post.where("title = ?", "test")
Post.where(title: "test")
但就是不要使用以下的做法:
Post.where("title = #{var}")
SELECT "posts".* FROM "posts" WHERE (title = '' OR 1='1')
但萬幸的是無法用 ; 注入第二段指令,例如 var = "');DELETE FROM posts;--" 後再執行 Post.where("title = #{var}"),就會產生:
SELECT "posts".* FROM "posts" WHERE (title = '');DELETE FROM posts;--')
ActiveRecord::StatementInvalid (PG::SyntaxError: ERROR: cannot insert multiple commands into a prepared statement)