Skip to content

Safe String Interpolation In Ruby

Age-Old Problem

Suppose you want to authenticate user from the login/password entered on the web from

    user = User.first(:conditions => "login = '#{ login }' AND password = '#{ password }'")
    
Such authentication code as we know can be easily fooled with the SQL injection. Thankfully in Rails there are parameterized queries and the above can be rewritten in at least 3 injection safe ways
    User.find_by_login_and_password(login, password)
    # or
    User.first(:conditions => ["login = ? AND password = ?", login, password])
    # or
    User.first(:conditions => ["login = :login AND password = :password", { :login => login, :password => password }])
    

Many Solutions (Haskell Included)

Parameterized queries cover most of the cases but what if you have large query with multiple parameters? You can quickly lose track of all the question marks and order in which parameters appear. Most likely you will prefer the latter, hash-based notation.

Side note: This stuff is actually analyzed in depth by Jim Weirich in his The Building Blocks of Modularity presentation where he talks about the connascence in software.

Back to our topic what we really want is just to write SQL queries injecting properly quoted values where needed. In other words we would like to be able to customize the way string interpolation works. In cool languages like Haskell this concept is natively supported and called Quasiquotation. While not really on par with such rigorous achievements it can be simulated everywhere where eval is available. Here is for example the solution from the Google Caja team.

Hack For Better Future

There is eval in Ruby and there is also one peculiar feature about it. You can pass binding to it, and when the block is created it’s binding is captured and therefore can be used from anywhere (providing access to the lexical scope captured by the block). Let’s illustrate that

    ruby-head > def m
                  local = 42
                  proc {} # return empty block to get binding from it later
                end
    ruby-head > eval("local", m.binding) # binding gives access to the scope where block was created
    => 42 
    

Using that feature we can hide eval and implement our own safe interpolation routines like I did in safe_interpolate.rb. Use it like

    ruby-head > require 'dolzenko/safe_interpolate'
    ruby-head > include SafeInterpolate
    ruby-head > login = "' or 1=1"
    ruby-head > sql_interpolate { 'login = #{ login }' }
     => "login = ''' or 1=1'" # single quote got turned into double and the whole expression is quoted itself
    
HTML and URI escaping are also provided
    ruby-head > content = "<script>alert(1)</script>"
    ruby-head > html_interpolate { '<p>#{ content }</p>' }
    => "<p>&lt;script&gt;alert(1)&lt;/script&gt;</p>" # injection prevented!

    ruby-head > query = "&admin=1"
    ruby-head > uri_interpolate { 'http://example.com?q=#{ query }' }
    => "http://example.com?q=%26admin%3D1"
    

Further Reading

A type-based solution to the “strings problem”: a fitting end to XSS and SQL-injection holes? by Tom Moertel. This article got me interested in the subject.
Haskell features I’d like to see in other languages by Tim Carstens. Check the «Quasi-quoting» section for collection of Haskell quoting related links.

blog comments powered by Disqus