When deleting the list item, is it necessary to confirm that it is a correct request by using CSRF token ?
I think the csrf token necessity depends on the importance of the data to be deleted. I wonder my idea is correct?
<ul>
<li>item 1 <button type="button" onclick="deleteItem()">delete</button></li>
<li>item 2 <button type="button" onclick="deleteItem()">delete</button></li>
<li>item 3 <button type="button" onclick="deleteItem()">delete</button></li>
</ul>
You only need csrf tokens when posting data.
Typically a client side secret"token" in a form tag or when doing an ajax call to the back end.
By default yoy should use CSRF validation every time you modify data. In reality this use ro be when you do POST, PUT, PATCH or DELETE request. In every case except from a GET request.
If you accept requests without a CSRF-token, it's possible for another website to make requests on behalf of the user, malicious intent or not.
Related
I'm adding in CSRF token validation and I'm running into a problem where I have two forms on a page and one of them will submit successfully and the other will not. I'm not doing AJAX requests, I'm simply using hidden input fields. If I submit the form when it is the only one on the page, it submits without issue. If I submit it on a page with more than one form, it fails.
Below is the template code for my two forms
{{if .IsAuthenticated}}
<form action='/admin/logout' method='POST'>
<button>Logout</button>
{{.CsrfField}}
</form>
{{end}}
<form action='/admin/stuff/create' method='POST'>
{{with .Form}}
<div>
<label>Title:</label>
<input type='text' name='title' value='{{.Get "title"}}'>
</div>
<div>
<input type='submit' value='Publish stuff'>
</div>
{{end}}
{{.CsrfField}}
</form>
And this is what the generated HTML looks like. Both appear to be valid.
When I click the "Logout" button though, I get the Forbidden - CSRF token invalid error, but clicking the create input value in the second form always works.
The logout button is correctly validated when I attempt to use it on the home page which is "/admin/" but it does not work on any of the other pages "/admin/snippet/:id" or "/admin/snippet/create". The Logout button is part of a base template, so it appears on every page, so there shouldn't be anything different in how it appears on any page.
I've read other SO posts about multiple forms & CSRF tokens on a page and I understand there should be no issue with multiple forms with the same information as long as you have each one in it's own form, it should be fine. So I am not sure where I am going wrong.
I found the issue. Currently the way that gorilla/csrf works, it does not like creating the masked token from one path and then sending that token off to another path. So in my situation, going from /admin/snippet/create to /admin/logout threw an error because it was expecting the path for the token to be /admin/snippet/<something> and so it threw an error.
This issue has been addressed in this PR: https://github.com/gorilla/csrf/pull/147 and essentially the solution is to set the default path yourself to something which all of your routes will contain, so in my case that was /admin
This is what my CSRF declaration looks like now in main.go
var csrfMiddleWare = csrf.Protect(
[]byte("<put your 32 character key here>"),
csrf.Path("/admin"),
csrf.Secure(false),
)
A note, if you had this issue and then apply this fix and it doesn't resolve the problem, trying testing in a separate browser as there may be some caching issues.
How to hide the values in the URL? In routes:
GET /admin/:userId com.example.sample.getUser(userId: Int)
When getUser action is requested, URL returns in the format as shown below:
http://localhost:9000/admin/0
I don't want to view the userId in the URL.
This is common GET vs POST difference. In GET request params are sent within the URL, in POST are not.
If you need to avoid params in the URL just use a HTML form with hidden field instead of a tag to send the requests and in your controller fetch the POST arguments, dummy HTML sample:
<form action="/admin/" method="POST">
<input type="hidden" name="userId" value="#userId" >
<input type="submit" value="#userName" >
</form>
It will give you submit button with name of the user as a label, you can use CSS to give it feel and look of common link.
Note that's invalid REST pattern, as POST shouldn't be used to showing data, you don't mention what is the purpose of this, so maybe you should rethink your needs.
Maybe you can also just make usage of parameters with default or fixed values:
check the docs for more options
After a couple of weeks playing with rails app and completing the rails tutorial, I wanted to learn about some common web attack in web applications.
I managed to perform a CSRF attack type using the below chunk of code in a .html file.
<form action="http://localhost:3000/users/2" method="post">
<input type="hidden" name="_method" value="delete">
<div>
<input type="submit" value="Delete">
</div>
</form>
Being logged in as an admin, I run the attack against my own code that was based on same session mechanism as Railtutorial and I succeed deleting the user where it should have been stopped as the authenticity token was missing.
The default behavior should be a session reset thus preventing the user being deleted outside of the web application.
I can see the 'Can't verify CSRF token authenticity' in the log, but the session is not reset.
Overriding the handle_unverified_request method, that should by default reset the session, with
def handle_unverified_request
raise(ActionController::InvalidAuthenticityToken)
end
the error get raised properly.
Running the attack on Railstutorial git code from railstutorial/sample_app_2nd_ed freshly install, I faced the exact same issue: the session is not reset as it should be. That mean the tutorial app is vulnerable to that kind of attack.
I dig a bit deeper into the code (http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-reset_session) but cannot figure out why in the context Railstutorial it doesn't seem to work.
Could anyone verify, If the tutiral is indeed vulnerable to CSRF attck? if yes would the solution be to rewrite the handle_unverified_request to do proper signout in that case? Finally why is it not working as it should be?
Thanks for your help.
I figure out how to force the signout of the user if the CSRF token can't be verify.
It seems related to the fact that we empty the session but not the remember token of the cookie.
Override handle_unverified_request in your application (so that all controller benefit from it) and add the sign_out method to clean the remember token.
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
def handle_unverified_request
sign_out
super
end
end
I have a mostly "static" web site with no server-side code and just a little JavaScript. Now I would like to add a contact form. I do not care how I get the contact form data (so just writing this data to a text file in the server will be ok).
What is the simplest solution for this problem? How do people usually handle this?
I believe I can add some server-side code (PHP or something) to handle the form (and write the form data to a file, for instance) but I would prefer a client-side solution.
Use an external tool, they are commonly referred to as "formmailer". You basically submit the form to their server, and they send the form contents via mail to you.
If you don't want that, you have to do something server-sided: Storing data on the server, without having a server side program that accepts the data from the client, is just not possible.
You could install CouchDB and interface that from Javascript :) Everyone could use that then, too :)
The most easy PHP script that stores POST data on your harddisk:
<?php file_put_contents('/path/to/file', serialize($_POST) . "\n", FILE_APPEND); ?>
You can use Google Drive and create form with required fields. and embed code (which will be iframe) in your static web page.
You will be able to get submitted data in spreadsheet.
You can use qontacto . it is a free contact form you can add to any website. it forwards you the messages.
I set up the fwdform service for this exact need.
Just two simple steps to get your form forwarded to your email.
1.Register
Make an HTTP POST request to register your email.
$ curl --data "email=<your_email>" https://fwdform.herokuapp.com/register
Token: 780a8c9b-dc2d-4258-83af-4deefe446dee
2. Set up your form
<form action="https://fwdform.herokuapp.com/user/<token>" method="post">
Email: <input type="text" name="name"><br>
Name: <input type="text" name="email"><br>
Message: <textarea name="message" cols="40" rows="5"></textarea>
<input type="submit" value="Send Message">
</form>
With a couple of extra seconds you can spin up your own instance on Heroku.
<input type="text" class="inputtext" name="email" id="email" value="" tabindex="1"> is the email box
<input type="password" class="inputtext" name="pass" id="pass" tabindex="2"> is the password box
<input value="Connexion" tabindex="4" type="submit" id="u_0_v">
is the submit button
Now... I have this script running but I still can't manage to login ( I get to the same login page: facebook.com)
import requests
from bs4 import BeautifulSoup
body = {'email':'xxxx#hotmail.com','pass':'xxxxx',}
con = requests.post('https://www.facebook.com', data=body)
s = BeautifulSoup(con.content)
print (s)
Do I have to pass in the 'submit button' in the body{}. I thought I should include it but there is no name for the submit button so I don't know how to include it in the body{}. Thanks for the help
You always need to pay attention to any additional (hidden) fields, that are sent along credentials, and might be needed for any server processing.
That is the case for your example with runescape.com. When you use your browser to intercept data, that is normally being sent along with the form, you can modify the script in this manner:
import requests
from bs4 import BeautifulSoup
body = {'username':'xxxx#hotmail.com','password':'xxxxx','submit':'Login','mod':'www','dest':'community'}
con = requests.post('https://secure.runescape.com/m=weblogin/login.ws', data=body)
s = BeautifulSoup(con.content)
print(s)
You can see mod and dest parameters were needed to make the server processing function. As for the submit button, it is rarely checked for, but it is always safer to include it as well (as I did in this example).
The result is not 404 anymore, but the login will nevertheless fail, as there is Captcha in place to prevent automatic login.
As for Facebook, there are a lot of complicated supplementary fields, that would require a lot of reverse engineering to be done. I would strongly suggest to consider using the official Facebook Graph API (https://developers.facebook.com/docs/graph-api) if possible to accomplish what you need.