TYPO3 How do i avoid form objectID manipulation? - forms

I would like to avoid the manipulation of the hidden field (__identify) in a form. For example the edit form. If someone goes to the inspector and change the value to another uid then the update action will actually update the manipulated value instead of the original.
Now if someone changes this to 8 then the update action will update the object with the uid 8.
Is there a way to avoid such action?
TYPO3: v9
Mode: Composer Mode
Best regards

Thanks to #Daniel Siepmann (typo3.slack.com) for pointing me to the right direction. So the answer is simple and easy to implement.
TYPO3 uses hmac for internal purposes and has a static function
called hmac under the GeneralUtility class.
Concept:
We create a hidden field in the form with a hmac string based on the uid of the object and a word of your choice. (To make the decryption more difficult for the attacker). Then on the controller we regenerate the hmac with the uid that has been passed via the form arguments to the controller and the word we previously defined. If they match, then the object can be updated. If not, then we redirect the user to another page (Error or list view, it is up to you).
How to use it:
your_extension/Classes/Controller/YourController.php
public function editAction(Object $object)
{
$hmac = GeneralUtility::hmac($object->getUid(), 'yourWord');
$this->view->assign('hmac', $hmac);
$this->view->assign('object', $object);
}
Here we generate the hmac based on the object uid and a word that you can alone specify. Then we pass it to the FrontEnd in order to add it on the hidden field and later to compare it.
VERY IMPORTANT: I would strongly recommend to use a word as well. It must be the same everywhere you use it. For me now the word is yourWord.
your_extension/Resources/Private/Templates/Edit.html
<f:form action="update" name="object" object="{object}" extensionName="ExtensionName" pageUid="{settings.flexform.pages.update.pid}" enctype="multipart/form-data">
<f:form.hidden name="hmac" value="{hmac}" />
{...}
</f:form>
Here we define the hidden field with the hmac value. We are going to compare it in the controller.
your_extension/Classes/Controller/YourController.php
public function initializeUpdateAction() {
$args = $this->request->getArguments();
/*Check if the user has not deleted the hmac hidden field*/
if ($args['hmac']) {
/*Regenerate the hmac to compare it with the one from the $args variable*/
$hmac = GeneralUtility::hmac($args['object']['__identity'], 'yourWord');
if ($hmac !== $args['hmac']) {
$this->redirect('list', 'ControllerName', 'ExtensionName', null, $this->settings['global']['error']['pid']);
}
}
else {
$this->redirect('list', 'ControllerName', 'ExtensionName', null, $this->settings['global']['error']['pid']);
}
}
Here we first evaluate if the hmac exists. The user might have deleted the hidden field to avoid the comparisson. If TYPO3 does not find any hmac in the passed arguments ($args['hmac']) then it will redirect the user to the specified page and the object won't be updated.
If TYPO3 finds a hmac, then generates another hmac with the given uid ($args['object']['__identity']) and the word you generated the previous hmac. If it does not match, that means that the user has manipulated the uid. Then TYPO3 redirects the user to the specified page and the object won't be updated.
All this could be written more elegantly but for the sake of this answer, i tried to make it short.
Best regards

Related

TYPO3 Fluid Form returns old values

