Thanks for your help as I bootstrap my way into Ruby and Rails.
In the Rails API for ActiveRecord::Base, there’s a section on Conditions that’s meant to simply cover syntax for interactions with ActiveRecord. But the example they’ve used includes a very interesting (to me) primer on input-sanitization in Ruby/Rails:
class User < ActiveRecord::Base
def self.authenticate_unsafely(user_name, password)
where("user_name = '#{user_name}' AND password = '#{password}'").first
end
def self.authenticate_safely(user_name, password)
where("user_name = ? AND password = ?", user_name, password).first
end
def self.authenticate_safely_simply(user_name, password)
where(:user_name => user_name, :password => password).first
end
end
Following this example code, they explain that:
“The authenticate_safely and authenticate_safely_simply both will sanitize the user_name and password before inserting them in the query, which will ensure that an attacker can’t escape the query and fake the login (or worse).”
I totally get how this sanitization of inputs is a Good Thing in preventing injection attacks. What I do not understand is where and how this implicit sanitization is happening, given that there are no special methods being called to pre-process the input data. The various example methods appear to have nearly identical semantics and yet the variations in form have huge effects on safety because of the way that they’re parsed. I’m assuming that these variations in form are similar in effect to the difference between using single-quotes and double-quotes around a string containing escape characters. But can anybody help me get smarter, faster, by understanding in general terms (or rather: at the logical level, rather than inside the interpreter) what’s actually happening under the hood to make this so?
Also, to what extent are any of these differences dependent on Rails-specific constructs, rather than the underlying Ruby?
Thank you!
The input sanitization is a feature provided by Active Record known as prepared statements or parameterized statements.
Most database access libraries provide this natively, however, Active Record chooses to emulate this feature using string-mangling mechanisms. See
build_whereinactive_record/relation/query_methods.rband backtrack your way tosanitize_sql_for_conditions(via itssanitize_sqlalias) inactive_record/base.rb. (Many thanks to mu is too short for the research.)Instead of the old-style practice of building query strings as strings in the application, you have a static template query with parameters. When you call the query, you supply the parameters and Active Record will construct a safe query for the SQL engine to execute.
You could do this task yourself — myriad PHP programmers choose to eschew their similar PDO database access layer. However, many programmers cannot get this correct: there are roughly 1500 SQL Injection flaws discovered in the last three years and my local CVE database shows over 5000 instances of programs vulnerable to SQL injection attacks since MITRE started keeping track. Nearly all of these were due entirely to people who chose to write their own SQL code by hand and did not properly sanitize their input. (I didn’t personally inspect each one but I can’t recall seeing any flaws in the ORM or database access layers that allow SQL injection attacks through. But to be on the conservative side, I chose “Nearly all”.)
Fellow stacker Jeff Atwood equates old-style SQL queries as the
gotoof database programming: