I have an ActionResult decorated with ValidateAntiForgeryToken and Authorize. Once my forms authorization timeout limit is reached, I'm receiving a "A required anti-forgery token was not supplied or was invalid" error instead of being routed to my login page.
Can someone explain why this happens?
The ValidateAntiForgeryTokenAttribute
'Represents an attribute that is used to detect whether a server
request has been tampered with.'
to quote MSDN. What Html.AntiForgeryToken() does is output a hidden field into the form, something like: <input name="__RequestVerificationToken" type="hidden" value="XXX" />.
What the ValidateAntiForgeryTokenAttribute does on post back is compare the posted value to a previously stored cookie, to verify that they match. See http://aspnet.codeplex.com/SourceControl/changeset/view/72551#338576 (the OnAuthorization method) for details. The cookie has a name of RequestVerificationToken_Lw (you can use a cookie inspection tool like FireCookie to see this).
The cookie stored is a session cookie (the important bit). This means that when your authorization timeout is reached (30 mins by default in .NET), the cookie expires, doesn't get sent with the next request and the comparison to the hidden field value fails, throwing a HttpAntiForgeryException.
Make sure to use #Html.AntiForgeryToken() with in the BeginForm(){....} in the view
#using(Html.BeginForm()){
#Html.AntiForgeryToken()
.
.
.
.
}
Then this will be validated in the action filter attribute [ValidateAntiForgeryToken]
Related
I'm having trouble validating a nonce created with wp_create_nonce() inside a hidden input with the name nonce in an html form:
<input type="hidden" name="nonce" value="<?php echo wp_create_nonce('action_name'); ?>" />
The form submission is done via ajax and validated with check_ajax_referer('action_name','nonce'). This always returns -1. All REST endpoints have been tested without nonces and work 100% fine.
The issue seems to stem from wp's user identifcation.
My debugging so far
Nonce creation
Within wp-includes/pluggable.php wp_create_nonce('action_name') creates a nonce hashing various variables including the user id and the action.
Ajax call
I submit an ajax call which calls check_ajax_referer('action_name','nonce'). This in turn calls wp_verify_nonce($nonce,$action) which verifies the nonce by hashing the same variables and comparing the two.
Reverse engineering to locate problem
My problem is that wp_create_nonce('action_name') is being created with the correct user id. However, when I run check_ajax_referer('action_name','nonce') which calls wp_verify_nonce($nonce,$action) which in turn calls wp_get_current_user(); no user is found (user id is 0).
Evidence the problem is to do with user id
If I temporarily edit wp-includes/pluggable.php to force my user id, the nonce validation works fine. It's as if ajax requests to a known and valid endpoint are being treated as if the user is logged out regardless of whether they are or not.
I'm clearly missing something here, but I have no idea what.
This is happening because a separate nonce with the action wp_rest is not being sent by the server to the client and received back from the client in an HTTP request header called X-WP-Nonce with every REST request.
To get this working, you will have to generate a nonce like this:
wp_create_nonce('wp_rest')
...and provide it to the client making the rest call. Once your client has the nonce value, you need to add it to every REST request e.g.:
headers: {
'X-WP-Nonce': nonce,
}
Creating the nonce on the server and accessing it on the client can be done several ways. Using wp_localize_script() is the most common and probably best practice for WordPress. wp_localize_script() addds a global variable to the client for a script to access. See https://developer.wordpress.org/reference/functions/wp_localize_script/.
Not entirely confident I have understood security in Laravel forms enough. For example, if a form contains
<input type="hidden" name="user_id">
then obviously a hacker could change the value before submitting an update.
While I have looked here at CSRF, I've not fully understood if this is enough protection?
E.g. Taking the above, if I go to a site and open a form to edit a record I'm permitted to view but not change, and maliciously alter the "user_id", is it enough that the form is protected with {{ csrf_field() }} or must I employ some further security such as Crypt::encrypt($id) to hide the user_id (held in a database) and Crypt::decrypt($id)?
Is it considered a bad practice to expose a row id (like a user id) in a client browser (even though everything is sent over https)?
Many Thanks
No, it's not enough to use just CSRF token in this case. You also need to use policies, guards, middleware to protect your app.
In this case, someone can alter the user_id if you read it from the form and use after that, so you need to use a policy like this one to protect data (this example is from the docs):
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
Also, when you need to use user ID, always use auth()->id() or auth()->user() if you need whole object. Never read user ID from the form.
The Laravel framework stores the value of this CSRF field like a session variable and matches it when you submit it.
When you submit the form Laravel checks that value from the session value stored. if there is a mismatch an error is thrown !
:)
CSRF token protect the site from cross-site requests, means an external user can't duplicate the form and send a post request. Laravel create a random session token which we place in the hidden field using csrf_field() or Session::token() function. Laravel checks the session with hidden field value from the form before processing the form.
Try removing the form action. It should work.
CodeIgniter gives an error "The action you have requested is not allowed." when it fails the check for CSRF. As I understand it, this means the POST is missing the hidden token from the form that proves that an attack is not being done.
The token is generated automatically with a call to the CI form_open function.
In my case, I'm using Knockout to post the contents of a ViewModel for saving, like this:
ko.utils.postJson($("form")[0], self.pages);
I've found solutions elsewhere that simply turn off the CSRF setting for the specific page, but that doesn't seem like a good solution.
Presumably because the token is not being received, the postJson call is not submitting the existing form. Is there a way to either submit the required token along with the JSON data or submit the JSON data with the existing form?
try to use form_open() and form_close
all form helper functions that will help.
or: I think it's from time zone difference as the Security class depends on time for hashing.
Here is how I add the csrf to the form
$this->addElement('hash', 'csrf', array('ignore' => false));
When this happens the session is created, Then when the user sends an ajax request, the values in the request are validated by creating an instance of the form, and the form is always valid for the first ajax request since the beginning of initial request which created the html output,
When the ajax request has been sent for the second time something different happens,
That instance of the form has a different csrf value than the originally made one, and when my code is done, the originally created session is destroyed as well, so there is no session to check the received the values against, and hence the form doesn't validated and the following error occurs.
No token was provided to match against
Any ideas at which event, the csrf values of the form are automatically stored in the session?
The hash value is generated at render time and invalidated after the each request.
If you want to continue using Zend_Form_Element_Hash in your AJAX form where the form may submit several times, your AJAX response should include the new hash value. Upon receiving the response, you should update the form data.
There s a solution without any to render in the view : Totaly ajax !
How to use Zend Framework Form Hash (token) with AJAX
I have a hidden input field, which value is read from a property of the request scope:
<h:inputHidden id="myHiddenField" value="#{requestScope['myVar']}" />
I trigger an Ajax-Request where I change the value of myVar.
<p:commandButton value="submit" action="#{myController.doSomething}" update="myHiddenField">
But my input field still contains the old value.
Any idea how I can solve this?
UPDATE:
Maybe I have to explain it a little bit more.. myVar contains the IDs of all input fields with an error message (facesContext.getClientIdsWithMessages()).
When I first submit the form (with some validation errors) it works as expected. When I resubmit the form with some other validation errors the value of myVar doesn't get updated... (Still contains the IDs of the 'old' errors) When I resubmit the form with no validation errors myVar gets updated. (myVar is empty now)
If you want to access a bean after the page has been loaded it needs to be at least ViewScoped. RequestScoped beans are destroyed when the page is loaded (the request is handled and there is no need for it anymore).