Am running a form using fluid with two fields, username and password. When first run, correct data is sent to the action. When different data is resubmitted, original data is sent to the controller action instead of the new values. How do I solve this problem?
EDIT: I've discovered that an array containing the user-submitted form fields and their values are obtained from the request, serialized as is and the string stored in a hidden input field called '__referrer[arguments]', which is then submitted with the form back to the user. When the user resubmits the form again with new values, the user doesn't realize that the old values are in the form, in the form of a serialized string, which is submitted together with the new values. Turns out, this is ok if no errors are reported by the validators. In that case the data is simply passed to the controller action and processing continues. But if errors are collected, processing is not sent to the controller action but is sent to the error action. The error action unserializes the old data and forwards that data (instead of the the new values) to the intended action controller. See \TYPO3\CMS\Extbase\Mvc\Controller\ActionController::forwardToReferringRequest().
EDIT: Steps to reproduce;
Create an action with one argument and create a form with one input element and a submit button. Create a validator for the element. Make it a string property. When all is said and done, start by sending a VALID value and let the form return. it will come back with no errors. but also if you look at it's hidden values, you find that the __referrer[arguments] has changes. That's because your previous values were serialized and are there. Now submit an INVALID value and check values entering your action. You'll be stunned they are the old ones.
This is weird. Currently if I disable the production of the __referrer[arguments] input field in the \TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::renderHiddenReferrerFields() method, everything works properly. How do I solve this? Please help.
Where things go wrong:
Here: \TYPO3\CMS\Extbase\Mvc\Controller\ActionController::forwardToReferringRequest() . In this method, arguments are sought from __referrer internal arguments instead of the submitted values. Normally, if errors are found, only two elements are found in the arguments form variable: controller for controller name and action for action name. This is because they were submitted originally with the form and they will recycle without changing as long as the validator finds errors. Nothing else will be added to it. This is good... until it validates. When it validates, results are sent directly to the action controller and not through the error controller. When the process goes back to the form, the object or arguments submitted will be added to the form and returned to the user. Remember it validated. When you then send a wrong value next, it goes to the error controller, but the old values (that validated) and were serialized in the __referrer[arguments] are unserialized and forwarded to your action. And that's how you end up with old values in your action. This is because it assumes that the variable carries only two values inside it; the controller and action names only. It's wrong.
Assumption:
Form post values processing seem to be built under the presumption that once a form validates, you won't need it again.
News.
The values you send are sought from extbase arguments and serialized in \TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper::renderHiddenReferrerFields() method, added to the form just before presenting it to you. All TYPO3 has to do is skip your values and let the default values only be serialized. Just saw that the doc section of the function has this:
/**
* Renders hidden form fields for referrer information about
* the current controller and action.
*
* #return string Hidden fields with referrer information
* #todo filter out referrer information that is equal to the target (e.g. same packageKey)
*/
The #todo part is the news. Hope it's done in the next-patch since there are many scenarios where you want to resume the form such as in data entry.
Solution
Easiest solution: Initialize the empty argument in your action and hand the argument to the form. In my case, I use a DTO.
Example
public function accountLoginAction(DTO\Account\LoginDTO $accountLogin=null):ResponseInterface
{
if($accountLogin){
$this->repository->logon();
...
} else{
$accountLogin=DTO\Account\LoginDTO::getNewInstance();
}
$this->view->assign('object', $accountLogin);
return $this->response();
}
Hope it helps someone.

Apex DateTime to String then pass the argument

