13

I'm building a simple app in ruby using the Sinatra framework. It's mainly "get" based - most requests will be for listing data. However there are a couple of key screens in the app that will collect user input. I want to ensure the app is as safe as I can make it, and currently, trying to find how to implement the kind of authenticity tokens that you get in a Rails form?

Where I've got to: Well, I know I need the tokens for csrf, but I'm unsure if I need to generate them myself or if Sinatra can do it for me - I've looked through the docs and they say that Sinatra is using Rack Protection, however, I can't find any example code for it and can't seem to figure out how to get it going - any help apprectiated - thanks!

barlop
  • 12,887
  • 8
  • 80
  • 109
slapthelownote
  • 4,249
  • 1
  • 23
  • 27
  • Is it default now? BTW to disable sinatra CSRF so ajax works https://stackoverflow.com/questions/42454418/how-to-add-access-control-allow-origin-headers-to-api-response-in-ruby/42456805#comment100478921_42456805 (and i'd only do this for a small demo application) . otherwise maybe something like $.ajaxSetup({.. https://stackoverflow.com/questions/27098239/post-422-unprocessable-entity-in-rails-due-to-the-routes-or-the-controller/56821525#56821525 but somehow adapted for sinatra – barlop Jul 10 '19 at 17:57

2 Answers2

28

Use the rack_csrf gem. Install it with

gem install rack_csrf

The rack_csrf gem has a Sinatra example. Below is a simpler example adapted from this page (seems offline. Archived version):

require "rack/csrf"

configure do
  use Rack::Session::Cookie, :secret => "some unique secret string here"
  use Rack::Csrf, :raise => true
end

Using enable :sessions instead of use Rack::Session::Cookie ... will also work in most cases (see Bill's comment).

In your view, you can get the token (or the tag) with the Rack::Csrf.csrf_token and Rack::Csrf.csrf_tag methods. If this appears lengthy, you may want to define a helper along the lines of:

helpers do
  def csrf_token
    Rack::Csrf.csrf_token(env)
  end

  def csrf_tag
    Rack::Csrf.csrf_tag(env)
  end
end

Small example using the helper method:

<form method="post" action="/tweet">
  <%= csrf_tag %>
  <input type="text" name="message"/>
  <input type="submit" value="Submit a tweet!"/>
</form>
Martin Tournoij
  • 26,737
  • 24
  • 105
  • 146
Patrick Oscity
  • 53,604
  • 17
  • 144
  • 168
  • 1
    Here is the link from Google's cache: http://webcache.googleusercontent.com/search?hl=en&output=search&sclient=psy-ab&q=cache%3Ahttp%3A%2F%2Femilloer.com%2F2011%2F01%2F15%2Fpreventing-csrf-in-sinatra%2F&oq=&gs_l=&pbx=1&bav=on.2,or.r_gc.r_pw.r_cp.r_qf.&fp=9bbc30a1cd66fab9&biw=1424&bih=820&ion=1 – Eric Levine Jul 27 '12 at 19:27
  • 1
    @elevine thank you! i updated my answer with a summary of the post in case it eventually disappears from google's cache, too. – Patrick Oscity Jul 27 '12 at 20:06
  • Google Cache did go down. Thank you for saving it here. – eddieroger Dec 26 '12 at 20:36
  • 1
    Internet Archive != google http://web.archive.org/web/20110513020511/http://emilloer.com/ – shadowbq Nov 09 '13 at 12:50
  • 3
    It should be noted that `enable :sessions` is not similar but not the same as `use Rack::Session::Cookie`. The former will set you up with an automatic, no-thinking-required session and the latter lets you do things like set expirations on sessions. Be sure to choose the option that works best for your scenario. That said, they'll both do the job in the context of this question's answer. http://www.sinatrarb.com/faq.html#sessions – wgp Sep 18 '14 at 14:27
  • How does Rack::Csrf compare to Rack::Protection::AuthenticityToken? They seem to serve the same purpose, yet R::P::AT claims to be "compatible" with Rack::Csrf, though I don't know what that means. – odigity Oct 02 '15 at 03:38
0

When same erb view is rendered, in a case where credentials are invalid while logging in. now after the render on submit it throws the error. Rack::Csrf::InvalidCsrfToken at /login Rack::Csrf::InvalidCsrfToken

Do we need to do a redirect in place of render to make this work? because in render the old csrf token is still in place. in a complete redirect we have a new token generated.