Beginner question about csrf

35 views
Skip to first unread message

Sebastjan Hribar

unread,
May 30, 2024, 5:08:40 PM5/30/24
to Roda
Hi all,

I've just started with Roda a few days ago and I really like it. However, I've hit a wall today with implementing csrf. I'm using Mastering Roda and this tutorial as guides and I've taken the roda-sequel-stack as the starter package.

I'm working on users first. Here is my code:

#main routes file
# frozen_string_literal: true
class RDFiremanager
plugin :hash_branches

Dir["routes/**/*.rb"].each do |route_file|
require_relative route_file
end
route do |r|
check_csrf!
r.hash_branches
end
end

#users routes
class RDFiremanager
hash_branch "users" do |r|
r.is do
r.get do
@users = User.order(:id)
view("users/index")
end

r.post do
@user = User.new(r["user"])
if @user.valid? && @user.save
r.redirect "/users"
else
view("users/new")
end
end
end

r.is "new" do
@user = User.new
@user_roles = user_roles
view("users/new")
end

r.is Integer do |id|
r.get do
"Ta uporabnik"
end
r.delete do
"Izbriši tega uporabnika"
end
end

r.is Integer, "edit" do |id|
r.get do
"Uredi tega uporabnika"
end
end

end
end

#new user form
<h1>New User</h1>

<form action="/users" method="post" role="form">
<%== csrf_tag %>
<div class="form-group">
<label class="control-label" for="user_name">Name</label>
<input class="form-control" id="user_name" name="user[name]" value="<%= @user.name %>" reuired="required" type="text"/>
</div>
<div class="form-group">
<label class="control-label" for="user_surname">Surname</label>
<input class="form-control" id="user_surname" name="user[surname]" value="<%= @user.surname %>" required="required" type="text"/>
</div>
<div class="form-group">
<label class="control-label" for="user_username">Username</label>
<input class="form-control" id="user_username" name="user[username]" value="<%= @user.username %>" required="required" type="text"/>
</div>
<div class="form-group">
<label class="control-label" for="user_email">Email</label>
<input class="form-control" id="user_email" name="user[email]" value="<%= @user.email %>" required="required" type="email"/>
</div>
<div class="form-group">
<label class="control-label" for="user_password">Password</label>
<input class="form-control" id="user_password" name="user[password]" required="required" type="password"/>
</div>
<div class="form-group">
<label class="control-label" for="user_roles">Roles</label>
<tr>
<td colspan="2">
<% user_roles.each do |k,v| %>
<div class="form-check">
<input class="form-check-input" id="user_roles" name="user[roles]" value=<%= v %> type="checkbox"/>
<label class="form-check-label" for="user_roles">
<%= k %>
</label>
</div>
<% end %>
</td>
</tr>
</div>
<div class="form-group">
<label class="control-label" for="user_active_status">Status</label>
<input class="form-control" id="user_active_status" name="user[active_status]" required="required" type="text"/>
</div>
<div class="form-group">
<input class="btn btn-success" type="submit" value="Create User"/>
</div>
</form>

When submitting the form I always get this error message: An invalid security token was submitted with this request, and this request could not be processed.

What am I doing wrong?

thank you, sebastjan


Jeremy Evans

unread,
May 30, 2024, 5:13:34 PM5/30/24
to ruby...@googlegroups.com
On Thu, May 30, 2024 at 2:08 PM Sebastjan Hribar <sebastja...@gmail.com> wrote:
Hi all,

I've just started with Roda a few days ago and I really like it. However, I've hit a wall today with implementing csrf. I'm using Mastering Roda and this tutorial as guides and I've taken the roda-sequel-stack as the starter package.

roda-sequel-stack uses the route_csrf plugin, which by default requires tokens specific to the request path and method for better security.  Your call to csrf_tag does not include the expected path.  You probably want:

<%== csrf_tag('/users') %>

Thanks,
Jeremy

Sebastjan Hribar

unread,
May 31, 2024, 1:44:07 AM5/31/24
to Roda
Thank you! Works perfectly.
Sebastjan

četrtek, 30. maj 2024 ob 23:13:34 UTC+2 je oseba jeremy...@gmail.com napisala:

Sebastjan Hribar

unread,
Jun 13, 2024, 3:31:52 PM6/13/24
to Roda
Hi,

what about with ajax requests?

I have the token in layout.erb like so:
<meta name="csrf-token" content="<%= csrf_token %>">

And in my script:
var csrfToken = $('meta[name="csrf-token"]').attr('content');

$.ajax({
url: '/modify_entity',
type: 'POST',
data: params,
headers: {
'X-CSRF-Token': csrfToken
},

And I still get the bad request due to an invalid token.

Any idea what is incorrect?

Best, seba


petek, 31. maj 2024 ob 07:44:07 UTC+2 je oseba Sebastjan Hribar napisala:

Jeremy Evans

unread,
Jun 13, 2024, 3:44:45 PM6/13/24
to ruby...@googlegroups.com
On Thu, Jun 13, 2024 at 12:31 PM Sebastjan Hribar <sebastja...@gmail.com> wrote:
Hi,

what about with ajax requests?

I have the token in layout.erb like so:
<meta name="csrf-token" content="<%= csrf_token %>">

And in my script:
var csrfToken = $('meta[name="csrf-token"]').attr('content');

$.ajax({
url: '/modify_entity',
type: 'POST',
data: params,
headers: {
'X-CSRF-Token': csrfToken
},

And I still get the bad request due to an invalid token.

Any idea what is incorrect?

The csrf_token in the meta tag is not specific to the path you are using it for.  For best security, make sure the token used is for the correct path.  In general that means avoiding meta tags with the token and using form/request specific tokens.

If security isn't that important, you could use the require_request_specific_tokens: false plugin option to not require request specific tokens.

If is possible to use request specific tokens for some paths, and generic tokens for others.  To do that, when calling check_csrf! if you don't want to require request-specific tokens, pass the require_request_specific_tokens: false option.  If you don't want to call check_csrf! at separate points in your routing tree, you can do something like:

  check_csrf!(require_request_specific_tokens: !generic_paths.include?(r.path))

Where generic_paths is an array of paths for which you would accept a generic token.

Thanks,
Jeremy

Sebastjan Hribar

unread,
Jun 13, 2024, 4:05:14 PM6/13/24
to ruby...@googlegroups.com
I would actually opt for highest security. So I'd remove the meta tag, but what is then the correct reference in the ajax request?

Jeremy Evans je 13. 06. 24 ob 21:41 napisal:
--
You received this message because you are subscribed to the Google Groups "Roda" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-roda+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ruby-roda/CADGZSSeuw5GPVW6epdX9yKyTpx1tTr9vMkfZ%2BXec8Q2tksHUPw%40mail.gmail.com.


Jeremy Evans

unread,
Jun 13, 2024, 4:14:57 PM6/13/24
to ruby...@googlegroups.com
On Thu, Jun 13, 2024 at 1:05 PM Sebastjan Hribar <sebastja...@gmail.com> wrote:
I would actually opt for highest security. So I'd remove the meta tag, but what is then the correct reference in the ajax request?

You should be able to get an appropriate token via:

  csrf_token('/modify_entity')
 
Thanks,
Jeremy

Sebastjan Hribar

unread,
Jun 14, 2024, 11:20:42 AM6/14/24
to Roda
So, I'm still hitting a wall. :(

In my index.erb I have the token like so:
<form id="modify-form">
<%== csrf_tag('/modify_entity') %>

This generates: <form id="modify-form"> <input type="hidden" name="_csrf" value="some token value" />

And in my script I have:
var csrfToken = $('#modify-form input[name="_csrf"]').val();

And then:
headers: {
'X-CSRF-Token': csrfToken
},

I get 400 bad request and under response I see Invalid Security Token. I've tried printing the token to the console and there's no value. Also, I don't see the csrf in the request header when inspecting in the dev tools.
I haven't made any base configuration changes to the app stack.

Is there any more info I can provide to help debug this, or is there a working sample code I can learn from?

Thank you. Seba




četrtek, 13. junij 2024 ob 22:14:57 UTC+2 je oseba jeremy...@gmail.com napisala:

Sebastjan Hribar

unread,
Jun 14, 2024, 11:20:42 AM6/14/24
to Roda
I've found the bug that prevented csrf being in the request header and I see it in the console. But I still get bad request due to an invalid security token.

četrtek, 13. junij 2024 ob 22:14:57 UTC+2 je oseba jeremy...@gmail.com napisala:

Jeremy Evans

unread,
Jun 14, 2024, 11:25:41 AM6/14/24
to ruby...@googlegroups.com
On Fri, Jun 14, 2024 at 8:20 AM Sebastjan Hribar <sebastja...@gmail.com> wrote:
I've found the bug that prevented csrf being in the request header and I see it in the console. But I still get bad request due to an invalid security token.

By default, the route_csrf plugin does not check header values, only form parameters.  If you want it to check header values, use the check_header: true plugin option.

Thanks,
Jeremy
 
četrtek, 13. junij 2024 ob 22:14:57 UTC+2 je oseba jeremy...@gmail.com napisala:
On Thu, Jun 13, 2024 at 1:05 PM Sebastjan Hribar <sebastja...@gmail.com> wrote:
I would actually opt for highest security. So I'd remove the meta tag, but what is then the correct reference in the ajax request?

You should be able to get an appropriate token via:

  csrf_token('/modify_entity')
 
Thanks,
Jeremy

--
You received this message because you are subscribed to the Google Groups "Roda" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ruby-roda+...@googlegroups.com.

Sebastjan Hribar

unread,
Jun 14, 2024, 8:12:38 PM6/14/24
to ruby...@googlegroups.com
Thank you, that worked.

Best, seba

Jeremy Evans je 14. 06. 24 ob 17:24 napisal:
Reply all
Reply to author
Forward
0 new messages