Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Why Rails is Hard

Why Rails is Hard

Yehuda Katz

April 30, 2012
Tweet

More Decks by Yehuda Katz

Other Decks in Technology

Transcript

  1. [T]here are known knowns; there are things we know we

    know. We also know there are known unknowns; that is to say we know there are some things we do not know. But there are also unknown unknowns – there are things we do not know we don't know. “ DONALD RUMSFELD
  2. EQUIVALENT? web browser passenger / unicorn rails router rails action

    web browser node server node router node app =
  3. This middleware requires session support, thus should be added somewhere

    below session() and cookieParser(). “ CONNECT DOCS
  4. By default this middleware generates a token named "_csrf" which

    should be added to requests which mutate state, within a hidden form eld, query-string etc. This token is validated against the visitor's req.session._csrf property. “ CONNECT DOCS
  5. class CashTransferController def create authorize! current_user, params transfer = CashTransfer.new(

    params[:from_account], params[:to_account], params[:amount] ) queue.push transfer end end WHAT IS CSRF?
  6. class CashTransferController def create authorize! current_user, params transfer = CashTransfer.new(

    params[:from_account], params[:to_account], params[:amount] ) queue.push transfer end end WHAT IS CSRF?
  7. <html> <h1>Send Money!</h1> <form action='bank.com/transfer'> <input type='text' name='from' value='me'> <input

    type='text' name='to' value='you'> <input type='text' name='amount' value='1biiiiiilliondollars'> <button type="submit">Transfer</button> </form> </html> BANK.COM
  8. var form = "<form action='bank.com/transfer'>" + "<input type='text' name='from' value='me'>"

    + "<input type='text' name='to' value='you'>" + "<input type='text' name='amount' value='1biiiiiilliondollars'>" + "<button type="submit">hehehehe</button>" + "</form>"; $(form).appendTo("body").submit(); EVIL.COM.
  9. <html> <h1>Send Money!</h1> <form action='bank.com/transfer'> <input type='hidden' name='csrf_token' value='b674005056434a48054707d'> <input

    type='text' name='from' value='me'> <input type='text' name='to' value='you'> <input type='text' name='amount' value='1biiiiiilliondollars'> <button type="submit">Transfer</button> </form> </html> MITIGATION.
  10. ▪ On by default ▪ Works for all common use-cases

    ▪ HTML forms ▪ Ajax requests ▪ API clients ▪ Not vulnerable to known attacks ▪ Earlier "known good" approaches turned out vulnerable to Flash exploit FRAMEWORK GOALS.
  11. <%= form_for @cash_transfer do |f| %> <%= f.text_field :from %>

    <%= f.text_field :to %> <%= f.text_field :amount %> <%= button "Transfer!" %> <% end %> AUTOMATIC.
  12. AVOID DISABLING. I've searched around SO and saw some information

    about turning off the CSRF check for my view via the csrf_exempt decorator, but I nd that unappealing
  13. Generated index.html.erb: <head> <%= csrf_meta_tags %> <%= javascript_include_tag "application" %>

    </head> Included rails.js: $.ajaxPrefilter(function(options, originalOptions, xhr) { if ( !options.crossDomain ) { rails.CSRFProtection(xhr); } }); rails.CSRFProtection = function(xhr) { var token = $('meta[name="csrf-token"]').attr('content'); if (token) { xhr.setRequestHeader('X-CSRF-Token', token); } }; RAILS.
  14. # share 7 bits of ASCII "Yehuda".encode("UTF-8").bytes.to_a == "Yehuda".encode("ISO-8859-1").bytes.to_a =>

    true # differ on the remaining bit "Yehüda".encode("UTF-8").bytes.to_a == "Yehüda".encode("ISO-8859-1").bytes.to_a => false UTF-8 VS. LATIN-1.
  15. # What does this mean? "\x59\x65\x68\xFC\x64\x61" # Without more metadata,

    unknown # ... we can try to guess ... BINARY DATA.
  16. SELECT * from users where id=1; |id|name | ----------- |

    1|Yehüda| SHOW VARIABLES LIKE "character\_set \_database"; |Variable_name |Value| ------------------------------- |character_set_database|utf8 | DATABASES.
  17. SELECT * from users where id=1; |id|name | ----------- |

    1|Yehüda| "\x59\x65\x68\xC3xBC\x64\x61". force_encoding("UTF-8") DATABASES.
  18. # For Ruby 1.9, UTF-8 is the default # internal

    and external encoding. Encoding.default_external = "UTF-8" Encoding.default_internal = "UTF-8" # DON'T do this in libraries! RAILTIES/RAILS.RB
  19. # Assume that templates are in the # default_external, which

    defaults to # UTF-8. encoding = Encoding.default_external source.force_encoding(encoding) DEFAULT_EXTERNAL.
  20. # look for a magic encoding comment # this works

    with any template engine! if source.sub!(/\A#{ENCODING_FLAG}/, '') encoding = magic_encoding = $1 else encoding = Encoding.default_external end # Tag the source with the default # external encoding or the encoding # specified in the file source.force_encoding(encoding) EXCEPT...
  21. ENCODING_TAG = Regexp.new( "\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*") # the handler has its

    own magic comment format def handles_encoding? true end template_source = template.source.dup.force_encoding("BINARY") # find any magic comment and extract the encoding erb = template_source.gsub(ENCODING_TAG, '') encoding = $2 # confirm that the encoding provided is valid and flag it erb.force_encoding valid_encoding( template.source.dup, encoding) # Always make sure we return a String # in the default_internal erb.encode! AND EXCEPT...
  22. # Now, validate that the source we got # back

    from the template handler is valid # in the default_internal. This is for # handlers that handle encoding but screw # up unless source.valid_encoding? raise WrongEncodingError.new( @source, Encoding.default_internal) end VALIDATE IT.
  23. class WrongEncodingError < EncodingError def initialize(string, encoding) @string, @encoding =

    string, encoding end def message @string.force_encoding("BINARY") "Your template was not saved as " \ "valid #{@encoding}. Please either " \ "specify #{@encoding} as the encoding " \ "for your template in your text " \ "editor,or mark the template with its " \ "encoding by inserting the following " \ "as the first line of the template:" \ "\n\n# encoding: <name of correct " \ "encoding>.\n\nThe source of your " \ "template was:\n\n#{@string}" end end CUSTOM EXCEPTION.
  24. Possible values: UTF-8 If the user enters characters that are

    not in the character set of the document containing the form, the UTF-8 character set is used. “ MSDN
  25. FAILURE. User changes encoding to Latin-1 User pastes smart quotes

    from Word IE sees that smart quotes are in Latin-1 IE ignores accept-charset :( :( :(
  26. <form accept-charset="utf-8"> <input type="hidden" name="utf8" value="✓"> <textarea> Latin-1 chárâctërs pasted

    from another application </textarea> <button type="submit">Do it!</button> </form> SNOWMAN HACK!
  27. SUCCESS. User changes encoding to Latin-1 User pastes smart quotes

    from Word IE sees that ✓ is not in Latin-1 IE honors accept-charset <3<3<3