Eirinya has asked for the wisdom of the Perl Monks concerning the following question:

Good evening to all of you esteemed monks,
I'm new of perl-cgi-mysql-Apache(2) and I'm learning them at same time for my new job.
I prepared these scripts(as exercises)in a database suitable for testing with the intention of create an html form page where a user can insert some data in the prova.prodotti database's table in a safe way by the form and than get a page with the updated list of all products in the table.
LWP was a module that I had to use for this exercise but I don't know how to do this neither without so I'm trying to do it one step at a time(more or less).
The two scripts are:
LWP_Query.cgi

#!/usr/bin/perl -wT use strict; use CGI; use warnings; use 5.010; use utf8; use DBI; use Data::Dumper; # use LWP::Simple; #header my $query = CGI->new(); warn Dumper($query); print $query->header( "text/html" ), $query->start_html(-title => "Inserisci prodotti", -bgcolor => "#ddffdd" ); #definition of variables my $db="prova"; my $host="localhost"; my $user="I-inserted-here-the-user-name"; my $password="I-inserted-here-the-user-pass"; #connect to MySQL database my $dbh = DBI->connect ("DBI:mysql:database=$db:host=$host", $user, $password) or die "Can't connect to database: $DBI::err +str\n"; print <<'HERE'; <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title>Prova input</title> </head> <body bgcolor= #ffddff> <h1 align="center"> Inserisci questo prodotto nel database de +i negozi:</h1> <br><br> <p align="center"> <form action="http://localhost/cgi-b +in/databaseLWP.cgi" method="post"><br> </p> <p align="center"><br> <input type="text" placeholder="Inserisci qui +il nome del prodotto" name="key_nome_prod" size="30" style="height:25 +px;font-size:18pt" maxlength$ <input type="text" placeholder="Inserisci qui +la tipologia del prodotto" name="key_tipo_prod" size="30" style="heig +ht:25px;font-size:18pt"maxle$ <input type="submit" value="Sent" name="invi +a" style="height:35px; width:70px" /> <input type="reset" value="Reset" style="hei +ght:35px; width:70px" /> </p> </body> </html> HERE print $query->end_html(); $dbh->disconnect( ); exit;


databaseLWP.cgi

#!/usr/bin/perl -wT use strict; use CGI; use warnings; use 5.010; use utf8; use DBI; use Data::Dumper; use LWP::Simple; #header my $query = CGI->new(); warn Dumper($query); print $query->header( "text/html" ), $query->start_html(-title => "Query prodotti", -bgcolor => "#ddffdd" ); #definition of variables my $db="prova"; my $host="localhost"; my $user="I-inserted-here-the-user-name"; my $password="I-inserted-here-the-user-pass"; #connect to MySQL database my $dbh = DBI->connect ("DBI:mysql:database=$db:host=$host", $user, $password) or die "Can't connect to database: $DBI::err +str\n"; ##assign keyword to a variable #my $key_id_prod = "key_id_prod"; my $key_nome_prod = "key_nome_prod"; my $key_tipo_prod = "key_tipo_prod"; #prepare the query my $sql = "INSERT INTO prodotti (nome_prod , tipologia_prod) VALUES (? + , ?)" ; my $sth = $dbh->prepare( $sql); $sth->bind_param( $key_nome_prod, $key_tipo_prod); #execute the query $sth->execute( ); # Retrieve the results of a row of data and print my ( $id_prod); $sth->bind_columns ( undef,\$id_prod, \$key_nome_prod, \$key_tipo_prod + ); print "<i>La lista aggiornata dei prodotti presenti nel database negoz +i è:</i> <br><br>"; print " ============================================================== +===== <br><br>"; while ( $sth->fetch( ) ) { print "<i>Id del prodotto=</i><b> $id_prod </b>,<i> il nome del +prodotto è</i><b> $key_nome_prod </b> della tipologia $key_tipo_prod +.<br>"; } print $query->end_html(); $sth->finish( ); $dbh->disconnect( ); exit;

