Forums

Questions (Mainly about server side validation)

brad2k10 19 May, 2010
Ill try to be brief here. I am creating a large form, for registration. Not joomla registration. Just a registration form.

In terms of server side validation am i correct when assuming?

When a user clicks submit, is information passed through jrequest? and therefore some validation takes place to strip html,etc and prevent injection.

If not lets say i take a field, so "first name" and i want to pass it through server side validation and strip it of html,and everything except 'alpha' how would i go about doing this? could an example be shown of say where the errors gather in an array for easiest lets say i have 2 fields "firstname"-"surname" and then parses an error after completing validation on the entire form.

I am using multiform, 'form1' will display information about the form and how to fill it out, once the user clicks continue 'form2' (main form will load)... on form1 is ther a way i can embed a browser test and check wether java script is enabled and return an error if not. As i want to be able to use livevalidation on the final form.

Thanks for your time beforehand.

Brad
GreyHead 20 May, 2010
Hi Brad,

When the user clicks submit the information is passed to the PHP $_POST array, it is *not* passed through JRequest by ChronoForms[1]. It's up to you to do that if you need to. (The problem is that different users have different cleaning requirements.)

I would use the ServerSide validation just for validation (because of the syntax there) and do my filtering/cleaning in the OnSubmit Before box.

To filter data use the JRequest::getXxx() methods. So to filter a 'name' input use
$name = JRequest::getString('name', '', 'post');
JRequest::setVar('name', $name);
this will retrieve a filtered value from the $_POST array and write it back in again.

You could use getWord() but this would disallow names like O'Connor, or Renée

To stack validation messages write the errors into a $messages[] array and check that after all the validations: In sketch form
<?php
$messages = array();
if ( fail some test ) {
  $messages[] = 'error message';
}
if ( fail some other test ) {
  $messages[] = 'another error message';
}
. . . // more tests
if ( count ($messages) ) {
  return implode('/n', $messages);
}
?>


Lastly Joomla has a browser sniffer built in (it may or may not be accurate).
jimport('joomla.environment.browser');
$browser = &JBrowser::getInstance();
if ( $browser->hasFeature('javascript')} {
  // do something
}

Bob

PS Data saved to the database is passed through JRequest but with the least possible filtering
$post = JRequest::get( "post" , JREQUEST_ALLOWRAW );

Filter bit mask. 1=no trim: If this flag is cleared and the input is a string, the string will have leading and trailing whitespace trimmed. 2=allow_raw: If set, no more filtering is performed, higher bits are ignored. 4=allow_html: HTML is allowed, but passed through a safe HTML filter first. If set, no more filtering is performed. If no bits other than the 1 bit is set, a strict filter is applied.

brad2k10 20 May, 2010
Thanks Bob for such a speedy reply the info you've given me is exactly what I wanted to know. Thanks again. I've also read in other posts that you are writing a book on Chronoforms and once it's complete I'd be happy to buy a copy so if you could provide me with info how to do that at the time that would be great also. Thanks again. Brad
GreyHead 21 May, 2010
Hi Brad,

Thanks. The book is looking good. I'm currently half-way through the Chapter re-writes responding to comments on the first half from a technical reviewer and an editor.

If all goes well it is scheduled for publication around September by Packt Books though there is nothing on their site yet.

The book has about 80 'hands-on' recipes for using ChronoForms from basic installation through to an advanced example of using a multi-page form to build a Joomla! article; and much in between.

Bob
brad2k10 23 May, 2010
Thanks for info on your book, Ill keep an eye on that publisher for it.

Could you lend me some more assistance

Here is a snapshot of some cleaning/filtering, is there a better way of doing this? Now im not really up on php that much, but in other languages i could perform this by setting up an array, and then doing a for each loop. Instead of having huge amounts of code like this. Could you help me with the syntax for php arrays because i cant get my head round it?

<?php
$stu_firstname = JRequest::getWord('stu_firstname', '', 'post');
JRequest::setVar('stu_firstname', $stu_firstname, 'post');
$stu_middlename = JRequest::getWord('stu_middlename', '', 'post');
JRequest::setVar('stu_middlename', $stu_middlename, 'post');
$stu_surname = JRequest::getWord('stu_surname', '', 'post');
JRequest::setVar('stu_surname', $stu_surname, 'post');
$stu_pref_name = JRequest::getWord('stu_pref_name', '', 'post');
JRequest::setVar('stu_pref_name ', $stu_pref_name , 'post');
$stu_address_no = JRequest::getString('stu_address_no', '', 'post');
JRequest::setVar('stu_address_no', $stu_address_no, 'post');
?>
GreyHead 24 May, 2010
Hi Brad,

