Sending Ruby to the jail: an attemp on a Haml Safemode

posted: February 17th, 2008 · by: Sven

in: Programming · tagged as: , , , , ·  14 comments »

For my intial speculations about the feasibility of a Haml safemode as an alternative for Liquid I got a bold 'No!'.sub(/o/){|c| c * 46} by Ryan Davis. Ouch! Also, Peter Cooper initially commented rather sceptically …

You guys were right that my first thoughts didn’t go far enough with just looking for certain syntax node types. But hey! There’s still hope. :)

In the meantime I’ve implemented an experimental attemp on a safemode plugin for Haml which takes a bit different approach and certainly does more to get its job done better.

Find the code here: http://svn.artweb-design.de/stuff/rails/haml_safemode.

So how does this work?

Basically I’ve switched to an approach where the “precompiled” Ruby code from a Haml template gets parsed (using ParseTree and Ruby2Ruby) and all method calls get “jailed” with only whitelisted methods allowed (think: Rails blankslate plus methods access control).

For example the code:

@article.title.upcase

in a Haml template would be converted to this:

@article.to_jail.title.to_jail.upcase

The method to_jail wraps the returned objects into Jail instances which are proxy objects with almost all methods removed. These proxies allow access only to whitelisted methods and raise an exception if someone tries to access something else. To instantiate this object it looks for a class Jail within the object’s class’ namespace (e.g. Article::Jail).

So, to actually allow to do something useful in Haml templates these Jail subclasses have to be implemented for an application’s models. (I.e. this is basically Liquid’s concept of so called drops.) For most of Ruby’s native classes like String, Fixnum, Array, Hash etc. the plugin will do that for us (see this file for a list of methods that will be allowed for certain native Ruby classes).

Also, the resulting Ruby code will be evaluated with a ScopeObject’s binding which shares the same blankslate-like base class with the jails. This object forbids access to almost everything but additionally passes calls to Rails helper methods back to the ActionView instance. So we can use helpers, too.

Compared to Liquid jails play the role of drops (I chose a different name to avoid any name clashes). Liquid’s filters actually become obsolete as they essentially play the role of calling methods on objects (e.g. formatting a date) which we can do in native Ruby here.

Because Haml uses Rails’ “compiled templates” architecture all the ParseTree parsing and Ruby2Ruby re-assembling only takes place once when the template is “compiled”. The result will be cached in memory and reused for subsequent requests.

Thus I wouldn’t be surprised when this Haml Safemode actually proves faster compared to Liquid which (unless I’m totally mistaken) does it’s RegExp work each and every time when the template gets rendered.

Now what?

So, this approach should be rather restrictive than permissive. All attack vectors I could think of pass the tests (i.e. they raise an exception). But of course I’m not sure whether it’s already waterproof.

I am by no means a security expert and definitely lack some “black creativity”. So I need your help with this!

Like I said, this code is highly experimental. Do not rely on it or use it for any serious purpose beyond playing with it, yet!

That said, this code is experimental :)

Please do experiment with it!

Check it out, hammer it with all your evil creativity and try to find any working attack vectors. I totally lack this kind of skill, so I’m sure there’s something to improve.

Many thanks to Peter Cooper who was so kind to already (briefly) review the code to catch any obvious bugs and check out the test cases.

Leave a comment

