CSC/ECE 517 Fall 2013/ch1 1w20 gq: Difference between revisions
Line 292: | Line 292: | ||
====''Download of Code Without Integrity Check (CWE-494)''==== | ====''Download of Code Without Integrity Check (CWE-494)''==== | ||
---- | ---- | ||
This is most common way to be attacked by attackers. We generally download codes from sites that we trust but hackers can hack the download site, convince the system to redirect to a different site, impersonate it with DNS spoofing or cache poisoning, or modify the code in transit as it crosses the network. This can also happen during update installation also. | This is most common way to be attacked by attackers. We generally download codes from sites that we trust but hackers can hack the download site, convince the system to redirect to a different site, impersonate it with DNS spoofing or cache poisoning<ref>http://www.securesphere.net/download/papers/dnsspoof.htm</ref>, or modify the code in transit as it crosses the network. This can also happen during update installation also. | ||
This can be prevented by encrypting the code with a reliable encryption scheme before transmitting or using proper forward and reverse DNS look-up to detect against DNS spoofing. Rails provides a simple way to confirm with file names before they are downloaded on to the client. For example, The ''send_file()'' method sends files from the server to the client. If you use a file name, that the user entered, without filtering, any file can be downloaded: | |||
send_file('/var/www/uploads/' + | |||
params[:filename]) | |||
Simply pass a file name like “../../../etc/passwd” to download the server’s login information. A simple solution against this, is to check that the requested file is in the expected directory: | |||
basename = | |||
File.expand_path(File.join(File.dirname(__FILE__), '../../files')) filename = | |||
File.expand_path(File.join(basename, @file.public_filename)) raise if basename | |||
=! File.expand_path(File.join(File.dirname(filename), '../../../')) send_file | |||
filename, :disposition => 'inline' | |||
====''Improper Resource Shutdown or Release (CWE-404)''==== | ====''Improper Resource Shutdown or Release (CWE-404)''==== |
Revision as of 20:43, 17 September 2013
This page explores the security in Rails-based web development, focusing on security features provided by Rails and the measures, as well as gems, that should be considered while developing a Rails application to keep it safe.
Background
Web application frameworks are made to help developers building web applications. In fact, however, one framework is not more secure than another: the Gartner Group estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources.
Ruby on Rails<ref>http://rubyonrails.org/</ref>, a MVC, has many features and some clever helper methods that automatically prevent security holes. There are additional tools and gems for Ruby and Rails that can further reduce the risks and handle some of the common programming errors<ref>http://www.sans.org/top25-software-errors/</ref>. 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.
Built-in features for security
- Safety levels (Ruby) - Ruby can limit how tainted (externally supplied) data is supplied. Meaningful values range from 0 to 4, with 0 being no safety check and increasing levels of security to 4 being the most secure (and most restrictive). Set with $SAFE = number in the Ruby program or the -T command from the command line.
- Parametric Polymorphism (Ruby) - This language feature can handle a wide variety of inputs without crashing, but unexpected inputs may be processed in unexpected ways, causing the need for greater input testing. Tools have been developed to perform this testing.
- protect_from_forgery command (Rails)
- Active Record for SQL Database Manipulation (Rails) - This is a built in class for securely accessing the SQL database.
- Automatically generated finder methods (Rails) - These methods access the database and are secure against SQL injection attacks
- Largely automated design (Ruby and Rails) - This helps reduce coding errors which mitigates some security issues
- Virtual machine (Ruby and Rails) - Code is run by a language interpreter rather than compiled and executed.
- Test case tools (Ruby and Rails) - Testing tools are used to find errors, some of which may be security issues. One tool is the Test Unit built in to Aptana's Ruby plug-in for Eclipse.
Tools and gems
- Tarantula - A tool that tests applications for common vulnerabilities. This is new and is still in development.
- R-spec - Another testing unit to be used with eclipse which provides documentation capabilities
- Clearance - A gem used to perform user login with hashed passwords
- Safe-ERB - A plugin used to counteract the Cross Site Scripting attack.
- Sandbox - Prevents damage to other applications
- Ruby-HMAC - Protects cookie information from unauthorized access.
Common security errors and the best practices
Insecure Interaction Between Components
Improper Input Validation (CWE-20)
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.Rails has a solution for this. The input validation can be done in model using Active Records which implements validation by overwriting Base#validate. The methods validates_presence_of, validates_inclusion_of, validates_confirmation_of, validates_length_of etc. are used for input validation. There are other methods like create_with_validation, update_with_validation or save_with_validation that can be used when specific operation is done.
For example, 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_length_of: 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 [1].
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 present" 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
Ruby and Rails also provides Test Unit which should be used to test for this. Polymorphism in Rails makes it very important to perform these checks. Rails has a plugin tarantula, a fuzzy spider. It crawls the rails application, fuzzing inputs and analyzing what comes back. 7
Improper Encoding or Escaping of Output (CWE-116)
Insufficient output encoding is the often-ignored sibling to poor input validation, but it is at the root of most injection-based attacks. An attacker can modify the commands that programmer intend to send to other components, possibly leading to a complete compromise of the application. When the program generates outputs to other components in the form of structured messages such as requests or queries, it needs to separate control information and metadata from the actual data. This is often ignored because many paradigms carry data and commands bundled together in the same stream, with only a few special characters enforcing the boundaries.
It is important to escape the output of web applications especially when re-displaying user input that was not input-filtered. Ruby uses escapeHTML() method to replace the HTML input characters &, ", <, > by their uninterpreted representations in HTML (&, ", <, and >). Rails' sanitize() method is a good solution to fend off encoding attacks. Output escaping is easily forgotten by programmer to code. Rails has the SafeErb plugin which reminds the programmer about output escaping if this is forgotten. 2
Failure to Preserve SQL Query Structure (CWE-89)' (aka 'SQL Injection')
Software applications are all about data. It is all about getting data into the database, pulling it from the database, massaging it into information, showing them in browser and sending it elsewhere. If attackers can influence the SQL that programmer use to communicate with the database, then they can do anything they want. If we use SQL queries in security controls such as authentication, attackers could even alter the logic of those queries to bypass security. They could modify the queries to steal, corrupt, or otherwise change the underlying data.
Rails has a solution for this. Popular goals of SQL injection attacks are to bypass authorization or carry out data manipulation or reading arbitrary data. SQL injection can also happen by influencing database queries by manipulating web application parameters. Ruby on Rails has a built in filter for special SQL characters, which will escape ’, ", NULL character and line breaks. Instead of passing a string to conditions options an array or an hash can be passed to sanitize tainted strings. Furthermore, Rails has predefined Active Record functions to access the database. These functions are designed to prevent SQL injection attacks from succeeding.
- Example: How ruby mitigates problem of bypassing authorization:
Use of User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")
Input of ’ OR ‘1’=‘1 as name, and ’ OR ’2’>’1 as password will create the following query:
"SELECT * FROM users WHERE login = OR '1'='1' AND password = OR '2'>'1' LIMIT 1"
The above query will find first record from user table and grant access to the user. In Ruby, Model.find(id) can be used in model to mitigate the problem of bypassing authorization. Array and hash are only available in model. There is one function sanitize_sql() which can be used in other places for this purpose. 2
Model.find(:first, :conditions => {:login => entered_user_name, :password => entered_password})
Failure to Preserve Web Page Structure (CWE-79)' (aka 'Cross-site Scripting(XSS)')
Due to stateless nature of HTTP, mixture of data and script in HTML or lots of data passing between web sites are very common. If programmers are not careful then attackers can inject Javascript or other browser-executable content into a web page that the application generates. Programmer's web page is then accessed by other users, whose browsers execute that malicious script.
This attack injects client side executable code. Cross site scripting can hijack the session, steal the cookie, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information, redirect the victim to a fake website or install malicious software through security holes in the web browser.
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 before displaying. It is also important to filter out malicious inputs and escape output of web application. Rather than blacklisting inputs it is good to create a white list describing the allowed values because blacklist never ends. Rails has the sanitize() method for this whitelist approach. sanitize() also defends encoding injection attacks. 2
- Example::
Attacker injects code to show an alert as follows strip_tags("some<script>alert('hello')</script>") Use of Rail's sanitize method tags = %w(a acronym b strong i em li ul ol h1 h2 h3 h4 h5 h6 blockquote br cite sub sup ins p) s = sanitize(user_input, :tags => tags, :attributes => %w(href title))
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[5].
Safe-ERB is another plugin developed specifically to counter this threat.
Failure to Preserve OS Command Structure (CWE-78)' (aka 'OS Command Injection')
Software applications are generally a bridge between an outsider on the network and the internals of the operating system. ---- An attacker will try and execute execute malicious code when an application 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. 2
- Example::
system("/bin/echo","Hello Sam; rm *")
It prints "Hello Sam; rm*". rm* doesn't work here.
Increasing the $SAFE parameter to 1 or greater should also reduce the potential damage of these attacks.
Since Ruby and Rails are run on a virtual machine, the code is one step removed from the operating system. However, the system command can be used to access operating system commands. As stated above, the system command has some protections, but it is likely to have some vulnerabilities as well.
Cleartext Transmission of Sensitive Information (CWE-319)
When software applications send sensitive information such as private data or authentication credentials across networks, the information crosses many different nodes in transit to its final destination. An attacker can gain access to it by controlling may be one node in the network.
For example, an attacker may find a username / password transmitted in cleartext and use this to do bad things. A SSL (Secure Socket Layer) plugin is available to prevent the cleartext transmission of sensitive information. To install it, issue the following command
ruby script/plugin install ssl_requirement.
And then include it at the top of your application.rb file, which effectively includes it in every controller:
include SslRequirement
For each controller that require SSL put the following code at the top of each controller
ssl_required :login, :account
Particular methods defined in the classes can also be protected methods as follows
ssl_required :method1 :method2
In the above scenario, any access to an action that requires SSL will automatically redirect to https://. If anyone tries to access a page that is supposed to be secure with an http:// link, they’ll be redirected. <ref>http://www.buildingwebapps.com/articles/6401-using-ssl-in-rails-applicati </ref>
ssl_allowed may be used to allow (but not require) the use of SSL. Note that TLS (Transport Layer Security) has replaced SSL, so this may be an outdated solution.
Consideration must also be given to not store sensitive information in the clear. Use Active Record hooks to perform AES128 encryption and decryption. Rails logging may log sensitive information, which could be accessed by attackers. Use the filter_parameter_logging :parameter1 :parameter2 to prevent logging of sensitive parameters. Clear sensitive data as soon as it is no longer needed [14]
Cross-Site Request Forgery '(CSRF)' (CWE-352)
CSRF happens by including malicious code or a link in a page that accesses a web application that the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands. Once the request reaches your server, it looks as if it came from the user, not the attacker. If the user has administrative privileges then the attacker also gains access to system critical resources and may cause serious problems.
Ruby on Rails is equipped to prevent this attack. Rails 2 or higher has a feature called protect_from_forgery which is specifically designed to combat attacks such as this. HTTP requests from browser generally provides two main types of requests – GET and POST. Some web browsers uses PUT and DELETE also but they are rare. Ruby has hidden_method field in order to handle this barrier. Use of GET and POST can prevent CSRF. A security token in non-GET requests will protect the application from CSRF. Ruby has a verify method which is defined in controller to make sure that specific actions may not be used over GET. 2 The following example to verifies the use of the transfer action over POST. If the action comes in using any other verb, it redirects to the list action.
- Example::
verify :method => :post, :only => [:transfer], :redirect_to => {:action => :list}
In the above example if the transfer action comes from any other web address, it redirects to the action list. So, CSRF will never happen. But only this will not protect from CSRF, because POST requests can be sent automatically. The solution to this is including a security token in non-GET requests which check on the server-side. In Rails 2 or higher, this is a one-liner in the application controller as follows
protect_from_forgery :secret => "123456789012345678901234567890..."
This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and requests generated by Rails. The secret won't be needed, if CookieStorage is used as session storage. It will raise an ActionController::InvalidAuthenticityToken error, if the security token doesn’t match what was expected. 2
Here is another example<ref>http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf</ref>:
- Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file.
<img src="http://www.webapp.com/project/1/destroy">
- Bob's session at www.webapp.com is still alive, because he didn't log out a few minutes ago.
- By viewing the post, the browser finds an image tag. It tries to load the suspected image from www.webapp.com. As explained before, it will also send along the cookie with the valid session id.
- The web application at www.webapp.com verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image.
- Bob doesn't notice the attack — but a few days later he finds out that project number one is gone. It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere – in a forum, blog post or email.
It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere – in a forum, blog post or email.
Race Condition (CWE-362)
Race condition is the scenario where multiple processes attempt to use the exact same shared resource at exactly the same time.<ref>http://encyclopedia2.thefreedictionary.com/race+conditio</ref> Ruby and Rails has testing tools (test unit / R-spec) which may be able to detect this. However, these testing tools may not catch such errors in all cases.
- Example::
Assume these two pieces of code can run in parallel Code section A Code Section B @balance = 500 @balance = @balance + 100 puts @balance
In the above example, the balance will usually read 500 from code section A. However, if the first line of code section A executes, then the Code Section B executes, then the print statement in A executes, the balance would read 600. The standard testing tools would most likely miss this error because it would happen very rarely.
Ruby provides the Mutex class 11 which can partially help avoid race conditions. The shared variable can be locked by one thread while it is being modified, then unlocked when the modification is done. This prevents some of the problems that can arise in race conditions, but can cause other problems, such as deadlock or livelock.
There does not appear to be a complete solution yet for the race condition in Ruby, or in any other languages.
Error Message Information Leak (CWE-209)
Improper error messages may leak out secrets to an attacker. The secrets could cover a wide range of valuable data, such as personally identifiable information (PII), authentication credentials, full installation path of the software, server configuration etc. A very common way of error message information leak is from log files. For any web application programmers should restrict detailed error messages to trusted users only. Programmer may use password encryption everywhere but if they come in clear text in log files then attacker can read that. Ruby has filter_parameter_logging method that can be written in controller to filter confidential parameter values. 2 A scaffold generated code uses the error_messages_for helper method which will display a single box at the top of the form showing all errors in the form. This method takes the model object as a parameter. The following is an example syntax for this method.
- Example:
<%= error_messages_for(:student)%> # student is the model object under consideration
Risky Resource Management
If the software within a web application does not properly manage the creation, usage, transfer, or destruction of important system resources, all the resources within an application are at risk of being exploited. This category contains errors such as Code Injection, Improper Resource Shutdown or Release , Failure to Constrain Operations within the Bounds of a Memory Buffer etc., and six other errors which will be covered in this section.
Failure to Constrain Operations within the Bounds of a Memory Buffer (CWE-119)'
If the memory accessible by the attacker can be effectively controlled, it may be possible to execute arbitrary code, as with a standard buffer overflow.
Suppose, if the attacker can overwrite a pointer's worth of memory (usually 32 or 64 bits), he can redirect a function pointer to his own malicious code. Even when the attacker can only modify a single byte arbitrary code execution can be possible. Sometimes this is because the same problem can be exploited repeatedly to the same effect. Other times it is because the attacker can overwrite security-critical application-specific data such as a flag indicating whether the user is an administrator. Applications written in languages like C or C++ which give direct access to memory are more prone to this kind of attacks. Rails automatically eliminates buffer overflow attacks since it is a strongly-typed programming language, meaning that it will disallow direct memory access and thereby usually prevent buffer overflows from happening. <ref>https://www.owasp.org/index.php/Buffer_Overflows </ref>
External Control of Critical State Data (CWE-642)
If you store that data in a place where an attacker can modify it, this also reduces the overhead for a successful compromise. Unprotected data such as cookie information, data stored in environment variables, registry keys, configuration files, log files or profile data are vulnerable to attack. While transmitting user state information over stateless protocol such as HTTP attackers can get access to it. If any security critical operation is done based on this data then the system will be vulnerable to attack. So it is very important to protect this information. A common way to do this is to cipher these data using hashed message authentication code(HMAC). Ruby has ruby-hmac interface to provide HMAC functionality. Based on a secret key, HMAC provides integrity check of information stored in or transmitted over an unreliable medium.
Rails suggests that all model requests are properly scoped, and for internal security, we need to make sure that confidential information isn’t leaking out in our logs, exception reports, and backups. The sessions which maintain the state for HTTP is maintained by the rails framework and provides several storage mechanisms for the session hashes. The most important are ActiveRecordStore and CookieStore.For this purpose ActiveRecordStore or CookieStore can be used. It is possible to configure the table name, primary key and data column in database table where cookie information or session information is to be stored. Information of these storage should be appended at the end of config/environment.rb: <ref>http://caboo.se/doc/classes/CGI/Session/ActiveRecordStore.html </ref>
CGI::Session::ActiveRecordStore::Session.table_name = 'legacy_session_table' CGI::Session::ActiveRecordStore::Session.primary_key = 'session_id' CGI::Session::ActiveRecordStore::Session.data_column_name = 'legacy_session_data'
Data store in cookie should be tamper-proof and signed because session data can be visible at client. Ruby solution for this is to sign session data using the below information in config.rb file.
Rails::Initializer.run do |config| config.action_controller.session = { :session_key => '_store_session', :secret => 'deri3337903j3h9our9k1heu9w8ejqwk82' } end
Here the secret is encrypted which makes the data impossible to read.
Even the above process can made to be more secure by using the SHA512 algorithm and using separate secret keys for each user. <ref>http://www.rorsecurity.info/2007/11/20/rails-20-cookies/</ref>. There is no need to pass unencrypted data directly.
config.action_controller.session = { :digest => 'SHA512', :secret => Proc.new { User.current.secret_key } }
Rails also provides secure logging to protect log files. We can filter certain request parameters from your log files by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log.
filter_parameter_logging :password
External Control of File Name or Path
When you use an outsider's input while constructing a filename, the resulting path could point outside of the intended directory. An attacker could combine multiple ".." or similar sequences to cause the operating system to navigate out of the restricted directory. Other file-related attacks are simplified by external control of a filename, such as symbolic link following, which causes your application to read or modify files that the attacker can't access directly.
This vulnerability can be overcome by using $SAFE, which is a global security parameter that Ruby provides. $SAFE controls a number of security features in the Ruby programming language. The higher you raise $SAFE, the more restrictions the Ruby interpreter puts on your program. The $SAFE level can only be raised, not lowered; malicious code cannot lower the $SAFE level to escalate privileges.
There are 5 $SAFE levels, represented by the integers 0 through 4. To set a $SAFE level, assign an integer to $SAFE. By default, $SAFE is set to 0. At this level, no extra checks will be performed by the Ruby interpreter. If, when you try to assign an integer to $SAFE that is less than the current value of $SAFE, a SecurityError exception will be raised. In addition, you can run Ruby scripts with a higher $SAFE level by supplying the -T switch to the Ruby interpreter.
$SAFE level 1 introduces the concept of tainted objects. Objects that accept input from external sources (files, sockets, environment variables, etc) will have the tainted flag set. This flag will follow all copies of the object, no matter how many times you filter or copy this object, it will always remain tainted. There are two methods that will help you manage tainted objects; Object#tainted? returns true if an object has the tainted flag set, and Object#untaint untaints the object.
Ruby will refuse to accept tainted objects as arguments to some "dangerous" methods. For example, you can't pass a tainted object to require or load, or open a file whose filename is a tainted object. This forces you to properly sanitize all input and explicitly untaint it before you can use it. It's also easy to add a tainted check to your methods; if any arguments you'd like to be untainted fail the tainted? check, raise a SecurityError exception.
begin # Raise the security level $SAFE = 1 # Read from $stdin, then try to open a file with a tainted object path = gets.strip puts "path is tainted" if path.tainted? f = File.open( path ) rescue SecurityError => e puts "Caught security exception: #{e}" end
% ./taint2.rb path is tainted Caught security exception: Insecure operation - initialize
One cannot open files whose names are tainted strings.$SAFE level one alone can make your Ruby programs much more secure. It forces you to be vigilant with input validation.
Untrusted Search Path (CWE-426)
Applications depend on its environment, to provide a search path so that it knows where it can find critical resources such as code libraries or configuration files. If the search path is under attacker control, then the attacker can modify it to point to any malicious code. This causes the software to access the wrong resource at the wrong time.
Again, the Ruby $SAFE parameter can be used to protect against this by increasing it to a proper value.
Failure to Control Generation of Code (CWE-94) (aka 'Code Injection')
For the ease of development it is general trick to dynamically generate codes. It becomes a serious vulnerability when the code is directly callable by unauthorized parties who can contaminate inputs to the code and the result may be disastrous.
Ruby allows this, but only if the developer writes the program to allow it. If such functionality is provided in an application, it should be tested extensively. As Ruby is dynamically typed language, for dynamic testing Ruby has <ref>http://github.com/relevance/tarantula </ref> which can crawl up the application and look for any break. Input validation should be done using an white box approach i.e. test those inputs which are allowed rather than testing those which are blacklisted because blacklist never ends. Ruby has very strong feature for input validation. See the Input validation section for this. Another mitigation is that Ruby is run in a virtual machine. This somewhat limits the damage that could be done (e.g. someone should not be able to format your C: drive (erase everything) from a remote site through a ruby application. There is still significant risk in allowing users to add their own code, and this functionality should be used sparingly, and tested thoroughly.
Download of Code Without Integrity Check (CWE-494)
This is most common way to be attacked by attackers. We generally download codes from sites that we trust but hackers can hack the download site, convince the system to redirect to a different site, impersonate it with DNS spoofing or cache poisoning<ref>http://www.securesphere.net/download/papers/dnsspoof.htm</ref>, or modify the code in transit as it crosses the network. This can also happen during update installation also.
This can be prevented by encrypting the code with a reliable encryption scheme before transmitting or using proper forward and reverse DNS look-up to detect against DNS spoofing. Rails provides a simple way to confirm with file names before they are downloaded on to the client. For example, The send_file() method sends files from the server to the client. If you use a file name, that the user entered, without filtering, any file can be downloaded:
send_file('/var/www/uploads/' + params[:filename])
Simply pass a file name like “../../../etc/passwd” to download the server’s login information. A simple solution against this, is to check that the requested file is in the expected directory:
basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files')) filename = File.expand_path(File.join(basename, @file.public_filename)) raise if basename =! File.expand_path(File.join(File.dirname(filename), '../../../')) send_file filename, :disposition => 'inline'
Improper Resource Shutdown or Release (CWE-404)
When system resources have reached their end-of-life, it is important to dispose of them correctly. Otherwise, the environment may become heavily congested or contaminated. Attack can happen from memory leak, freeing invalid pointer, double-freeing etc. Attackers can exploit improper shutdown to maintain control over all those resources the programmer thinks he got rid of them. This can lead to significant resource consumption because nothing actually gets released back to the system.
Ruby has Memtrack API's to defend this attack. Patches MRI Ruby 1.8.7p72 can be used to add heap dumping, object reference finder, stack dumping or object allocation/deallocation tracking etc. 8 There are plugins available for Ruby to detect memory leak such as Bleakhouse. Bleakhouse detects memory leaks by hammering the ObjectSpace for information throughout the execution of the application and by producing pretty charts to show what's going on.9
Another way to defend any attack due to memory leak is to log memory usage information before and after each request (but it is only for Linux). The following code is an example for this. 21
#Put this in applictation_controller.rb before_filter :log_ram # or use after_filter def log_ram logger.warn 'RAM USAGE: ' + `pmap #{Process.pid} | tail -1`[10,40].strip end
Improper Initialization (CWE-665)
If proper initialization of data and variables are not done then attackers can extract sensitive information that remain from previous sessions. Attackers can modify these variables and break security when they are used in security-critical operations such as making an authentication decision. In Ruby "nothing" is an object, so uninitialized instance variables points to nil object by default rather than taking garbage values. Ruby also has fuzzy crawler Tarantula to defend against this. Proper testing can reveal this kind of bug in the code. Ruby has Test unit and R-spec for efficient testing.
Incorrect Calculation CWE-682)
Computers can perform calculations whose results don't seem to make mathematical sense. For example, while multiplying two large, positive numbers, the result might be a much smaller number due to an integer overflow. Other calculations like divide-by-zero is impossible to perform. When attackers have some control over the inputs that are used in numeric calculations, it could be caused to make incorrect security decisions. It might be caused to allocate far more resources than intended - or maybe far fewer, as in the case of integer overflows that trigger buffer overflows due to insufficient memory allocation. It could violate business logic, such as a calculation that produces a negative price. Finally, denial of service is also possible, such as a divide-by-zero that triggers a program crash. 1
Test tools may be used to spot these errors. Test unit is provided with Rails, and R-spec is another test tool which may be used to mitigate this.
Porous Defenses
Use of a Broken or Risky Cryptographic Algorithm (CWE-327)
Sometimes programmers are tempted to develop their own encryption scheme expecting that it will be difficult for attackers to crack. But this is just an illusion because cryptography design is very hard. Attacker always get trick to decrypt data.
The solution is to use industry standard cryptographic algorithm which is proven to be the best in current time. There are AES 128 encryption and SSL support in Ruby and Rails that provide secure cryptographic algorithms.
- Example: AES Encryption decryption using OpenSSLlibrary 22
def aes(m,k,t) (aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(k) aes.update(t) << aes.final end def encrypt(key, text) aes(:encrypt, key, text) end def decrypt(key, text) aes(:decrypt, key, text) end if $0 == __FILE__ p "text" == decrypt("key", encrypt("key", "text")) end
Hard-Coded Password (CWE-259)
Password hard coding makes application prone to attacks. If same password is used allover the application then it increases the chance of attacks to spread to every user because password is very easy to guess.
Solution for this is to store password in very secure place like database and give proper privileges to these tables. In Ruby password management can be done by generating a unique and random salt value for each password and hashing them using hash algorithm SHA512 and store the hash in database. It mixes the salt and hash to make it impossible to decode passwords. Rails also has Clearance and Cucumber plugins for this. Clearance is a tool for authentication with e-mail and password. Authentication check may be required as the application evolves and cucumber is used for that. 10
- Example:: Example shows part of code to create salt, hash and mix them for storage 23
# Generates salt def Password.salt salt = .. 64.times { salt << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr } salt end # Generates a 128 character hash def Password.hash(password,salt) Digest::SHA512.hexdigest("#{password}:#{salt}") end # Mixes the hash and salt together for storage def Password.store(hash, salt) hash + salt end
Insecure Permission Assignment for Critical Resource (CWE-732)
Attack may happen on data stores, critical programs or configuration files if they have unnecessary permissions that make the resources readable or writable by the world.
Configuration store such as windows registry should be protected and permission given on all objects created in file system should be reviewed. This is mostly a manual effort during system design.
Use of Insufficiently Random Values (CWE-330)
For most security features, good random numbers are required. Some random number generators do not generate sufficiently random numbers. If attackers can predict the next random number to generated then they can crack the security boundary.
Ruby provides the rand() function, but the default random functions are generally not sufficiently random. Other random number generators, such as these 12 have been developed in Ruby. It is difficult to determine if these, or the default rand() are sufficiently random for the particular application.
Execution with Unnecessary Privileges (CWE-250)
Sometimes the application may need extra privileges to perform certain tasks. But running applications with extra privileges, will give the application access to resources that the application's user can't directly reach. This may turn out to dangerous consequences. Developers should use the minimum set of privileges required and reduce privileges when they are no longer needed. Authorization should also be checked during login to application for secure access. Ruby has before and after filters for validations which can be used.
Client-Side Enforcement of Server-Side Security (CWE-602)
Application will be vulnerable to this attack if it trusts client to do the authentication checking. Attackers can simply reverse engineer the client and write their own custom clients that can cause security hazards. The consequences will vary depending on what the security check of the application is protecting, but some of the more common targets are authentication, authorization, and input validation. If security is implemented on servers then it is to be assured that application will not solely relying on the clients to enforce it.
Active record (within rails) provides the attr_protected method which is used to protect records of the database. This allows the developer to specify which attributes to protect. Alternatively, attr_accessible can be used to protect everything except the values listed. Tarantula may detect this vulnerability through its fuzzed inputs test.
Ruby and Rails Security Comparison with other Frameworks
Ruby and Rails security
Ruby and Rails are fairly new. Ruby was released in 1995 25 and Rails was released in 2004 26. Most other languages have been around for longer so security tools have had more time for improvement. Ruby and Rails were built with security in mind, not as an afterthought. Ruby has methods for dealing with the most common security errors, but many these methods are not widely known. It would be beneficial if more of the best security plugins and tools were incorporated into the main distributions of Ruby and Rails. Since Rails is built on Ruby, it should inherit the security features of Ruby. There are additional security features of Rails, so Rails should be more secure than Ruby alone.
Security tools are currently being developed to combat many of the common security errors, but many of these tools have not reached maturity. Some are tools difficult to locate, install or use. Some are not yet well developed and are likely to miss common flaws. If security tools continue to be developed and incorporated into Ruby and Rails, then it will likely be one of the most secure langauges. Many languages were not originally developed with security in mind.
Comparison to other platforms
Since there are so many facets to security, it is difficult to say which programming language is the most secure. However, out of the common languages, it appears Ruby and Rails ranks fairly high. What follows is a comparison of Ruby and some of the more notable platforms.
The E programming language was developed specifically as a secure programming language. While security was not the main focus of Ruby and Rails, it certainly was a strong consideration. E was developed two years later than Ruby, in 1997 27. E is not among the top 50 used programming languages 28, so it has not been used and tested nearly as much as Ruby. Because Ruby has had additional testing, it is most likely that Ruby (and Rails) is more secure, although E was designed as a secure language.
The C++ programming language is among the most popular, and has been around for many years, since 1979 29. In the list of most popular languages, as of September 2009, C++ ranks fourth and C ranks second 28. When C++ was developed, security was not as large of a concern, and security was not a main focus. However, there have been tools developed for C++ as there have been for Ruby, such as these 33. Since C++ has been around for a longer and security tools have had longer to mature, it is most likely that C++ can be more secure than Ruby with the use of new tools such as these. However, Ruby and Rails has more built in security than C++. Applications developed in C++ are likely less secure than those developed in Ruby unless great care is taken to use the right security tools.
The Java programming language is the most popular language as of September 2009 28. Java has existed since 1995 and security is in one of its five goals 30. Because Java is so widely used, it has been extensively tested. Security breaches in Java appear to be rare, and several security holes have been fixed 31. Because of the extensive testing, widespread use and lack of reported problems, Java is likely more secure than Ruby. It is quite likely that Java is the most secure programming language.
PHP is (as of September 2009) the third most popular programming language 28. PHP was developed in 1995, same as Ruby. Security fixes have been made to this scripting language. However, over 1/3 of the currently known security vulnerabilities are in PHP! This would appear to be one of the least secure languages, although it is said that most of these vulnerabilities are from programmers not following best security practices 32. Ruby is likely far more secure than PHP.
Below we compare some of the security features of Rails with two of the most common application frameworks, Springs and Struts.
Security Feature | Rails (Ruby) | Spring (Java) | Struts (Java) |
---|---|---|---|
Authorization | a beforefilter is used to intercept all calls to the actions to check the user and his/her role before the action is processed | a security manager level exists between the caller and service which intercepts the request and checks for access rights of the caller. | implemented in the RequestProcessor portion of the "Controller" via the RequestProcessor.processRoles method |
Input Validation | uses the helper class validates of ActiveRecord | Spring uses a validator interface to validate objects. A validation class is written implementing this interface so that its features can be used in the validation class. | There is a validator plug-in and after it is loaded into the application, have to extend org.apache.struts.validator.action.ValidatorForm instead of org.apache.struts.action.ActionForm.Then when the validate method is called, the action's name attribute from the struts-config.xml is used to load the validations for the current form.
|
Race Condition | Uses no shared architecture to avoid race condition, hence easy and elegant design to implement thread safe code. | Uses Data access abstraction to prevent race condition. designing thread safe methods not as elegant as rails | Methods accessing shard objects have to explicitly take care of cached values of objects(values cannot be stored in field but rather always passed as parameters) in order to make it thread safe.So not an elegant solution |
SQL injection attack: | could be still be performed if vulnerable sql statements are constructed by the programmer manually. | Cannot be performed | Cannot be performed |
Storage of session data: | On disk or database, inherently less secure since person with access to server can view private files on the server | stores session data in memory, hence more secure for against this particular vulnerability | stores session data in memory, hence more secure for against this particular vulnerability |
CSRF attacks: | provides good defense against CSRF by inclusion of tokens. Rails MVC has enough official clear documentations to prevent it | Spring MVC documentation doesnt provide clear guidance in defending against these attacks | Struts has enough documentation. Provides the tokenSessionInterpreter to prevent CSRF attacks |
Validation Integration with Model Object | A fundamental idea in object programming is to unite data with related actions. In rails, the Model Object should typically encapsulate both data and validations. | Spring MVC takes validation out of your Model Objects (its proper home), and moves it into a separate class having a single method | In Struts 1, data validation is completely separated from the Model Object. |
Appendix
Vulnerability: Susceptibility to attack. A detailed description can be found here
SHA512: A cryptographic hash algorithm which uses hash algorithm to encrypt data. A detailed description can be found here
Race condition: Situation when two operations on shared resources collide and create confusion. A detailed definition can be found here
Framework: A framework in software makes code development easier and may automate certain portions of code development. A detailed description can be found here
Salt: Extra bits automatically added to the password before it is encrypted. This process is not visible to the user. Salt is used to increase system security by reducing the effectiveness of password cracking attacks. A detailed description can be found here
References
<references/>