Verifying POST input with a large array


#1

I am trying to create a PHP script which allows a user of my site to choose a background photo for the site pages. I have a default photo that i use and 100 additional photos to choose from. I use an array in my subscription form to store the names of acceptable form fields. For example:

$arraylist = ['option1', 'option2', 'option3'];

Is it the correct method to store values of this magnitude (100)? Is there a better way to do this?

I also ask because of SQL injection. I had previously thought about SQL injection as user input only. I failed to realize that fiddling with a form could allow an SQL injection (disregarding my hard-coded options). I now filter my values with htmlspecialchars() to be on the safe side. However, i wonder if there is a method other than posting a form? is there a server side method for storing this info in a database? is ajax server-side?

also, the form is very simple: i show thumbnails of background photos with a radio button input and a label for each option. The form is submitted with an Update button.

Please remember that i am building a nature related website. The background photos are typically landscape shots (a beach, a walkway, grass, a mountain, sky and clouds etc.)

Thank you for your time.


#2

So, to protect against sql injection, you use parameterized statements. It means that the data going in, is data and not a command. Believe I posted a resource for that once before to you?

As far as the array for acceptable form fields, that is fine, looks like it is a bit lengthy, but if your form has that many fields, so be it - though it could be that you are over complicating things as well? Or maybe I am misunderstanding. Do you mean that you are dynamically building the SQL using that array?


#3

@astonecipher

change background PHP script contains a form in a manner as follows:

<form method="post" action="update/">
  <div><input type="radio" id="poppymeadow" name="cssbgimg" value="poppymeadow" /><label for="poppymeadow">Poppy Meadow</label></div>
  <!-- 100 options currently -->
  <input type="submit" name="bgupdate" value="Update" />
</form>

update index.php

$bgvar = trim(htmlspecialchars($_POST['cssbgimg']));
$uservar = $_SESSION['username'];
$bgkeys = ['poppymeadow', 'option2', 'option3', ... 'option100'];

