#25 SQL Injection
Did you really name your son Robert'); DROP TABLE Students; --?
- XKCD No. 327
Dalam beberapa episode, kita akan membahas mengenai keamanan dan pengamanan situs anda dari serangan hacker. Salah satu aturan utama dari security adalah jangan pernah percaya pada input dari seorang user. Dalam Rails, hal ini berarti memperhatikan seluruh item dalam hash params. User dapat mengontrol baik key maupun value dari hash params, sehingga seluruh input harus diwaspadai. Hal tersebut juga berlaku untuk hash cookies, dimana user dapat mengatur apa yang ada di dalamnya. Sebaliknya, hash session adalah satu-satunya yang berada di sisi server, sehingga dapat dipercaya nilai yang ada pada hash session.
Salah satu masalah umum security adalah SQL Injection. Hal ini terjadi ketika inputan dari user ditempatkan secara langsung dalam sebuah query SQL. Jika seorang user mengetahui bahwa inputan miliknya langsung dilakukan insert ke dalam sebuah query, secara sengaja mereka dapat menulis input yang dapat mengambil data dari database yang seharusnya tidak boleh terlihat atau bahkan mengubah atau menghapus data dalam database. Kita akan menjelaskan bagaimana hal ini bekerja dan bagaimana terlindung dari SQL Injection dengan sebuah contoh sederhana.
Pencarian Task
Di bawah ini kita memiliki sebuah form pencarian sederhana pada sebuah halaman yang melakukan pencarian dengan menggunakan SQL LIKE untuk mendapatkan task dengan nama yang cocok dengan input yang diberikan pada form.
Masalahnya adalah input yang diberikan oleh user langsung dikirimkan ke sebuah query database dalam controller kita.
class TasksController < ApplicationController def index @tasks = Task.find(:all,:conditions=>"name LIKE ’%#{params[:query]}%’") end end
TasksController menunjukkan SQL query yang berpotensi tidak aman
Nilai di dalam key :conditions
dalam hash find
yang langsung digunakan sebagai query SQL. Yang menjadi masalah dengan hal ini adalah jika user mengirimkan parameter yang mengandung sebuah petik tunggal, maka seluruh statement akan dianggap sebagai SQL. Sehingga jika kita jika sebuah istilah atau kata mengandung sebuah petik tunggal pada saat seach, misal Task 1’TEST, inputan setelah petik tunggal akan dieksekusi sebagai SQL dan Rails akan melakukan throw error.
Processing TasksController#index (for 127.0.0.1 at 2009-02-01 21:29:26) [GET] Parameters: {"query"=>"Task 1’TEST"} Task Load (0.0ms) SQLite3::SQLException: near "TEST": syntax error: SELECT * FROM "tasks" WHERE (name like ’%Task 1’TEST%’) ActiveRecord::StatementInvalid (SQLite3::SQLException: near "TEST": syntax error: SELECT * FROM "tasks" WHERE (name like ’%Task 1’TEST%’) ):
SQL tidak valid muncul jika inputan user mengandung petik tunggal.
Dalam perintah SQL diatas yang dimiliki oleh kata dalam search, Task 1’TEST; dan dari kata tersebut, kita dapat melihat bahwa tanda petik yang ada di dalamnya merupakan statement SQL yang lengkap. Kemudian, bagian yang terakhir setelah tanda titik koma, TEST%), merupakan SQL yang tidak valid yang akan melakukan throw error. Hal ini berbahaya karena dapat menyebabkan SQL apapun dapat dieksekusi dalam database. Bagaimana menghentikan hal ini?
Jawabannya adalah melakukan escape terhadap petik tunggal. Rails memiliki cara yang mudah untuk melakukannya. Kita dapat mengirimkan sebuah array of conditions, di mana di elemen pertamanya merupakan kata yang inputkan saat search dengan value yang digantikan dengan tanda tanya (?). Setiap tanda tanya dalam elemen pertama akan digantikan oleh value dari dari elemen selanjutnya dalam keadaan telah di-escape, sehingga aman. Jika seandainya kita memiliki tiga buah parameter, maka kita menggunakan tiga buah tanda tanya dan array kita akan memiliki empat buah elemen.
@tasks = Task.find(:all, :conditions=> [ "name LIKE ?", "%#{params[:query]}%" ]
Cara yang lebih aman untuk mengirimkan sebuah kata dalam search yang di-input oleh user.
Sekarang query kita telah ter-update. Jika kita melakukan search ulang untuk sebuah kata atau istilah dengan petik tunggal didalamnya, Rails akan melakukan escape petik tersebut dengan aman. Untuk menyakinkan bahwa search menggunakan LIKE, tanda persen (%) perlu ditempatkan dintara query dalam elemen array yang kedua. Menempatkan tanda persen yang mengapit tanda tanya tidak akan membuat query yang menggunakan LIKE tersebut bekerja.
The SQL in our log file now looks like this. Processing TasksController#index (for 127.0.0.1 at 2009-02-01 21:59:31) [GET] Parameters: {"query"=>"Task 1’TEST"} Task Load (0.5ms) SELECT * FROM "tasks" WHERE (name like ’%Task 1’’TEST%’)
Kata yang di-search oleh user telah di-escape dengan aman.
Kita dapat melihat sekarang bahwa petik telah di-escape. Sebagai catatan, jika menggunakan Sqlite, tanda petik akan di-escape dengan menggunakan dua buah petik tunggal (’’). Sedangkan database lain biasanya menggunakan backslash (\) sebelum tanda petik.
Dalam Rails anda mungkin hanya perlu mengkhawatirkan bagaimana melakukan escape input dalam method find jika anda menggunakan parameter :conditions. Jika anda menggunakan method dinamis find_by seperti yang dijelaskan pada episode 2 maka Rails akan secara otomatis melakukan escape terhadap inputan apapun untuk meyakinkan bahwa anda telah aman dari SQL Injection.