On my web page I have Zend_Form with CSRF hash. It submits to the same page and it is used to update user's data (name, surname, birthdate and so on...). I would like to reinitialize CSRF hash after every valid post submission. How I could do it?
Now when I first time submit 'user data' form I get proper behavior (user data gets updated). But if I submit again (right after first, proper submit) to change another form field I get error saying:
The two given tokens do not match
Is there anyway to reinitialize hash properly?
Your problem comes from this function in Zend_Form_Element_Hash the $session->setExpirationHops is set to 1 hop, so if you try to resubmit the same form it will always fail. This is by design and is the root of the security you're seeking.
However you can change this behavior, simply extend Zend_Form_Element_Hash and override this method to set the expirationHops to a value you prefer (you can also set the session options manually at any time if you prefer).
public function initCsrfToken()
{
$session = $this->getSession();
$session->setExpirationHops(1, null, true);
$session->setExpirationSeconds($this->getTimeout());
$session->hash = $this->getHash();
}
It goes without saying that it would be in the interest of security to perform a full refresh and repopulate the form to perform any edits. This should reset the hash.
My problem came from tampering with Zend_Form_Element_Hash init methods, I had this:
$_csrf = new Zend_Form_Element_Hash($this->_csrfName);
$_csrf->setSalt(md5($name));
$_csrf->setAttrib('id', '');
$_csrf->initCsrfToken();
The last line should never be there. When I removed it, everything started to act correctly.
#RockyFord: The initCsrfToken() function was place I was looking at the beginning, but
for me (I mean my intuition) it just couldn't be that. So I just debugged, debugged, debugged,
and at last commenting out 4th line was proper solution. Sorry for holding off reply, I totally forgot.
Related
Going to try my best to explain this...
In my app I have the need to display the validation errors on a form when the user loads the form initially. In short, they've entered data and saved it, but now the data has been checked and we've detect errors they need to correct before they can fully submit the form. (It's multi step form that can be filled out over multiple sessions...think big.)
Previously I was doing something like:
THIS DOESN'T WORK IN SYMFONY >=2.8.10 See this answer
$entity // passed in as param on action method
$form = $this->createForm(..., $entity);
$csrfToken = 'random_string'; // retrieved from FormConfigInterface
// perform the submit but don't clear missing
$form->submit(['_token' => $csrfToken], false);
This was working, but seems to have broken in Symfony 2.8.10+, but works in 2.8.9. In 2.8.10+ there are no validation errors (the form is considered valid).
I'm able to retrieve the validation errors in a ConstraintViolationListInterface, but I can't find a way to "merge" that with the form (which I think would be the prefered way). Since I couldn't, I tried the above which "fake" submits the form...which seems like a bad idea.
Is there a better/proper way?
(Note: the form is much more complicated and includes validation groups...but I'm not concerned about that or the error in 2.8.10+ at this point.)
I have an edit made with blade to edit a resource, like this:
{{Form::model( $post ,['action'=> ['PostController#update', 'id' => $post->id], 'method' => 'post'])}}
Which generates a form with action
http://example.com/posts/edit/123
And my fields, having text and hidden inputs
Seeing this url, it's very easy for a bad-intentioned user to update other posts.
How can I protect the route to make it fail if the id is manipulated with the inspector? Is there any built-in wat to tokenize the id to make sure it matches? Can this also de applied to all the hidden inputs?
Thanks
EDIT:
An example on my hidden fields usage:
My posts are generally questions and answers, when an user tries to add an answer to a question, I set question_id as a hidden field, and I want to check it is not manipulated.
Limonte's answer is correct to secure the ability to edit other peoples posts - and you should always do that. To answer the second half of your question:
I set question_id as a hidden field, and I want to check it is not manipulated.
The problem is that you can never trust the data supplied by a client to your system. You must always assume it has been tampered with.
One option to help minimize the risk is you can use the encryption service by Laravel to do this:
{{ Form::hidden('question_id', Crypt::encrypt($question_id)) }}
Then in your controller
$question_id = Crypt::decrypt(Input::get('question_id'));
Just make sure you've set a random application encryption key in your app.php config file
To protect route you should check permission in PostController#update.
In the method beginning check if user can edit given post:
public function update($postId)
{
$post = Post::findOrFail($postId);
if ($post->user_id !== Auth::id()) {
abort(403, 'Unauthorized action.');
}
// validate, update record, etc.
}
I have a website where the front page contains a search form with several fields.
When the user performs a search, I make an ajax call to a function in a controller.
Basically, when the user clicks on the submit button, I send an ajax call via post to:
Route::post('/search', 'SearchController#general');
Then, in the SearchController class, in the function general, I store the values received in a session variable which is an object:
Session::get("search")->language = Input::get("language");
Session::get("search")->category = Input::get("category");
//I'm using examples, not the real variables names
After updating the session variable, in fact, right after the code snippet shown above, I create (or override) a cookie storing the session values:
Cookie::queue("mysite_search", json_encode(Session::get("search")));
And after that operation, I perform the search query and send the results, etc.
All that work fine, but I'm not getting back the values in the cookie. Let me explain myself.
As soon as the front page of my website is opened, I perform an action like this:
if (!Session::has("search")) {
//check for a cookie
$search = Cookie::get('mysite_search');
if($search) Session::put("search", json_decode($search));
else {
$search = new stdClass();
$search->language = "any";
$search->category = "any";
Session::put("search", $search);
}
}
That seems to be always failing if($search) is always returning false, and as a result, my session variable search has always its properties language and category populated with the value any. (Again: I'm using examples, not the real variables names).
So, I would like to know what is happening here and how I could achieve what I'm intending to do.
I tried to put Session::put("search", json_decode($search)); right after $search = Cookie::get('mysite_search'); removing all the if else block, and that throws an error (the ajax call returns an error) so the whole thing is failling at some point, when storing the object in the cookie or when retieving it.
Or could also be something else. I don't know. That's why I'm here. Thanks for reading such a long question.
Ok. This is what was going on.
The problem was this:
Cookie::queue("mysite_search", json_encode(Session::get("search")));
Before having it that way I had this:
Cookie::forever("mysite_search", json_encode(Session::get("search")));
But for some reason, that approach with forever wasn't creating any cookie, so I swichted to queue (this is Laravel 4.2). But queue needs a third parameter with the expiration time. So, what was really going on is that the cookie was being deleted after closing the browser (I also have the session.php in app/config folder set to 'lifetime' => 0 and 'expire_on_close' => true which is exactly what I want).
In simple words, I set the expiration time to forever (5 years) this way:
Cookie::queue("mysite_search", json_encode(Session::get("search")), 2592000);
And now it seems to be working fine after testing it.
stack: symfony2/doctrine2/php/mysql
a multipage form constist of two steps. each step is realized in a controller action.
in step1, the form is displayed. form-input is validated in the same action. if the form is valid the user should be redirected to the second step/action. in the second step the user has to confirm his input. after confirmation the data should be stored in the db.
thus form-entities/form-data are/is needed in the second step/action. however i do not want to store it in the db before confirmation.
do i really need to serialize all objects? to the session?
is there a better approach?
any suggestions?
First of all, I would recommend validating the input via JavaScript before posting and not in the controller action on the server.
If you don't want to serialize the data to the session you can simply pass it on to the next page when you receive it in the first action and then post it to the second action, I'm imagining something like this:
firstAction() {
$exampleData = $_POST['exampleData'];
// Do whatever you need, then pass the data on to the next page
return $this->render('SomeBundle:Views:secondPage.html.php',
array('exampleData' => $exampleData));
On the second page you then just have to access $exampleData with JavaScript and best put it in some hidden input field inside the form.
<!-- secondPage.html.php -->
<script type="text/javascript">
var exampleData = <?php echo $exampleData ?>;
$('#hiddenInput').val(exampleData);
</script>
The second controller action will then receive $exampleData as well without having it serialized in the session.
Sorry if there are any syntax errors, haven't used symfony2 in a while :)
tried to use serialization, but entities are quite complex with "many" associations. thus serialization is too slow. even after detaching.
first solution (simplified):
store the POST variables to the session inside the first step/action.
$postParams = $this->getRequest()->request;
$session = $this->getRequest()->getSession();
if (!$session) {
$session = new Session();
}
$session->set($sessionKey, $postParams);
in the second step/action i used the form to repopulate my entity.
$cancellation = $manager->initCancellationSomehow();
$session = $this->getRequest()->getSession();
if (!$session) {
$session = new Session();
}
$parameterBag = $session->get($sessionKey);
$cancellation = $this->getCancellation($customerId);
$form = $this->createForm(
new CancellationType(),
$cancellation,
array(
'em' => $this->getDoctrine()->getManager())
);
$form->bind($parameterBag->get('form'));
[..]
second solution:
well my first thought was to store cancellation in the db. therefore i added a state attribute (active/temp/..). unconfirmed cancellations get marked as temp. if the user confirms the state gets changed form temp to active. temp collections get deleted after on hour by a garbarge collector which runs at a low priority.
i like the second solution because the user has to confirm the final cancellation, which is already stored in the db. if the frontend does not work as expected the user will likely notice corrupted cancellations (e.g. wrong entries selected). if he confirms, only the state is changed. feels safe. in the first solution the user confirms what should be stored in the db, but isn't till now. feels unsecure.
I would like to do something roughly analogous (but not exactly identical) to the following: I want to create a Person content type, which has an SSN field. I would like to store the SSN field as an integer, but allow the user to input the number as 123-45-6789. This means that before validation triggers, stating that "123-45-6789" is invalid input, I would like to remove the dashes and treat this as an integer.
I've tried to use both a #value_callback function, as well as a non-default validation function. The problem then is that although I can force the value to be validated, the unchanged value is what is passed to the db for insertion, which fails. In example, this means that although I can force "123-45-6789" to be recognized by Drupal as "123456789", the database is still being passed "123-45-6789", which of course fails.
The one obvious solution would be altering this via client side javascript, before the value is even submitted to the webserver. I would strongly prefer to avoid this route.
Apologies if I've misunderstood but you should just be able to do something like this:
function my_validation_handler(&$form, &$form_state) {
if (passes_ssn_validation($form_state['values']['SSN'])) {
// Changing the value in $form_state here will carry on over to the submission function
$form_state['values']['SSN'] = convert_to_db_format($form_state['values']['SSN']);
}
else {
form_set_error('SSN', 'The SSN was invalid');
}
}
Then you'd attach that validation function using $form['#validate'][] = 'my_validation_handler' in either your form build or form_alter function.
Hope that helps
you should use hook_node_presave(). It allows you to change the values of different fields before they are inserted to the database. Here's the official documentation:
http://api.drupal.org/api/drupal/modules--node--node.api.php/function/hook_node_presave/7
Hope this can help :)