14 Comments

  1. Tiago Bastos said February 18th, 2008 at 01:11 PM  

    Nice work!

  2. rewt said February 19th, 2008 at 04:29 AM  

    wrwf

  3. Eric Hodel said February 21st, 2008 at 10:41 AM  

    What is ruby2rubypatch.rb about? Why isn’t processcall overridden in Haml::Safemode::Parser?

  4. Sven said February 21st, 2008 at 12:30 PM  

    Hi Eric,

    ruby2ruby_patch.rb splits up process_call into several more fine-grained methods (to avoid the need of overwriting the whole, pretty long method). One of these (process_call_receiver) is then overridden in Haml::Safemode::Parser. So, in effect, process_call actually is modified in the parser (it prepends the call to to_jail to the call receiver).

    Does that answer your question?

  5. Eric Hodel said February 21st, 2008 at 07:44 PM  

    You haven’t avoided overwriting Ruby2Ruby#process_call, you’ve explicitly overwritten it, just in a different file and class.

    It would be better to pull the whole thing down into your subclass so you don’t pollute the namespace of other users of Ruby2Ruby.

  6. Sven said February 21st, 2008 at 10:12 PM  

    Hi Eric,

    depends how you look at it. If this change would go into Ruby2Ruby I wouldn’t. ;)

    You’re right that things didn’t make a lot of sense the way they’ve been. I’ve moved that stuff to the parser class.

    Thanks for the suggestion.

  7. Peter Cooper said February 23rd, 2008 at 04:36 PM  

    You need to comment out:

    require ‘ruby2ruby_patch’

    in the test initializer, since it isn’t in the repository anymore.

  8. Sven said March 3rd, 2008 at 08:20 PM  

    Hi Peter,

    thanks for the catch. I’ve just done that. Also, I’ve added a MIT license.

  9. Flurin said March 11th, 2008 at 08:24 AM  

    Hi Sven,

    How is the progress on haml_safemode? I’m very interesting in doing a similar thing with the precombiled ruby code ERB generates.

    Did your approach hold against the various “attacks”?

    Flurin

  10. Sven said March 12th, 2008 at 02:07 AM  

    Hi Flurin!

    Actually there hasn’t been too much process. Neither attacks I would have learned of. Maybe that’s a good sign as this article has received quite some attention. But maybe it just means that nobody cared enough to really check things out.

    I’ve had some conversation with Peter though regarding using it for ERB (and pretty much any Ruby eval). This most probably will raise some more attention and … hopefully … testing.

    If you’re interested in getting involved into this, please drop me a note by email.

  11. refinance said May 9th, 2009 at 01:53 AM  

    what is loans? :)

  12. money said May 9th, 2009 at 03:11 PM  

    what is finance? :)

  13. Andrew Townley said November 27th, 2009 at 07:42 PM  

    Hi Sven,

    This is really interesting as I’m looking at doing some dynamic templating in a web application as well. However, it doesn’t use precompiled templates. Basically, in order to make it work the way it needs to, it does instance_eval because these are really like macros as far as the application goes. Since the possibility of certain users to edit them is a requirement, your solution seems quite intriguing.

    Did you and Peter take this further? If not, what would it take to move your conversation forward? I’m going to need this in the near future, and I’d like to avoid reinventing the wheel. I’ll send you an email as well.

    I’ll grab the code and see if I can figure out how to integrate it into what I have. I’m not using Haml or anything else. I just need this kind of functionality applied to arbitrary strings of ruby code.

    It would be very interesting to know the limits of an approach like this.

    Thanks for the post! :)

    Cheers,

    ast

  14. Tyler Rick said January 1st, 2010 at 01:35 AM  

    This looks very interesting indeed. I wish there were more exposure for this and more people out there using and talking about it.

    I’m looking at options for making parts of a site editable… Since I’m already using HAML for my templates, I’d hate to have to install Liquid in addition to HAML just for this purpose, especially given HAML’s superior syntax (although I have to wonder if non-programmers will appreciate/understand HAML’s syntax?).

    Anyway, hopefully I’ll get a chance to take a closer look at this soon…

Leave a comment

Name required
E-Mail and Website optional

If you can read this, you don't use a typical webbrowser that plays nice with CSS.
Please do not fill in anything here!

Hint: Markdown will be applied to your comment. If you post any code, be sure to escape underscores (like so: \_) if you do not want them to be converted to an <em>phasis.

artweb design
Sven Fuchs
Grünberger Str. 65
10245 Berlin, Germany


http://www.artweb-design.de

Fon +49 (30) 47 98 69 96
Fax +49 (30) 47 98 69 97