CSC/ECE 517 Fall 2009/wiki1b 8 rubysecurity: Difference between revisions
(fifth) |
(sixth) |
||
Line 153: | Line 153: | ||
This method can be invoked before performing any actions in the adminisration controller by adding the line | This method can be invoked before performing any actions in the adminisration controller by adding the line | ||
before_filter :authorize | before_filter :authorize | ||
====Execution with Unnecessary Privileges ==== | |||
---- | |||
This error falls under the category of providing Role Based Access Control[Citation:]. Therefore we need to provide proper authorization mechanisms for avoiding this error. Refer to [[#Improper_Access_Control_.28Authorization.29 | Improper Access Control (Authorization)]] to see how this can be achieved. | |||
====Insecure Permission Assignment for Critical Resource ==== | |||
---- | |||
This error too is as a result of improper handling of authorization. Refer to the section [[#Improper_Access_Control_.28Authorization.29 | Improper Access Control (Authorization)]] to see how this can be achieved. |
Revision as of 00:28, 22 September 2009
This article explores how the Ruby on Rails framework handles common dangerous programming errors committed in a web application and how it compares with other web application frameworks in terms of handling of these errors.
Introduction
Almost all real world data and transactions are now available as web services which are created in some web application framework. As the number of such services increase, so do the possibility that attackers might try to exploit new loopholes in the application that is developed. The consequences of such attacks are far reaching and destructive in terms of business and security perspective. The danger is so grave that during January this year that experts from more than 30 US and international cyber security organizations jointly released the consensus list of the 25 most dangerous programming errors that lead to security bugs and that enable cyber espionage and cyber crime. Ruby on Rails [citation needed], a MVC based application framework architecture, is used for designing web applications using Ruby language, comes with some features which can handle some of the common programming errors cited. In the next section we will see how each of these errors are handled according to their classification and in the third section we will see how Rails compares with other application framework in handling these errors.
Ruby on Rails's handling of the common programming errors
Synchronous to the classifiation of programming errors as cited in [citation], this section has three sub divisions or categories.
CATEGORY: Insecure communication between components
This section contains the programming errors such as Improper Input validation, improper encoding of output,SQL injection etc.,
Improper Input Validation
Authorization is the process of checking whether a user has access to do what he wants to do. This automatically brings the issue of handling roles in the web application. If roles are not properly defined and implemented, an attacker can login as a genuine user by registering with your application and can perform unwanted reads / write which can lead to loss of sensitive information.
Validation in Rails is very simple and short. We have to use the function validates. Validation can be done with ActiveRecord [citation] which is the ORM layer supplied with rails that relieves the programmer of dealing with the underlying databse and is the solid model foundation of Rails MVC architecture. ActiveRecord comes with a number of helper classes for validation. Using these helper classes, to check that a variable in the model is not null, we use validates_presence_of: followed by the field names that need to be validated. To check the length of a variable we use validates.lengthof: function. The below code uses the validation helper methods of ActiveRecords which checks if the name of the student is not null or no symbols are present. It also checks whether the user with the same name already exists in the database as a new user is being created. The rest of the code is self-explanatory.
sample code for Input validation
class Student < ActiveRecord::Base validates_presence_of :name, :sex, :age, :weight validates_format_of :name, :with => /^\w+$/, :message => "is missing or invalid" validates_uniqueness_of :name, :on => :create, :message => "is already presnt" validates_inclusion_of :sex, :in => %w(M F), :message => 'must be M or F' validates_inclusion_of :age, :within => 18..40 validates_length_of :name, :allow_blank => false, :allow_nil => false, :maximum => 30 end
Without the validates helper method, the check for the format of the name can be done as
class Student < ActiveRecord:Base def validate unless name && name=~/^\w+$/ errors.add(:name, "is missing or invalid") end end end
[citation for : http://biodegradablegeek.com/2008/02/introduction-to-validations-validation-error-handling-in-rails/]
Improper Encoding or Escaping of Output
According to [citation needed : http://cwe.mitre.org/top25/#CWE-116], improper output encoding is the root for all injection basd attacks. The attacker can modify the output intended for other components in such a way that he /she can cause harm to the application, if the control information and metadata carried in the output is not properly seperated from the real data.
Failure to Preserve SQL Query Structure (aka 'SQL Injection')
If not much attention is paid as to how SQL queries are handled in the application code, attackers can modify / supply appropriate parameters so that they can gain access to sensitive information in the database such as Account database, which is not good from the security viewpoint. For example in Ruby, the following function poses a serious security threat if the value for the conditions method comes from an external source. The attacker can pass a SQL metacharacter as the input to breach his/her privilege.[citataion:book]
Users.find(:all, :conditions => "name like '%#{session[:user].name}'")
Rails recommends that in order to avoid SQL injections, never to substitute anything into an SQL statement using the ruby's #mechanism. Rails comes with a facility called blind variable for using in place of #. It is nothing but having a placeholder in place of #. If more than one placeholder is present in the query, ActiveRecord uses the corresponding input from the input array to substitute in the placeholder. The function from the above example can be replaced by [citataion:book]
Users.find(:all, :conditions => "name like '%?%'")
Failure to Preserve Web Page Structure (aka 'Cross-site Scripting')
Attackers are known to write simple javascript code in to an application's page using session cookies so that whenever a cookie destined for another legitimate user of the application is sent to that user, the attacker can redirect the cookie to be sent his/her computer. In this way an attacker can steal a legitimate users' identity. To prevent this Rails recommends that the application we create never blindly display any data coming from an external source to be blindly displayed on the application's page. It recommends the application to convert such HTML codes into plain texts berfore displaying.[citataion:book] Rails also comes with a helper method h(string) which is an alias for HTML escape, which performs the above mentioned escaping in Rails views. Rails also recommends using this helper method for any variable that is rendered in the view. The below code snippet shows how to render a user comment on to the application's view.[citataion:book]
Failure to Preserve OS Command Structure (aka 'OS Command Injection')
An attacker will try and execute execute malicious code when an appliaction uses data from the user as a parameter to execute system level commands resulting in breach of security. This is because an attacker can execute another command through the input to a first command by appending a semicolon(;) or a vertical bar (|). Ruby's exec(command), syscall(command), system(command) and \command are all vulnerable to such attacks. In order to avoid this Rails recommends programmers to use the system(command, parameters) method which passes command line parameters safely. For example, [citation : http://guides.rubyonrails.org/security.html#command-line-injection]
system("/bin/echo","hello; rm *") # prints "hello; rm *" and does not delete files
Cleartext Transmission of Sensitive Information
Cross-Site Request Forgery (CSRF)
Race Condition
Error Message Information Leak
CATEGORY: Porous Defenses
This category contains errors such as improper access control, Use of a Broken or Risky Cryptographic Algorithm , Hard-Coded Password, Use of Insufficiently Random Values and three more errors which will be covered in this section.
Use of a Broken or Risky Cryptographic Algorithm
Programmers who try to invent their own cryptographic environment for securing transfer of sensitive information mostly end up writing a broken or risky cryptographic algorithms. Brilliant mathematicians and scientists worldwide have broken their minds trying to perfect an algorithm which will be difficult to break. So a programmer is not expected to come up with a brand new algorithm. The programmer may be just re inventing the wheel and it results in an unnecessary risk that may lead to the disclosureor modifcation of sensitive information. Ruby allows easy implementation of advanced 128 and 256 bit algorithms such as AES. Ruby allows using Active record hooks to perform AES encryption and decryption. Ezcrypto and Sentry are two such hooks. EzCrypto is optimized for simple encryption and decryption of strings. There are encrypt/decrypt pairs for normal binary use as well as for Base64 [citation:http://en.wikipedia.org/wiki/Base6]) encoded use. It also provides a key class to generate keys as random or initialized with binary / Base 64 encoded data. An example piece of code for generating a random key and encrypting data using the generated key [citation:http://ezcrypto.rubyforge.org/]
@key=EzCrypto::Key.generate #to generate a random key @encrypted=@key.encrypt("clear text") # encrypting using previous gen key @decrypted=@key.decrypt(@encrypted)
Hard-Coded Password
Passwords are generally used in both inbound and outbound authentications.Having hard coded passwords in an application sounds naiive and we do not expect any programmer to do it. But when the constraints in terms of cost become tight, programmers do hard code passwords, even for sensitive accounts, within their application as it will reduce testing and support budget. If hard-coded passwords are used, it is almost certain that malicious users will gain access through the account in question. The likelyhood of the vulnerability being exploited is very high as mentioned in [citation:http://cwe.mitre.org/data/definitions/259.html]. Rails recommends handling passwords by generating a ramdom but unique salt value for each password and hashing them using 160-bit SHA hashing algorithm and then storing the hash in the database. The salt is created by concatenating a random number and the object id of the user object. The salt’s value is then stored in the model object’s salt attribute. [citation: book]
def create_new_salt self.salt=self.object_id.to_s + rand.to_s end
Then the SHA is computed with following piece of code [citation: book]
private def self.encrypted_password(password, salt)
string_to_hash = password + “wibble” + salt
Digest::SHA1.hexdigest(string_to_hash) End
When an incoming authentication request arrives, the entered password is appended with a salt and hashed using SHA1 and it is stored in the password attribute of the user object. This is then compared to the stored password for that user in the database to check whether it is correct or not.
Use of Insufficiently Random Values
Good randomness is necessary for the security features implemented in our web application using cryptographic algorithms to be effective and useful. If our application is using a normal Pseudo Random Number Generator [citation:http://en.wikipedia.org/wiki/Pseudo-random_number_generator] then it is easy for the attacker to guess the next random number after some number of trials. Randomness is needed to produce session IDs and for generating nonces to prevent replay attacks. Rails make sure that the random number generated is random enough so that the attacker has very less probability of guessing the random number. For example, for generating session IDs in Rails, first a random number is generated where the random string is the current time, a random number between 0 and 1, the process id number of the Ruby interpreter (also basically a random number) and a constant string. This random string is then hashed using MD5 and the resulting value is stored as session ID. The method to produce a random string is rand. If you needed a random integer to simulate a roll of a six-sided die, you'd use: 1 + rand(6). A roll in craps could be simulated with 2 + rand (6) + rand(6). Finally, if you just need a random float, just call rand with no arguments. [citation : http://www.codeodor.com/index.cfm/2007/3/25/Ruby-random-numbers/1042]
Client-Side Enforcement of Server-Side Security
Trusting clients to perform security checks on behalf of the server is not a good idea because the attackers can reverse engineer the client and can write their own custom clients to bypass authentication and authorization. To avoid this situation, Rails recommends that the authorization mechanism and input validation mechanism (refer to Improper Input validation from the section 2.1.1) be implemented properly.
Improper Access Control (Authorization)
If proper authorization mechanism is not implemented in the web appliaction then we are not making sure that the users do what they can do. Attackers exploit this loophole to gain access to protected and sensitive files which they modify or delete. Rails provides filters to make sure that only administrators have access to admin files and no user of the application can modify these files. beforefilter is used to intercept all calsl to the actions in the admin contoller. Here this function acts as an interceptor and checks the user id stored in the session to check if it is really the administrator who has requested the action. If yes, the action is allowed to go thorugh. Else, it is blocked. This method is included as a part of ApplicationController, the parent class of all the controllers in our Ruby application. Also the beforefilter method is restricted as a private method so that it is not visible to the end user or elkse the attacker can modify this method also. An example beforefilter method which checks for admin login for continuing the action and if not prompts the user to log in as an admin [citation: book]
class ApplicationController < ActionController::Base private def authorize unless User.find_by_id(session[:user_id]) flash[:notice]="log in as admin to proceed" redirect_to(:controller => "login", :action => "login") end end end
This method can be invoked before performing any actions in the adminisration controller by adding the line
before_filter :authorize
Execution with Unnecessary Privileges
This error falls under the category of providing Role Based Access Control[Citation:]. Therefore we need to provide proper authorization mechanisms for avoiding this error. Refer to Improper Access Control (Authorization) to see how this can be achieved.
Insecure Permission Assignment for Critical Resource
This error too is as a result of improper handling of authorization. Refer to the section Improper Access Control (Authorization) to see how this can be achieved.