You can simplify the code if you have a lot of fields.
<?php
$results['word']   = array('stu_firstname', 'stu_middlename', 'stu_surname', 'stu_pref_name');
$results['string'] = array('stu_address_no');
$results['var']    = array('xxx');
foreach ( $results as $k => $v ) {
  switch ($k) {
    case 'word':
      $temp = JRequest::getWord($v, '', 'post');
    break;
    case 'string':
      $temp = JRequest::getString($v, '', 'post');
    break;
    case 'var':
    default:
      $temp = JRequest::getVar($v, '', 'post');
    break;
  }
  JRequest::setVar($v, $temp, 'post');
?>

Bob
brad2k10 24 May, 2010
Bob

If i can just ask for your assistance some more. I have a 6 step multiform. At step 6, it submits to a database.

So two things :
1./
Where do i put the data filtering/validation, can this be done on the child forms or should it be done on the motherform in the submit box. (Im assuming without looking that the database is only written on the motherform on final submit).

2./
Now as you suggested i wrote the php accordingly for data validation, now to protect the form from unwanted bots and submissions i was going to do some server side validation on the filtered data. Simple stuff, along the lines of testing certain inputs, to make sure they arnt full of random characters. Again does this validation need to be done on the mother form or child forms per step?.

Is there a good way to stop bots and other unwanted submissions easily. i was thinking maybe some logic testing on step 6. "Please type HUMAN here". Would that be a suitable way of doing it or would you do it different.
GreyHead 25 May, 2010
Hi Brad,

Validation should be done step by step on the child forms. Basically get each step correct before moving on. The same applies to checking for 'sensible data'.

Filtering could be either. The multi-page plugin cumulates the step data in the session so that it is all available at the final step.

Saving to the database can be done either at the end or step by step, I prefer step by step unless the form is really short. You then need to make sure that the record identifier is passed forward so that later steps update the same record.

Bot proofing - in whatever form should be on step 1 (there's only a tiny probability that a bot will get to the later steps by stumbling on the direct url). You can use a Captcha or a human test.

Bob
brad2k10 25 May, 2010
Hi Bob

(Ive completely edited this post) Ive decided i will let the form submit to database in one place at the end. Because the way i see it, if i let it write to database on each step, and a user has a problem then i will have half complete rows. Aswel as this, if thier session times out (i know i can control the amount of time before this happens) then i will get half filled rows. In practise the form should take no longer than 5 minutes to fill out anyway.

However could you shed some light on this, i want to be able to to validate phonenumber fields could you provide me with a working example to check that the field contains 11 digits. Is that possible?

a question on the built in captcha, is this method still strong, or can more complex bot scripting beat it? ...

a question on multiform, ive sorta tested this, and it works, but ive got the "onsubmit" code for form 6, showing a thank you message and then echo'ing some information filled in on previous steps. Is there a way to include the cf_id (as i want the user to have a identifier for thier submitted record). Is this possible, in terms of security, does echo'ing data at the end like this pose any security flaw, (assume i have protected all inputs from injection by filtering and validation)

Brad
GreyHead 28 May, 2010
Hi Brad,

The trade-off between saving at each step and saving at the end is pretty much as you describe.

You can use a custom validation with a reg-exp to validate phone numbers There's a similar example here

The built in Captcha is still secure as far as I know. With a multi-page form the problem is minimal anyhow, not many bots can find their way to the later pages.

You can add a PHP based test if that helps, check the result in the ServerSide validation box as usual.

You can get the cf_id of the new record and display it afterwards if you wish. ChronoForms puts the saved data into $MyForm->tablerow["table_name"]

My personal preference is not to use cf_id as a public record key but instead to generate a short random string like XY99A. It removes the possiblity of a user guessing what the preceding and following ids might be. I generate this, usually on saving the form, check it's unique, and save it into an extra database column.

Bob




Bob
andyss 20 Jan, 2012
Can this be used for CFv4 or is this type of security built in to the new version?

    <?php
    $results['word']   = array('stu_firstname', 'stu_middlename', 'stu_surname', 'stu_pref_name');
    $results['string'] = array('stu_address_no');
    $results['var']    = array('xxx');
    foreach ( $results as $k => $v ) {
      switch ($k) {
        case 'word':
          $temp = JRequest::getWord($v, '', 'post');
        break;
        case 'string':
          $temp = JRequest::getString($v, '', 'post');
        break;
        case 'var':
        default:
          $temp = JRequest::getVar($v, '', 'post');
        break;
      }
      JRequest::setVar($v, $temp, 'post');
    ?>


Andy
GreyHead 21 Jan, 2012
Hi Andy,

This filtering isn't built in to CFv4 (It doesn't have any reliable way of knowing the format of your data). There is the Auto ServerSide validation action which does something similar but not quite the same. The main difference is that the filtering code cleans data; the AutoServerside validation does an accept/reject test on it.

You can adapt this code for CFv4 by replacing this line
JRequest::setVar($v, $temp, 'post');
with
$form->data[$v] = $temp;
Note that this should be run early on (probably in the ServerSide Validation) so that the $form->array is updated before it is used in any other action.

Bob

The AutoServerside validation action uses a series of preg-replace methods to test the data. If the submitted data does not pass the test the On Fail event is triggered.

Required: checks that a result is included in the form data. The result may be blank or empty!!! This is not the same as the usual meaning of 'required'.

Empty / Not-empty: checks for the presence or absence of a value in the form data. Empty uses count() for arrays / strlen() for strings; Not empty uses preg_match with [^.*]

Alpha uses a case insensitive preg_match with ^[a-z ._-]+$

AlphaNumeric uses a case insensitive preg_match with ^[a-z0-9 ._-]+$

Digit uses a preg_match with ^[-+]?[0-9]+$

NoDigit uses a preg_match with ^[^0-9]+$

Number uses a preg_match with ^[-+]?\d*\.?\d+$

Email uses a preg_match with ^([a-zA-Z0-9_\.\-\+%])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$
Note that 'legal' email formats can vary widely and this may not permit every email -good for 99% of the common ones though.

Phone uses a preg_match with ^\+{0,1}[0-9 \(\)\.\-]+$

International Phone uses a preg_match with ^\+{0,1}[0-9 \(\)\.\-]+$

URL uses a case insensitive preg_match with ^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&%\$#\=~])*$
Again this should be OK for the vast majority of URLs

The Joomla! filters offer this range (based on the PHP Filters). The filtering method takes three parameters: the data, a mask, a type. (Note that you don't usually access this directly).

Filter bit mask.
1=no trim: If this flag is cleared and the input is a string, the string will have leading and trailing whitespace trimmed.
2=allow_raw: If set, no more filtering is performed, higher bits are ignored.
4=allow_html: HTML is allowed, but passed through a safe HTML filter first. If set, no more filtering is performed.
If no bits other than the 1 bit is set, a strict filter is applied.

Integer/Int uses a preg_match with -?[0-9]+

Float/Double uses a preg_match with -?[0-9]+(\.[0-9]+)?

Bool/Boolean uses (bool)

Word uses a case insensitive preg_replace with [^A-Z_]
i.e. anything other than A-Z, a-z or underscore is removed.

Cmd uses a case insensitive preg_replace with [^A-Z0-9_\.-]

Alnum uses a case insensitive preg_replace with [^A-Z0-9]

Base64 uses preg_replace with [^A-Z0-9\/+=]

String uses (string) and tries to decode any UTF, decimal or hex encoded characters then removes any 'bad' HTML (see the mask settings).

Array uses (array)

Path uses (string) and preg_match with ^[A-Za-z0-9_-]+[A-Za-z0-9_\.-]*([\\\\\/][A-Za-z0-9_-]+[A-Za-z0-9_\.-]*)*$

Username uses preg_replace with [\x00-\x1F\x7F<>"\'%&]
andyss 21 Jan, 2012
Hey Bob, thanks for the info. I figured as much but wasn't 100% sure whether to set the cleansed values back to the posted data with the setVar command or to the data array (if I understand the workings of CF4 properly).

Andy
Max_admin 22 Jan, 2012
Hi Andy,

Set them in the $form->data array!

Regards,
Max
Max
ChronoForms developer...
Did you try ChronoMyAdmin for managing your Joomla database tables ?
andyss 24 Jan, 2012
I get a bunch of errors containing:


Warning: Illegal offset type...
Warning: Illegal offset type in isset or empty...


Any thoughts?

Andy
GreyHead 24 Jan, 2012
Hi Andy,

What is the code you are using and what are the line numbers in the Warning messages.

Bob
andyss 24 Jan, 2012
This line:

$form->data[$v] = $temp;


Andy
GreyHead 24 Jan, 2012
Hi Andy,

It's may be breaking when $v is an array; you'll need to test for that and do something. Still hard to tell without more specific data.

Bob
This topic is locked and no more replies can be posted.