First of all: This article does not show any existing security problems with formhandler. It shows you two real world examples, which I found on a TYPO3 website I did not create, but had to do maintenence for. I will also show what I have done, to (hopefully) resolve the problems.
The TYPO3 extension
formhandler is known as the swiss army knife, when it comes to all kind of forms in TYPO3. Formhandler is
flexible in many ways and allows a TYPO3 integrator not just to create complex forms with several validation options, but also to
prefill fields when a form is loaded or to
save/update fields in the TYPO3 database when a form is successfully submitted. I really like this extension and use it in many projects because of it's clean structure, flexibility and because it is actively maintained.
Formhandler is often not just used to create simple contact forms, but also to provide forms which
shows, creates or modifies records from/in the TYPO3 database. If the TYPO3 integrator does not think about
security at this point, there are several things that can go wrong.
I'm not really a security expert, so if I write totally nonsence in this article, feel free contact me, so the article will contain correct and helpfull information for others.
Example 1 - PreProcessor_LoadDB and GP variable causing leackage of sensitive data
In the first example, a website user does submit data through a form from a
third party extension. After the data is submitted, the user receives an e-mail containing a
link to a formhandler page, where the user has to fill out some other form fields. The link, which is sent to the user is like shown below:
http://www.domain.tld/myform.html?formname[uid]=123
The parameter "
formname[uid]" contains the uid of the newly created record from the third party extension.
When I opened the form the first time, I did not see anything special (expect from the URL parameter). All form fields where empty, so I asked myself, why the URL contained the parameter. After I had a look at the output of the website, I knew, what the URL was used for. The output of the website contained 3 hidden formfields like shown below:
<input type="hidden" name="formname[uid]" value="123">
<input type="hidden" name="formname[recipientname]" value="John Doe">
<input type="hidden" name="formname[email]" value="email@domain.tld">
Ouch, now I could easily find out, who else did submit data through the form from the third party extension by just
increasing/decreasing the uid. The original TYPO3 integrator used a
PreProcessor_LoadDB preprocessor to load the additional data in the hidden fields.
Depending on how the database lookup is integrated, it could result in a SQL injection, if you just pass the given UID directly to the
select.where of the
PreProcessor_LoadDB preprocessor.
Example 2 - Finisher_DB and GP variable causing unwanted data manipulation
The second example uses the same form as shown in example 1. When a website user fills out the form, all fields get submitted to the server and an e-mail ist sent with a
Finisher_Mail finisher. As you may guess, the recipient email is taken from the hidden input field, which in case is very bad, since you just can replace the e-mail address in the hidden field with another e-mail address and after form submission, a totally
different recipient will receive an e-mail sent from the server.
If you don't think it can get worst, it actually will. The formhandler setup did also contain a
Finisher_DB finisher, which
updated some fields on a given record in the TYPO3 database. Since the
uid to this record is parsed directly through
GET/POST variables, it can just be changed and within that, you can just submit the form with different uids and
update the fields that the Finisher_DB updates with garbage - of course for
all records in the table.
Problem summary and solution approach
All named problems rely on the some cause - user input is just seen as trusted. At no point it is actually checked, if the user has changed the uid or has replaced the values from hidden input fields with own content.
Actually, to take benefit from
PreProcessor_LoadDB to prefill fields with Database values
or
Finisher_DB to update records, you actually
need to know the uid from the record where to fetch/update the desired values. So how can you make sure, that the uid is
not changed by the user?
Adding a HMAC to the URL
We can add a second parameter to the URL, that contains the
HMAC for the
uid parameter. This HMAC will later on be used to
validate, if the
uid has been modified by the user.
TYPO3 has a
HashService (TYPO3\CMS\Extbase\Security\Cryptography\HashService), which can be used to
generate and
validate HMACs. To generate a HMAC for a given string, the TYPO3 HashService uses the TYPO3
encryption key as shared secret. The function generateHmac($string) returns a HMAC for a given string as shown below (short version without type/encryption key check)
hash_hmac('sha1', $string, $encryptionKey);
The TYPO3 HashService also has a function called validateHmac($string, $hmac) to validate, if the given string matches a given HMAC.
Using the generateHmac function, I end up with formhandler URLs like shown below:
http://www.domain.tld/myform.html?formname[uid]=123&formname[uidhmac]=averylonghmac
Adding HMAC check to formhandler
Formhandler has
interceptors, which can be used on
init and on
save of a form. Init-interceptors are called each time a form is displayed and save-interceptors are called before the form-finishers are executed. So the best approach to add the
HMAC check is to do this through an own
interceptor.
I created a configurable interceptor, which is able to use the validation functions from the TYPO3 HashService.
The source code for the new interceptor is available in this
gist. You can just create an own extension with this class and then use it in the formhandler TypoScript settings. Below follows an example TypoScript setup, which uses the new interceptor.
initInterceptors.1 {
class = Tx_MyFormhandlerExtension_Interceptor_HashService
config {
redirectPage = 11
validateHmac {
fields.uid = uidhmac
}
}
}
The configuration above now validates, if the
given uidhmac is the correct HMAC for the
given uid. If this validation fails, the user is redirected to a configured page.
You can also use the interceptor with
appended HMAC string. Those HMACs are created using the appendHmac function in the HashService. Basically it just takes the given string and appends the HMAC for the string to it. An url could look like shown below.
http://www.domain.tld/myform.html?formname[appendedhmac]=123averylonghmac
To validate the given appended HMAC string you can use the interceptor as shown below.
initInterceptors.1 {
class = Tx_MyFormhandlerExtension_Interceptor_HashService
config {
redirectPage = 11
validateAndStripHmac {
fields.1 = appendedhmac
}
}
}
Conclusion
Nearly all described problems from the two examples have been solved with the new interceptor. A user can now not just increase/decrease the GET/POST parameter uid to retreive the personal data of other users and he/she can may also not be able to update other database records, if the interceptor is also configured as a
save-interceptor.
The only problem that remains is, that a user can replace the fetched e-mail-address with different one. I simply resolved this issue by removing the e-mail-address from the output and doing a lookup for it in a special save-interceptor I created.
I've created a
patch for formhandler and maybe the new interceptor can make it as a core feature of formhandler.
So always remember: Don't trust user input and always think twice when working with user generated data.