Advanced Rails - Building Industrial-Strength Web Apps in Record Time

(Tuis.) #1

144 | Chapter 5: Security


You would need some form of path normalization if you wanted to compare these
paths. More importantly, you would want to know that/var/www/public/../config/
database.ymlis most definitely not within/var/www/public, lest you try to serve it as
plain text to the client. As bad as that is, it is much worse when allowing a user to
upload files.


The double-dot problem (known as adirectory traversal attack) is one of the oldest,
most basic canonicalization problems around, but it persists to this day. Path nor-
malization is easy in Ruby using theFile.expand_pathmethod, and this should
always be used as a final test for any files opened based on user input:


name = params[:filename]

base_path = File.expand_path(File.join(RAILS_ROOT, "public"))
file_path = File.join(base_path, name)

if File.expand_path(file_path).starts_with?(base_path)
data = File.read(file_path)
else
raise "Access denied"
end

Another approach to preventing directory traversal is to blacklist paths containing
characters such as../. However, this is very, very hard to do right. Path compo-
nents can be URI-encoded, and it is difficult to predict how many levels of decoding
will be performed before hitting the filesystem. With the advent of Unicode and its
many encodings, there are myriad ways one set of characters can be represented to a
web application. Far better to check the thing you actually care about (whether or
not the file is in the right directory) than to check something incidental to it (whether
or not any “funny” characters were used in the pathname).


SQL Injection


SQL injection is an attack against programs that do not take proper precautions
when accessing a SQL-based database. A standard example of vulnerable code is:


search = params[:q]
Person.find_by_sql %(SELECT * FROM people WHERE name = '#{search}%')

Of course, all someone has to do is search for “'; DROP TABLE people; --”, which
yields the following statement:


SELECT * FROM people WHERE name = ''; DROP TABLE people; --%';

Everything after the--is treated as a SQL comment (otherwise, the attempt might
cause a SQL error). First, theSELECTstatement is executed; then theDROP TABLEstate-
ment causes havoc. Ideally, the database user that executes that statement should
not haveDROP TABLEprivileges, but SQL injection is always damaging. There are
plenty of other attack vectors.

Free download pdf