I have two Visualforce pages, and for several reasons I've decided it is best to pass a datetime value between them as a string. However, after my implementation my date always appear to be null even though my code seems to compile.
I have researched quite a few formatting topics but unfortunately each variation of the format() on date time seems to not produce different results.
The controller on page 1 declares two public variables
public datetime qcdate;
public String fdt;
qcdate is generated from a SOQL query.
wo = [SELECT id, WorkOrderNumber, Quality_Control_Timestamp__c FROM WorkOrder WHERE id=:woid];
qcdate = wo.Quality_Control_Timestamp__c;
fdt is then generated from a method
fdt = getMyFormattedDate(qcdate);
which looks like this
public String getMyFormattedDate(datetime dt){
return dt.format(); }
fdt is then passed in the URL to my next VF page.
String url = '/apex/workordermaintenanceinvoice?tenlan='+tenlan +'&woid=' + woid + '&invnum=' + invnum + '&addr1=' + addr1 + 'fdt=' + fdt;
PageReference pr = new PageReference(url);
I expected when calling {!fdt} on my next page to get a proper string. But I do not.
UPDATE:
Sorry I guess I should not have assumed that it was taken for granted that the passed variable was called correctly. Once the variable is passed the following happens:
The new page controller creates the variable:
public String fdt
The variable is captured with a getparameters().
fdt = apexpages.currentPage().getparameters().get('fdt');
The getfdt() method is created
public String getfdt(){
return fdt;
}
Which is then called on the VF page
{!fdt}
This all of course still yields a 'blank' date which is the mystery I'm still trying to solve.
You passed it via URL, cool. But... so what? Page params don't get magically parsed into class variables on the target page. Like if you have a record-specific page and you know in the url there's /apex/SomePage?id=001.... - that doesn't automatically mean your VF page will have anything in {!id} or that your apex controller (if there's any) will have id class variable. The only "magic" thing you get in apex is the standard controller and hopefully it'll have something in sc.getId() but that's it.
In fact it'd be very stupid and dangerous. Imagine code like Integer integer = 5, that'd be fun to debug, in next line you type "integer" and what would that be, the type or variable? Having a variable named "id" would be bad idea too.
If you want to access the fdt URL param in target page in pure Visualforce, something like {!$CurrentPage.parameters.fdt} should work OK. If you need it in Apex - you'll have to parse it out of the URL. Similar thing, ApexPages.currentPage() and then call getParameters() on it.
(Similarly it's cleaner to set parameters that way too, not hand-crafting the URL and "&" signs manually. If you do it manual you theoretically should escape special characters... Let apex do it for you.

Handle POST data sent as array

I have an html form which sends a hidden field and a radio button with the same name.
This allows people to submit the form without picking from the list (but records a zero answer).
When the user does select a radio button, the form posts BOTH the hidden value and the selected value.
I'd like to write a perl function to convert the POST data to a hash. The following works for standard text boxes etc.
#!/usr/bin/perl
use CGI qw(:standard);
sub GetForm{
%form;
foreach my $p (param()) {
$form{$p} = param($p);
}
return %form;
}
However when faced with two form inputs with the same name it just returns the first one (ie the hidden one)
I can see that the inputs are included in the POST header as an array but I don't know how to process them.
I'm working with legacy code so I can't change the form unfortunately!
Is there a way to do this?
I have an html form which sends a hidden field and a radio button with
the same name.
This allows people to submit the form without picking from the list
(but records a zero answer).
That's an odd approach. It would be easier to leave the hidden input out and treat the absence of the data as a zero answer.
However, if you want to stick to your approach, read the documentation for the CGI module.
Specifically, the documentation for param:
When calling param() If the parameter is multivalued (e.g. from multiple selections in a scrolling list), you can ask to receive an array. Otherwise the method will return the first value.
Thus:
$form{$p} = [ param($p) ];
However, you do seem to be reinventing the wheel. There is a built-in method to get a hash of all paramaters:
$form = $CGI->new->Vars
That said, the documentation also says:
CGI.pm is no longer considered good practice for developing web applications, including quick prototyping and small web scripts. There are far better, cleaner, quicker, easier, safer, more scalable, more extensible, more modern alternatives available at this point in time. These will be documented with CGI::Alternatives.
So you should migrate away from this anyway.
Replace
$form{$p} = param($p); # Value of first field named $p
with
$form{$p} = ( multi_param($p) )[-1]; # Value of last field named $p
or
$form{$p} = ( grep length, multi_param($p) )[-1]; # Value of last field named $p
# that has a non-blank value

Angular 2 - FormControl setValue 'onlySelf' parameter

Trying to understand what the 'onlySelf' parameter does when passing to setValue.
this.form.get('name').setValue('', { onlySelf: true })
The documentation says: "If onlySelf is true, this change will only affect the validation of this FormControl and not its parent component. This defaults to false."
However I'm struggling to understand this. Still fairly new to the using Angulars' model driven forms.
Angular2 by default will check for the form control/form group validity cascadingly up to the top level whenever there's an update to any form element value, unless you say no. onlySelf is the tool to help you do that.
Say you have a loginForm that has a username field and a password field, both of them are required, like this:
this.userNameControl = this.formBuilder.control('Harry', Validators.required);
this.passwordControl = this.formBuilder.control('S3cReT', Validators.required);
this.loginForm = this.formBuilder.group({
userName: this.userNameControl,
password: this.passwordControl
});
After this code, this.loginForm.valid is true.
If you set the value of a control using the default setting (onlySelf = false), Angular2 will update the control's validity as well as form group's validity. For example, this:
this.passwordControl.setValue('');
will result in
this.passwordControl.valid === false
this.loginForm.valid === false
However, this:
this.passwordControl.setValue('', { onlySelf: true });
will only change passwordControl's validity only:
this.passwordControl.valid === false
this.loginForm.valid === true
Put it this way, let's say that you have a form, called mainForm which is valid. It has four controls on it and all four have a value. Now, you decide to update the value of one of your controls, let's say you update it to some incorrect value and you specify onlySelf: true. If you try to call this.mainForm.valid, you will get the result that your form is valid even though your control is not valid, and it's invalid state should not allow the form to be submitted. But because the forms valid property is reporting true, you will be submitting inconsistent values to the backend.
It might be confusing why you would have this property, but there might be occasions when you don't want to invalidate the form because of one value or control. Probably you have some advanced checks on the server and you want to correct the value on the server or you might depend on a value from some external web service that might not be available at the time. I'm sure there are number of scenarios but this is something from top of my head.

Codeigniter: Submitting Forms

How do I submit a form that can do two different things based on the URI?
For example, if the URI contains a string "new" the form will submit differently than it would if "new" were not in the URI.
I'm having trouble implementing this, as when a form is submitted, it takes the URI of whatever "form_open" says.
Altering the form_open path is probably not the way to do this. How are you using this? Does the person filling out the form affect the "new" string?
What I would do is put a hidden input on the form and set THAT value to "new". Then in the controller, use a GET to take the value of the input form, and do a simple IF / ELSE statement based off the value of that variable.
This way, you could setup several different ways to use the same form - hidden=new, hidden=old, hidden=brandnew, hiddend=reallyold could all process the form values differently, even sending them to different tables in your DB or whatever.
Kevin - I thought I'd done something like this before and I had - here's a quick look:
In routes.php:
$route['some/pathname/(:any)'] = "my_controller/my_function/$1";
Then in mycontroller.php:
function my_function($type)
{
if ($type == "new") {
do this }
elseif ($type == "update)" {
do this }
}