Both scripts are in the folder cgi-bin and the error that sudo tail -f /var/log/apache2/error.log showed me is this:
eirinyalaura@Laura://var/log$ sudo tail -f /var/log/apache2/error.log [sudo] password for eirinyalaura: [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: DBD::mysql: +:st bind_columns failed: Statement has no result columns to bind (per +haps you need to successfully call execute first, or again) at /usr/l +ib/cgi-bin/databaseLWP.cgi line 54., referer: http://localhost/cgi-bi +n/LWP_Query.cgi [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: DBD::mysql: +:st fetch failed: fetch() without execute() at /usr/lib/cgi-bin/datab +aseLWP.cgi line 59., referer: http://localhost/cgi-bin/LWP_Query.cgi [XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: $VAR1 = bless( { [XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: '.par +ameters' => [], [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: + '.fieldnames' => {}, [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: + 'use_tempfile' => 1, [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: + '.charset' => 'ISO-8859-1', [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: + 'escape' => 1, [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: + 'param' => {} [Thu Dec 10 XXX] [cgi:error] [pid XXX] [client ::XXX] XXX: + }, 'CGI' );

That probably be clear for anybody has experience with these languages but not so much for me that also with documentation don't understand well about binding and also fetching.
Be patient because this is just my third script(unfortunately I have few time to learn how to work in a good and safe way on databases) and this is why I'm seeking your wisdom.

Replies are listed 'Best First'.
Re: Bind and fetch fails
by hippo (Archbishop) on Dec 10, 2015 at 18:58 UTC

    The query in databaseLWP.cgi is an INSERT and therefore calling bind_columns on the already executed statement makes no sense. Nor does the subsequent fetch. Have you forgotten to prepare and execute some other SELECT query in the meantime, perhaps?

      *Updated* I didn't prepare other Select, all that I did for the exercise is in the code posts. I thought about what you said and I thought that probably bind_column was useless because I just wanted print the values of the variables presents in the table and maybe it's not important to bind them to columns so, trying to think in a simpler way, I removed the while cycle and the subsequent fetch replacing them with a simple print of variables.
      I also changed the assigment of keywords inserted in the form html of the first script to the variables in databaseLWP(the script that I'm modifying) in a more correct way, now the reply-page returns the correct valor(except id), but not from table, just from html form, in fact the id value isn't shows.
      At least I found the bug that caused the problem with inclusion of the value in databases and was the mysql syntax in the query(I missed the '' near the names of values), a common beginner mistake, but I also changed the ? with directly name of the variables because if I didn't it the query inserted only ? instead of values.
      With these modifications the script now puts the value in the database but without enough security in my opinion, what do you suggest?

      (...) #prepare the query my $sql = "INSERT INTO prodotti (nome_prod , tipologia_prod) VALUES (' +$key_nome_prod' , '$key_tipo_prod' )" ; my $sth = $dbh->prepare( $sql ); #$sth->bind_param( $key_nome_prod , $key_tipo_prod ); (...) print "<i>La lista aggiornata dei prodotti presenti nel database negoz +i è:</i> <br><br>"; print " ============================================================== +===== <br><br>"; print "<i>Id del prodotto=</i><b> $id_prod </b>,<i> il nome del prodot +to è</i><b> $key_nome_prod </b> della tipologia $key_ti$

        but I also changed the ? with directly name of the variables because if I didn't it the query inserted only ? instead of values. With these modifications the script now puts the value in the database but without enough security in my opinion, what do you suggest?

        Don't put quotes around the question marks. If you do they will be treated as string literals. Here's some sample code to get you started:

        my $sql = 'INSERT INTO prodotti (nome_prod , tipologia_prod) VALUES (? +, ?)' ; my $sth = $dbh->prepare ($sql); my $res = $sth->execute ($key_nome_prod, $key_tipo_prod); die ("Result is $res instead of 1: " . $sth->errstr) unless (defined $ +res && $res == 1);

        In order this:

        1. Sets up the SQL statement with 2 placeholders for the data values
        2. Prepares the SQL statement into a statement handle $sth
        3. Executes one insert with the two variables as bound arguments and captures the result
        4. Throws an exception if the result is not as expected

        Constructing the query this way with the bind parameters means that the data from the untrusted source is handled securely by the driver and eliminates the possibility of SQL injection from this direction.