if (!in_array($bgvar, $bgkeys, true)) {
  // etc. ...
} else {
  include "databasefile.php"; //outside of root
  $_SESSION['bgimg'] = $bgvar;
  header("Location: "); //send back to change background selection script
  exit;

databasefile.php

$dbh = new PDO("mysql:host=$host; dbname=$dbname; charset=utf8mb4", $user, $pass, $dr_opt);
$query = 'UPDATE members SET background = :bgname  WHERE username = :userid';

$stmt = $dbh->prepare($query);
$stmt->execute(array(':bgname' => $bgvar, ':userid' => $uservar));

it’s just a form with 100 options. The option selected gets passed to the database file.
the backgroundimg column is UPDATEd with the new value.
The new value is stored in a SESSION variable to update the css background property.
Each time a member logs in, the image name is retrieved from the database for display on the site.

I figure that i have to use a style tag in the head of each page/template. I think that a php file with a CSS header is not cached and making 100 custom css files seems to be absurd.

I am bothered by the fact that i have such a large array but i love the option to change the background photo.
I remember that you helped me with my subscription form.

By the way, i finally made a login script with a database and it is working now. I have alot of work to do on the login to make it secure.
I am just happy to finally have a login without paying someone to build it for me and it only cost me time.


#4

Ideally, you would do all of this dynamically, without writing out 100 different values in an array or 100 different radio buttons.

Are the permissible background images all stored in a single folder or have a unique naming pattern so that you could write code that gets (see the glob() function) an array of the permitted choices?

Once you get an array of permissible choices, you should use this array to dynamically build and output the radio button markup (you should also pre-check the radio button corresponding to any existing choice) and use this array when validating the submitted choice.

htmlspecialchars() is an output function, you use it when outputting values to the web page that could break the html markup. Don’t use it on input data. Submitted input data may be output on a web page at some point, but you would use htmlspecialchars() when it is output, not when it is submitted.


#5

Yes! sometimes i am stupid. I suppose that i cram my head full of php, then i get confused and make very dumb errors. Another reason why i am being careful with a login and database work. Anyway, i think that you are suggesting to read all of the image names from the folder to store them into an array. Then use that array with a loop to display the choices. yes?

Thank you for setting me straight about the htmlspecialchars. I need to revisit this subject and reeducate myself. My apologies for this negligence.

I have an early appointment for physical therapy, so i need to log off for the night. I’ll code this tomorrow and correct my stupidity. Good night everyone :smile:


Form submission via Javascript
#6

so, here is my solution to the dynamic display of background options:

    <form method="post" action="update/">
    <?php
      $bgIndexer = glob("../img/*.*");
      $bgFormat = array('jpg','jpeg');

      for ($i=0; $i < count($bgIndexer); $i++) {
          $bgId = basename($bgIndexer[$i]);
          $ext = strtolower(pathinfo($bgId, PATHINFO_EXTENSION));
          $bgName = pathinfo($bgId, PATHINFO_FILENAME);

          if (in_array($ext, $bgFormat)) {
              switch ($bgName) {
                case $_SESSION['background']:
                  print  "<div><input type=\"radio\" id=\"" . $bgName . "\" name=\"cssbg\" value=\"" . $bgName . "\" checked /><label for=\"" . $bgName . "\">" . $bgName . "</label></div>";
                  break;
                default:
                  print  "<div><input type=\"radio\" id=\"" . $bgName . "\" name=\"cssbg\" value=\"" . $bgName . "\" /><label for=\"" . $bgName . "\">" . $bgName . "</label></div>";
              }
          } else {
              continue;
          }
       }
    ?>
      <input type="submit" name="bgupdate" value="Update" />
    </form>

the code is working dynamically and the current background is shown as checked. I can add a css class later to make the current choice more obvious.

is the code okay or can it be simplified? i am obviously a beginner, so i always appreciate pro guidance.

problem: i’ve learned html, css and javascript in the 90s. Thus, i am used to hard coding pages and making folders to store all of the data. When we think about submitting a form and processing that data: i always make a formpage.php, then submit that form to a processform.php file in a subdirectory of the formpage.php (so as to maintain an index.php system only). I do not know how to acomplish this task without this system. I’m told that ubmitting to self is dangerous, so i do not submit to the same page. I say this because how can i store the filenames in an array for input verification? i suppose that i need to run glob again for array values in the processform.php, unless you have a better method.

can you recommend the best way to clean/sanitize input before storing it in a database?


#7

There’s nothing wrong with the form and form processing code being on the same page. What is a problem is directly using portions of the current url to do so, since it could contain html/css/javascript that would allow cross site scripting if you output it on a web page, without applying htmlentities() to it.

In html5, you can leave the action=’…’ attribute out of the form tag and the browser will submit to the same page.

To get an array of just the file’s filenames, use some of php’s array functions to operate on the data as a set -

$pattern = "../img/*.*";
$files = glob($pattern);

// break the filename into its various parts - i.e. apply the pathinfo() function to each element in the $files array
$files = array_map('pathinfo',$files);

// get an array of just the filename column values
$filenames = array_column($files,'filename');

// examine the result
echo '<pre>'; print_r($filenames);

This will give you an array of the permitted filename values. You should use a foreach(){} loop when looping over an array. You would use this same array with an in_array() statement to validate that the submitted form value is one of the permitted choices.

In your code posted above, if the use of strtolower() is because there can be ‘random’ letter cases used in the files, you should lowercase them when the files are uploaded to the server. If you are filtering the files using the jpg and jpeg extensions because there are other extensions present or files other than background images are in the folder, if possible, just store the background images in the folder. The glob() function would then return only the permitted background images.

If you cannot do the above, you can filter the $files array by writing a call-back function and using array_filter(). The call-back function would strtolower() the ‘extension’ element and use in_array() to identify array entries to keep. This would look like -

// call-back function to filter by extension
function _filter_ext($var)
{
	$ext = ['jpg','jpeg'];
	return in_array(strtolower($var['extension']),$ext);
}

// filter the array by the extension value
$files = array_filter($files, '_filter_ext');

The above would be used before the array_column() statement.

Other than trimming data, so that you can detect if all white-space characters were entered, you should NOT modify user submitted data. You validate that it is either an expected format or value, then safely use it in whatever context it is being used in. In an sql query context, use a prepared query to supply the data when the query is executed. In a html context, use htmlentities() when outputting it onto a web page/email.


#8

@phdr
so, i am obviously a beginner. I thought that i had to loop through the glob to get the values into an array. Your code is very clean and well done. I’ve changed the code to what you have posted. Brilliant! You are obviously a Master of PHP. I feel bad that you have to put up with my novice tactics. Anyway, I’ve learned alot from you all ready. Thank You.

I always trim the input for spaces but i also use the filter_var with FILTER_SANITIZE_STRING for string input. I’ve tested all of the available options for sanitization and some of them can really mess up an otherwise normal string. Thus, your advice is well taken.

i should mention that my site will be hosted on a dedicated server, so i can name all of the files in lowercase or keep them in a separate folder. The reason for trying to isolate jpg files: i have yet to learn classes and router/controllers. I am not very knowledgeable about Apache. I hate the idea that someone can navigate to folders with no php content/webpages, such as an image directory. I always disable index browsing so i keep an index.php in every directory to prevent an error page. I am trying to learn better web development but that takes time. I’d like to get my site online this year and hopefully by June. I have alot to learn about PHP and Databases.

I am wondering what you think about this background changing page: i am using a form with radio buttons and one submit button. Do you think that using a form for each background is absurd? i’d like to allow the input radio button to be replaced by a submit button. Thus, one-click update process without using JavaScript. Does this sound cuckoo to you? otherwise, i can keep the radio inputs and style the page as a css3 slider with a constant update button available.