In my project an User can create a Customer and assigning it zero or more Tag. These entities have a relation with User of course. This is done by a form that has a tag field of entity type, filtered by current logged user:
$user = $this->securityContext->getToken()->getUser();
$builder
->add('tags', 'meta_selector', array(
'label' => 'Tag',
'class' => 'Acme\HelloBundle\Entity\Tag',
'property' => 'select_label',
'query_builder' => function(EntityRepository $er) use($user) {
$qb = $er->createQueryBuilder('t');
return $qb
->where($qb->expr()->eq('t.user_id', ':user')
->orderBy('t.name')
->setParameter('user', $user);
}
))
;
And this is working fine. Looking at a generated HTML tags are rendered as checkboxes:
<div class="controls">
<label class="checkbox">
<input type="checkbox" value="2" name="customer[tags][2]"
id="customer_tags_2"> A Tag
</label>
<label class="checkbox">
<input type="checkbox" value="3" name="customer[tags][3]"
id="customer_tags_3"> Another Tag
</label>
</div>
I'd like to investigate further about form tampering. In particular making a POST request from a trusted user adding customer%5Btags%5D%5B1%5D=1, that is a tag with id equal to 1 which exists but it has been created by another user. Attacker user is creating an customer with a tag created by another user:
POST http://localhost/Symfony2/web/app_dev.php/app/customers/new HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: it-it,it;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://localhost/Symfony2/web/app_dev.php/app/customers/new
Cookie: PHPSESSID=3avu1a2a1eufthr5tdftuhrnn7; hl=it
Content-Type: application/x-www-form-urlencoded
Content-Length: 276
customer%5Bfirst%5D=fake&customer%5Blast%5D=fake&customer%5Bgender%5D=m&customer%5Bbirthday%5D=&customer%5Bemail%5D=&customer%5Bmobile%5D=&customer%5Baddress%5D=&customer%5Bcountry%5D=IT&customer%5Btags%5D%5B1%5D=1&customer%5B_token%5D=455783fa2f866677669c9034a90554b9f75d68b4
.. and seems there is some sort of control that prevents this. Result is 200 OK (should be a 302 in case of success) without any error and form is rendered again. Of course entity is not persisted.
Actual question is: how Symfony 2 protect from this kind of form "tampering" attacks? A possible explanation is the it checks that submitted tags exist inside the collection returned by the form builder. But a reference is needed...
EDIT: even disabling CSRF protection the result is the same. By the way i was passing a valid token and CSRF is intended to protect from other types of attacks.
The answer to your question can be explained quite easily. Every choice field (and the entity type is a specialization of the choice type) has a list of choices. For each choice, the field is aware about
the model representation ("choice") of the choice (e.g. a Tag instance)
the view representation ("value") of the choice (e.g. the ID)
the label used in the view (e.g. a property of Tag)
When you submit the form, the choice field looks in this list which model representation matches the submitted view representation. If none can be found, the field remains unassigned.
The code for this logic can be found in the class ChoiceList and its descendants, in your case EntityChoiceList. Upon submission, the method getChoicesForValues() is executed which does the lookup and is optimized for speed.
Wild guess: CSRF protection is enabled and you do not render the errors.
Related
Does anybody know how to validate the file type input.
I have modified (hard coded) the class.vf_file.php input.
$strOutput .= "<input accept=\".pdf,.doc,.docx\" etc----/>\n";
This helps with Google Chrome, but Safari, Firefox ignore the modifications
Preventing users to submit the form if any other type of file is detected would be the ideal solution.
Thank you
I would recommend using a third party file uploading library like Plupload. We always use ValidForm Builder together with Plupload; works like a charm.
However you can use the meta array to implement custom attributes in the <input> tag without having to hardcode anything:
$objForm->addField(
"upload-document",
"Upload Document",
ValidForm::VFORM_FILE,
array(), // Validation array
array(), // Error handling array
array( // Meta array
"fielddata-extensions" => "pdf,doc,docx"
)
);
By prefixing meta keys with the 'field' prefix, you add that specific meta to the <input> field itself instead of it's wrapping <div class='vf__optional'></div>
The above example will output:
<div class="vf__optional">
<label for="upload-image">Upload Image</label>
<input type="hidden" name="MAX_FILE_SIZE" value="2097152">
<input type="file" value="" name="upload-image[]" id="upload-image" class="vf__file" data-extensions="pdf,doc,docx">
</div>
So using a combination of meta and a third party file upload handler, you can actually to pretty cool stuff.
That being said -- I must admit that the file upload field didn't get as much attention as the other field types lately.
I am printing an arbitrary number of identical forms on a page using Spring's <form:form tag, and I need a way to distinguish between them, because form errors of one form are now printed on all forms.
Currently all the identical forms are bound to the same, single backing object. That works fine, because a user can only submit one form at a time.
I am using Spring validation to validate form fields. When one of the fields has an error (let's say 'name' is empty) than the error message is printed under the 'name' field of all forms.
Obviously, I need some way to allow Spring to distinguish between the forms, but how?
Some example code:
This Spring webflow definition creates a ClientFormBacking object to back the client forms. And it submits a form POST to the bindAndValidate method, which calls the validator.
<webflow:var name="clientFormBacking" class="com.example.ClientFormBacking"/>
<webflow:view-state id="list" view="clients" model="clientFormBacking">
<webflow:on-entry>
<webflow:evaluate expression="contextParameterManager.findAll()" result="flowScope.clients"/>
</webflow:on-entry>
<webflow:transition on="update-client" to="list">
<webflow:evaluate expression="clientBinder.bindAndValidate(flowRequestContext)"/>
<webflow:evaluate expression="clientService.saveOrUpdate(flowScope.clientsPageBacking)"/>
</webflow:transition>
</webflow:view-state>
Print an arbitrary number of identical forms with a name field and name error field in JSP:
<c:forEach items="${clients} var="client">
<form:form commandName="clientFormbacking">
<form:input path='name' value='${client.name}'/>
<form:errors path="name" cssClass="input-error" />
</form:form>
</c:forEach>
The validator that rejects the 'name' field:
ClientValidator implements Validator {
public void validate(Object target, Errors errors) {
// Problem here! There are multiple <form:errors path='name'/> tags....
errors.rejectValue("name", "validation.error.name.empty");
}
}
I'm creating a form text field, but would like to set an additional attribute called additional so the html markup looks like this.
<dd id="email-element">
<input type="text" value="" id="email" name="email" additional="">
</dd>
I'm able to set the attribute using setAttrib like so.
$email = new Zend_Form_Element_Text('email');
$email->setAttrib('additional', '');
$this->addElement($email);
I'm then setting the value of additional on the client side via ajax. But when the form is submitted, additional appears empty. When I var_dump the form, I can see it as an attribute on this form field, but it's empty. Also when I var_dump the request, it's not on it (which is understandable since it's an attribute, and not the field value itself). Is there a way to read attributes that were changed on the client side?
PHP has no way of reading form attributes that were modified in the browser, but you can read it on the client side and send it back to PHP. The only data submitted are the element values themselves.
If you need the attribute in PHP, add a hidden input called additional (or whatever you like), and during the form's onsubmit event, you can read the value of the attribute, and populate the hidden element and then submit the form. Note that if the client has Javascript disabled, the value will not come through, but that method can be used to read it and send it to the server.
Hope that helps.
JSP:
<form:form commandName="editWeather" method="post" action="../edit">
<!-- Input fields -->
<input type="submit" value="Submit">
</form:form>
And this is how I get the model in Spring:
#ModelAttribute("DONTGIVEADAMN") Weather weather
And I can still use the weather to do my operations and it works great, for example:
weatherService.editWeather(weather);
My question is...Why does this work?
Model attribute name doesn't matter when binding data received from a form (because names of form fields correspond to the names of fields of the model object), it matters only when rendering a form.
I particular, when model attribute name in your POST handler method doesn't match the commandName in the form, you will be able to receive the data, but won't be able to redisplay a form with validation errors.
its matching the class type (or interface), not the name of the variable/parameter; and the specified request mapping/method signature must be correct.
I need to write a module that sends order data to an epayment service, similar to say, paypal. They need the data to be submitted from a form with elements that look something like this (notice the duplicate name):
<input name="ORDER_PNAME[]" type="hidden" value="CD Player">
<input name="ORDER_PNAME[]" type="hidden" value="Geanta voiaj 2L">
This makes it impossible to override the form by simply editing $form in module_form_alter() because "ORDER_PNAME[]" would be a duplicate key in $form.
So I need to bypass the whole drupal form handling system. I looked and found that I could overwrite the $form variable in uc_cart_checkout_review with plain html form data (see http://api.ubercart.org/api/function/uc_cart_checkout_review/2 line 4).
What would be the correct way to do this?
On the rights of a workaround:
you can add the necessary form elements using markup element:
$form['your_name'] = array(
'#type' => 'markup',
'#value' => '<input name="ORDER_PNAME[]" type="hidden" value="CD Player">
<input name="ORDER_PNAME[]" type="hidden" value="Geanta voiaj 2L">',
);
If you don't need to redirect user to that e-payment service page, just send data, you can use curl to post the necessary data. A related question: Auto Submitting a form (cURL).