Web App Security
Introduction
This page is the seed of Public Display's collective knowledge base about security. Here you will find the main classes of vulnerability we need to be aware of, with a brief description.
Cross Site Scripting (XSS)
XSS attacks occur when an attacker is able to cause our application to execute malicious code (usually javascript) on a user's browser. There are two main categories of XSS attack (although there are other more obscure ones as well). A reflected XSS attack requires social engineering to get a user to click on a link or otherwise visit a page which will be contaminated with malicious content. A stored XSS attack causes the malicious content to be stored in our database (e.g., in a blog comment) where it can affect multiple users who view that content without the need for any social engineering.
Please note that in our security discussion earlier this month, I more or less denied the existence of reflected XSS attacks, stating that we didn't need to worry about users attacking themselves. I was wrong! Once social engineering comes into the mix, it is totally possible for users to be tricked into initiating an XSS attack against themselves, and thus we have an obligation to prevent this when possible. Stored XSS vulnerabilities are, however, more easily exploitable and thus more of a concern.
Two complimentary strategies can be used to thwart XSS attacks:
- Any user input that does not need to contain html or javascript should be cleaned up with strip_tags or sanitize, especially if it's going to be stored in the database
- Most importantly, any data that came from any user needs to be escaped with h() (short for html_escape) in the ActionView before it is sent to the browser. If this is not possible because it is important for html tags in the data to be interpreted, then you must very carefully validate the content of those tags, at the very least by using sanitize.
Notice that strategy 2 is always required even if you've done strategy 1 and believe the data is "safe". This is because A) some other, less security-conscious coder might allow bad data in later on, and B) it's always possible that an attacker will find a way to fool the input validation (i.e., find a vulnerability in sanitize or strip_tags) so we always want to escape any potentially dangerous characters on the way out to the user.
Reflected XSS
Bug 989 shows how malicious code could be executed from a cached source that was harvested by bluto. For the time being, this is effectively a reflected attack, because the user has to be tricked into harvesting the malicious source. However, the cached source is stored in our database, so this could easily escalate to a stored XSS attack with a much wider range of penetration if/when we implement calendar sharing or browsing features.
Stored XSS
I have yet to find any vulnerabilities of this type, and furthermore I think we are pretty safe from them -- for now -- because our application lacks a real social component. That is, it is difficult or impossible for users to access content that other users have placed in the database, except for cached sources if they happen to harvest the same URI. Since there is no sharing of data from the database, there can be no stored XSS attacks.
However, as social features are added to bluto, this will be an increasingly important area to keep an eye on.
Injection Attacks
Injection attacks focus on executing malicious code on the server. In a sense, XSS is also an injection attack, but because it targets the client rather than the server it is usually considered separately.
There are many subcategories of injection attack based on the kind of malicious code being run.
SQL Injection
Probably the best known injection attack. The attacker attempts to get the server to execute unauthorized SQL using quote characters and comment markers. A good example can be found starting on pg. 439 in Agile Development with Rails, 1st Ed. (there's probably one in the second edition too, but I haven't looked up the page number yet).
The good news is that Rails is quite good at thwarting SQL injection attacks as long as you use the ActiveRecord methods (#find, etc.) to access your models, and use the array syntax for passing in parameters rather than simple string interpolation. For example, assuming that looking_for_admin is a parameter the user has sent us, which we are expecting to be 'true' or 'false' but could, in fact, be anything:
We would always use
User.find(:all, :conditions => ['admin = ?', looking_for_admin])
rather than
User.find(:all, :conditions => "admin = #{looking_for_admin}")<
Other rails-provided methods such as User.find_by_admin(looking_for_admin) are safe as well. Rails carefully escapes any potentially dangerous tokens to defuse SQL injection attacks. Note that the array syntax for passing parameters also works (and is recommended!) in #find_by_sql methods and most other cases where you would pass user input to an ActiveRecord object. Therefore there is rarely a need to use variable interpolation in a SQL context. The only exception is when the value being interpolated needs to be interpreted by the database as an identifier (e.g. a column name). In that case, the value must be carefully validated before being inserted into the query.
Ruby Injection
This can happen when a method that interprets a string as ruby code, such as #eval or #send is used on unvalidated user input. This can allow an attacker to have unrestricted access to the application as though they were writing ruby on our server.
The only ways to prevent this are:
- Whenever possible, do not allow any user input into the commands being executed with #eval, #send or the like
- If user input absolutely must be used in a command like this, make sure it is extremely carefully validated as being a reasonable command before it is executed.
No examples of ruby injection vulnerabilities have yet been found in bluto.
Command Line Injection
This occurs when a malicious user manages to get dangerous content interpolated into a string that is sent to the server's command line, with the backtick (`) operator, for example. This can allow the attacker to do anything on the server that the web server user can do, and possibly escalate to root if the underlying OS has a vulnerability that allows this.
Avoiding command line injection is similar to avoiding ruby injection (see above).