Wednesday, 22 September 2010

Stuxnet - Going from virtual attacks to physical

Amazing article on Stuxnet, a piece of malware so complex that it's taken four months just to decipher it's purpose. Which turns out to be... attacking an Iranian nuclear power plant. So this piece of malware operating in the virtual world is intended to destroy a physical plant in the real world. Methinks we will be seeing more of this type of thing.

Posted by jherrington at 9:42 AM in News

Rails Data Security

Last week I wrote about the SQL Injection potential in Rails. The conclusion there was to write ActiveRecord code the Rails way and everything will turn out alright. To continue on that theme I'm going to take a look at how use ActiveRecord the right way to impose limits on data visibility.

Take the schema below as an example:

Here we have two tables; users and groups. A user owns an asset and should only be able to see assets they own. Simple enough. The ActiveRecord code for the basics is shown below:

require 'rubygems'
require 'active_record'
 
ActiveRecord::Base.establish_connection(
	  :adapter  => 'mysql', :database => 'assets',
	  :username => 'root', :password => '',
	  :host     => 'localhost' )
ActiveRecord::Base.logger = Logger.new(STDOUT)

class User < ActiveRecord::Base
  has_many :assets
end

class Asset < ActiveRecord::Base
  belongs_to :user
end

So now I have a User and an Asset class that wraps the table. Just to make sure it's working I first get all of the users, and the user named 'Jack' with the good code below:

# Good
p User.find(:all)
print "\n"

# Good
p User.find_by_name('jack')
print "\n"

This works, so now I can demonstrate how to find the assets for just me in the next code block:

# Good
User.find_by_name('jack').assets.each { |gi| p gi }
print "\n"

ActiveRecord automatically does the user_id restriction for me on the groups table like so:

SELECT * FROM `users` WHERE (`users`.`name` = 'jack') LIMIT 1
SELECT * FROM `assets` WHERE (`assets`.user_id = 1) 

So I didn't have to do any of the ID magic. I just tell ActiveRecord that a user has many assets and that assets belong to users and it does all the rest.

The same thing happens when I get the user associated with a particular asset like so:

# Good
p Asset.find(1).user
print "\n"

ActiveRecord then generates this code:

SELECT * FROM `assets` WHERE (`assets`.`id` = 1)
SELECT * FROM `users` WHERE (`users`.`id` = 1)

And once again, I don't have to do any of the ID magic work.

If you don't use has_many, has_one, belongs_to or any of that you will end up writing code like this:

# Bad
p Asset.find_all_by_user_id( 1 )
print "\n"

In this case I'm using a find on asset, but I'm specifying a value for user_id. You don't need to do that. Just get the user object then use it's assets member variable.

The same kind of issue is in this code:

# Bad
p Asset.find( :all, :conditions => [ 'user_id=?', 1 ] )
print "\n"

Again, we don't need to go that far with ActiveRecord. Let it do the work for us.

Here is another example where I use the user_id on an Asset record to do a find on the user.

# Bad
p User.find( Asset.find( 1 ).user_id )
print "\n"

The issue with all of the bad code is that I'm doing too much and not letting ActiveRecord do what it does best. And when I do all the work there is a potential that I might forget to put in the restrictions, which might end up giving one user access to the assets of another.

ActiveRecord has a very powerful model system. When it's used appropriately it will enforce data visibility restrictions for you.

Posted by jherrington at 8:42 AM in Fortify