Update db row with perl cgi - mysql

I am trying to update my sql table with some new values entered into the user. For some reason the sql command is not updating my db. I am getting the correct values I verified. Here is my code
#!/usr/bin/perl
#This is going to be the user login check and will set a cookie
use DBI;
use CGI qw(:standard);
use strict;
#Connection error
sub showErrorMsgAndExit {
print header(), start_html(-title=>shift);
print (shift);
print end_html();
exit;
}
#Connecting to the database
my $dbUsername = "root";
my $dbPassword = "password";
my $dsn = "DBI:mysql:f18final:localhost";
my $dbh = DBI->connect($dsn, $dbUsername, $dbPassword, {PrintError => 0});
#error checking
if(!$dbh) {
print header(), start_html(-title=>"Error connecting to DB");
print ("Unable to connec to the database");
print end_html();
exit;
}
print header;
print start_html(-title=>'Add Classes');
#Get the information the user entered
my $id = param('classid');
my $className = param('classname');
my $department = param('department');
my $classnum = param('classnum');
my $grade = param('grade');
my $credits = param('credit');
print "$id $className, $department, $classnum, $grade, $credits";
#first sql check to see if username is already taken
my $check = "UPDATE tblclasses(classname, department, classnum, grade, credits) VALUES (?, ?, ?, ?, ?) WHERE classID = $id";
my $sth = $dbh->prepare($check);
$sth->execute($className, $department, $classnum, $grade,$credits);
print "<h1>Success</h1>";
print "<form action=http://localhost/cgi-bin/edit.pl method = 'post'>";
print "<input type = 'submit' name = 'submit' value = 'Update Another'>";
print "</form>";
print "<form action=http://localhost/cgi-bin/actions.pl method = 'post'>";
print "<input type = 'submit' name = 'submit' value = 'Back to actions'>";
print "</form>";
print end_html();
exit;
When I try to run the sql command in mysql workbench it successfuly updates the row. What is my issue?

There is an error in the syntax of your SQL statement :
UPDATE tblclasses(classname, department, classnum, grade, credits)
VALUES (?, ?, ?, ?, ?)
WHERE classID = $id
Should be written :
UPDATE tblclasses
SET classname = ?,
department = ?,
classnum = ?,
grade = ?,
credits = ?
WHERE classID = ?
See the mysql docs.
Side notes (as commented also by #Grinnz) :
you should always « use strict »
you should set DBI attribute « RaiseError » to 1 on your database or statement handle(s) ; hence all DBI errors become fatal ; disabling both « RaiseError » and « PrintErrror » results in DBI neither dying on errors nor reporting them, hence you must manually check the return code of each and every DBI call to make sure that it worked - see the DBI docs
you should bind all variables in your SQL statement to void SQL injection (you did not bind $id, I changed that in the above query)

Without knowing the DBMS I can't be 100% certain, but it appears as though you blended the syntax for an insert and an update command. The correct syntax for an update should be:
UPDATE tblclasses
set
classname = ?,
department = ?,
classum = ?,
grade = ?,
credits = ?
WHERE classID = $id
Also, for what it's worth, you should also be able to pass the $id variable as a parameter also rather than interpolating it. This, in theory, will be kinder to the database as it will be compiling once and executing the same SQL statement over and over, only with different bind variable values:
my $check = qq{
UPDATE tblclasses
set
classname = ?,
department = ?,
classum = ?,
grade = ?,
credits = ?
WHERE classID = ?
};
my $sth = $dbh->prepare($check);
$sth->execute($className, $department, $classnum, $grade,$credits, $id);

Related

Any risks of being SQL injection attack in this case?

I have a html file that contains a submit form ,which asks the users the fill in their personal info .
Then it will post and store into the DB by method of PHP SQL .
i.e.
// Check input errors before inserting in database
if (empty($CName_err) && empty($Address_err) && empty($amount_err) && empty($Phone_err)) {
// Prepare an insert statement
$pdo = Database::connect();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "INSERT INTO database (CName, Address, Phone, Amount ,Ticket, Purpose) VALUES (?, ?, ?, ? ,?, ?)";
$q = $pdo->prepare($sql);
$q->execute(array($CName, $Address, $Phone, $amount ,$Ticket ,$Purpose));
Database::disconnect();
Hence, Any risks of being SQL injection attack in this case?
What should I do to improve my coding ?

Script gives database error on just one system

I am a VOIP administrator and I have script for updating directory database in Perl which was purchased from vendor before I was employed here.
The script is working fine on all of the servers except for one.
#!/usr/bin/perl
use lib "/opt/asterisk/lib/";
use DBI;
use Asterisk::config;
sub trim($);
# database information
$db="kesc";
$host="sip-ho.kesc.com.pk";
$userid="foo";
$passwd="bar";
$connectionInfo="dbi:mysql:$db;$host";
$hubname = "";
# make connection to database
$dbh = DBI->connect($connectionInfo,$userid,$passwd);
# Perl trim function to remove whitespace from the start and end of the string
sub trim($)
{
my $string = shift;
$string =~ s/^\s+//;
$string =~ s/\s+$//;
return $string;
}
my $rc = new Asterisk::config (file=>'/etc/asterisk/sip.conf',keep_resource_array=>0);
#list = $rc->fetch_sections_list();
$n = 1;
foreach (#list)
{
if ($_ ne "general") {
$entry = $rc->fetch_keys_hashref(section=>$_);
while ( my ($key, #value) = each(%$entry) )
{
if ($key eq "callerid") {
#vars = split('<',$value[0][0]);
$query = "insert into directory (extension,name,hub) values (" . trim($_) . ", '" . trim($vars[0]) . "', '$hubname') ON DUPLICATE KEY UPDATE hub='$hubname'";
$sth = $dbh->prepare($query);
$sth->execute();
}
}
}
$n++;
}
Now I get below mentioned error when executing it.
DBD::mysql::st execute failed: You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near 's Office', '') ON DUPLICATE KEY UPDATE hub=''' at line 1 at ./directory line 39.
I have also replaced it from other server with same MySQL version where it works perfectly.
Please guide me.
Thank you for the code. It is as I suspected; you really shouldn't insert values directly into an SQL statement
Change line 37, 38, and 39 to this and it should work for you
$query = 'INSERT INTO directory (extension, name, hub) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE hub = ?';
$sth = $dbh->prepare($query);
$sth->execute( trim($_), trim($vars[0]), $hubname, $hubname );
Beware that the same problem is likely to exist elsewhere in the code base, so it really should be completely reviewed

MySQL query 2 tables, 1 insert

I'm trying to pull data from 2 tables and then insert result into a third table. My code follows but only does 1 correct entry, and the rest are blank. There are 348 entries total. What am I missing here?
$dbh = DBI->connect(
"DBI:mysql:$mysqldatabase:$mysqlhostname",
"$mysqlusername",
"$mysqlpassword"
);
if(!$dbh) { die("Error: could not get DBI handle\n"); }
$sqlConnect = 1;
$SQL =<<SQL;
SELECT * FROM oscmax2.info2
SQL
$sth = $dbh->prepare($SQL);
if(!$sth) { die("Error: " . $dbh->errstr . "\n"); }
if(!$sth->execute) { die("Error4: " . $sth->errstr . "\n"); }
while (my #row = $sth->fetchrow_array) {
$products_id = $FORM{'product_id'};
$affiliate_id = $FORM{'affiliate_id'};
$demo = $FORM{'demo'};
}
if($sth->rows != 0) {
$total_rows = $sth->rows;
for ($counter = 0; $counter < $total_rows; $counter++) {
$SQL =<<SQL;
SELECT products_attributes_id FROM oscmax2.products_attributes
WHERE products_id = '$products_id'
SQL
$sth = $dbh->prepare($SQL);
if(!$sth) { die("Error: " . $dbh->errstr . "\n"); }
if(!$sth->execute) { die("Error: " . $sth->errstr . "\n");}
while (my #row = $sth->fetchrow_array) {
$products_attributes_id = $FORM2{'products_attributes_id'};
}
$SQL =<<SQL;
INSERT INTO oscmax2.products_attributes_download(
products_attributes_id, products_attributes_filename,
products_attributes_maxdays, products_attributes_maxcount
)
VALUES
('$products_attributes_id', '$affiliate_id/$demo', '7', '1')
SQL
$dbh->do($SQL) || die("Error5: " . $dbh->errstr . "\n");
}
}
$sth->finish();
if($sqlConnect) { $dbh->disconnect();
The blocks
while (my #row = $sth->fetchrow_array) {
$products_id = $FORM{'product_id'};
$affiliate_id = $FORM{'affiliate_id'};
$demo = $FORM{'demo'};
}
and
while (my #row = $sth->fetchrow_array) {
$products_attributes_id = $FORM2{'products_attributes_id'};
}
are wrong. You use #row to accept the data from each row of the result, but never use it. The database fetch won't affect %FORM and %FORM2 so you are just collecting the same data from them several times over
Update
At a guess you want something like this. Please study it and use the techniques rather than copying and testing it as it is, as I have no way of knowing what the structure of your database is and I have made several guesses
You should note the following points
There is no need to test the status of each DBI operation and die if it failed. By default the PrintError option is enabled and DBI will raise a warning if there are any errors. If you want your program to die instead, which is wise, then you can enable RaiseError and disable PrintError and DBI will do it all for you
There is no need to fetch all of the data from a table into memory (which I think is what you are trying to do with your while loops. You should fetch each row into an array and process the data row by row unless you have a reason to do otherwise
You should always prepare your statement and use placeholders. Then you can pass the actual parameters to the execute call and DBI will correctly quote them for you. Furthermore you can move all the prepare calls top the top of the program, making your logic much clearer to read
There is almost never a reason to call finish or disconnect. Perl will do the right thing for you when your database or statement handles go out of scope or your program ends
I have named the statement handles $select1 and $select2. These are very poor names, but I don't know the structure of your database so I couldn't write anything better. That shouyldn't stop you from improving them
I have had to guess at the columns returned by the first SELECT statement. If the three variables don't correspond to the first three elements of #row then you need to correct that
You should avoid using capital letters in Perl lexical identifiers. They are reserved for globals like package names, and nasty clashes of purpose can be caused if you don't abide by this rule
use strict;
use warnings;
my ($mysqldatabase, $mysqlhostname, $mysqlusername, $mysqlpassword) = qw/ dbase host user pass /;
my $dbh = DBI->connect(
"DBI:mysql:$mysqldatabase:$mysqlhostname",
"$mysqlusername",
"$mysqlpassword",
{RaiseError => 1, PrintError => 0}
) or die "Unable to connect to database: $DBI::errstr";
my $select1 = $dbh->prepare('SELECT * FROM oscmax2.info2');
my $select2 = $dbh->prepare(<<__END_SQL__);
SELECT products_attributes_id FROM oscmax2.products_attributes\
WHERE products_id = ?
__END_SQL__
my $insert = $dbh->prepare(<<__END_SQL__);
INSERT INTO oscmax2.products_attributes_download (
products_attributes_id,
products_attributes_filename,
products_attributes_maxdays,
products_attributes_maxcount
)
VALUES ( ?, ?, ?, ? )
__END_SQL__
$select1->execute;
while ( my #row = $select1->fetchrow_array ) {
my ($products_id, $affiliate_id, $demo) = #row;
$select2->execute($products_id);
while ( my #row = $select2->fetchrow_array ) {
my ($products_attributes_id) = #row;
$insert->execute($products_attributes_id, "$affiliate_id/$demo", 7, 1 );
}
}

Unable to insert a record into MySQL database using DBI

I am trying to insert a record into a MySQL database using Perl DBI. I am not getting any errors but the insert is not working. However, I am able to successfully fetch records from the database using DBI.
Here is the code that does the insert:
#!"C:\xampp\perl\bin\perl.exe"
use diagnostics;
use DBI;
use strict;
use warnings;
my $driver = "mysql";
my $database = "mysql";
my $dsn = "DBI:$driver:database=$database";
my $userid = "root";
my $password = "password";
my $buffer;
my #pairs;
my $pair;
my $name;
my $value;
my %FORM;
# Read in text
my $ENV;
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "GET")
{
$buffer = $ENV{'QUERY_STRING'};
}
# Split information into name/value pairs
#pairs = split(/&/, $buffer);
foreach $pair (#pairs)
{
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%(..)/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
my $first_name= $FORM{name};
my $address = $FORM{address};
my $city = $FORM{city};
my $occupation = $FORM{occupation};
my $age = $FORM{age};
my $dbh = DBI->connect("dbi:mysql:dbname=mysql", "root", "password",{ AutoCommit => 0,RaiseError => 1}, ) or die ("Couldn't connect to database: ") , $DBI::errstr;
# my $sth = $dbh->prepare("INSERT INTO persons
# (FirstName, LastName,Address,City)
# values
# ($first_name, $last_name,$address,$city)");
my $query = "insert into userrecords(Address,Age,City,Name,Occupation)
values (?, ?, ?, ?, ?) ";
my $statement = $dbh->prepare($query) or die ("Couldn't connect to database: "), $DBI::errstr;
$statement->execute($address,$age,$city,$name,$occupation) or die ("Couldn't connect to database: "), $DBI::errstr;
$dbh->disconnect();
my $URL = "http://.....:81/cgi-bin/showdata.cgi";
print "Location: $URL\n\n";
exit(0);
When I run my code in the Padre IDE, I get the following errors:
****Error*********
Useless use of a variable in void context at InsertRecord.cgi line 50 (#1)
(W void) You did something without a side effect in a context that does
nothing with the return value, such as a statement that doesn't return a
value from a block, or the left side of a scalar comma operator. Very
often this points not to stupidity on your part, but a failure of Perl
to parse your program the way you thought it would. For example, you'd
get this if you mixed up your C precedence with Python precedence and
said
$one, $two = 1, 2;
when you meant to say
($one, $two) = (1, 2);
Another common error is to use ordinary parentheses to construct a list
reference when you should be using square or curly brackets, for
example, if you say
$array = (1,2);
when you should have said
$array = [1,2];
The square brackets explicitly turn a list value into a scalar value,
while parentheses do not. So when a parenthesized list is evaluated in
a scalar context, the comma is treated like C's comma operator, which
throws away the left argument, which is not what you want. See
perlref for more on this.
This warning will not be issued for numerical constants equal to 0 or 1
since they are often used in statements like
1 while sub_with_side_effects();
String constants that would normally evaluate to 0 or 1 are warned
about.
Useless use of a variable in void context at InsertRecord.cgi line 59 (#1)
Useless use of a variable in void context at InsertRecord.cgi line 60 (#1)
Use of uninitialized value in transliteration (tr///) at InsertRecord.cgi line
23 (#2)
(W uninitialized) An undefined value was used as if it were already
defined. It was interpreted as a "" or a 0, but maybe it was a mistake.
To suppress this warning assign a defined value to your variables.
To help you figure out what was undefined, perl will try to tell you the
name of the variable (if any) that was undefined. In some cases it cannot
do this, so it also tells you what operation you used the undefined value
in. Note, however, that perl optimizes your program and the operation
displayed in the warning may not necessarily appear literally in your
program. For example, "that $foo" is usually optimized into "that "
. $foo, and the warning will refer to the concatenation (.) operator,
even though there is no . in your program.
Use of uninitialized value $ENV{"REQUEST_METHOD"} in string eq at
InsertRecord.cgi line 24 (#2)
Use of uninitialized value $buffer in split at InsertRecord.cgi line 29 (#2)
Location: http://.......:81/cgi-bin/showdata.cgi
Press any key to continue . . .
***********END***********************
What is the issue?
When I was editing your code so that it was more readable, I stumbled upon what I assume is the solution:
You are using $name when inserting into the database, but you use $first_name when getting the value $FORM{name}. So since you used $name above, it has the value of the last name used, whatever that might be. The relevant code snippets:
($name, $value) = split(/=/, $pair);
...
$FORM{$name} = $value;
...
my $first_name = $FORM{name};
...
$statement->execute($address,$age,$city,$name,$occupation)
# ^^^^^--- should be $first_name
Your problem would have been solved if you had used proper scope on your variables, namely something like this:
foreach my $pair (#pairs) {
my ($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%(..)/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
Then when you later would try to use $name, you would get the error
Global variable "$name" requires explicit package name ...
Which would alert you to your mistake and save you hours in debugging time. When you declare variables at the top of the script, instead of in the smallest possible scope, you effectively disable the protection that use strict 'vars' offers. So don't do that.
Also, you should probably use the CGI module instead of trying to handle it manually. It will make things easier, and safer. Don't forget to perform sanity checks on your data to prevent database injection attacks.
Your script when cleaned up and properly formatted looks like this.
What happens when you replace your code with this:
#!"C:\xampp\perl\bin\perl.exe"
use strict;
use warnings;
use diagnostics;
use DBI;
use CGI qw[param redirect];
my $driver = "mysql";
my $database = "mysql";
my $dsn = "DBI:$driver:database=$database";
my $userid = "root";
my $password = "password";
my $dbh = DBI->connect("dbi:mysql:dbname=mysql", "root", "password",
{ AutoCommit => 0,RaiseError => 1}, )
or die "Couldn't connect to database: ", $DBI::errstr;
my $query = "insert into userrecords(Address,Age,City,Name,Occupation)
values (?, ?, ?, ?, ?) ";
my $statement = $dbh->prepare($query)
or die "Couldn't connect to database: " , $DBI::errstr;
$statement->execute(param('address'), param('age'), param('city'),
param('name'), param('occupation'))
or die "Couldn't connect to database: " , $DBI::errstr;
$dbh->disconnect();
my $URL = "http://.....:81/cgi-bin/showdata.cgi";
print redirect($URL);
I've basically made two changes:
Use the CGI.pm module to handle the CGI interaction (getting the parameters and printing the redirection header).
Fixed your "void context" errors by removing the misplaced parentheses in all of your calls to die.
I'm made no substantive changes to the code, but at least we now have a clean version to go with.
Update: D'oh. It's obvious now the code is cleaned up a bit. If you have "Autocommit" turned off, then you need to commit your changes. Add $dbh->commit between the calls to execute() and disconnect().
The warning comes from this:
or die ("Couldn't connect to database: ") , $DBI::errstr;
The , $DBI::errstr is outside of the die and nothing is done with it, thus being in void context. You want something like this:
or die ("Couldn't connect to database: $DBI::errstr");
Also, your form handling code has some issues. If you're writing CGI scripts, you may as well use the CGI module. Here's a quick cleanup of your code:
#!"C:\xampp\perl\bin\perl.exe"
use diagnostics;
use CGI ':standard';
use DBI;
use strict;
use warnings;
my $driver = "mysql";
my $database = "mysql";
my $dsn = "DBI:$driver:database=$database";
my $userid = "root";
my $password = "password";
my $name = param('name');
my $address = param('address');
my $city = param('city');
my $occupation = param('occupation');
my $age = param('age');
my $dbh = DBI->connect( $dsn, $userid, $password,
{ AutoCommit => 1, RaiseError => 1 },
) or die("Couldn't connect to database: $DBI::errstr");
my $query = <<'END';
INSERT INTO userrecords(Address,Age,City,Name,Occupation)
VALUES ( ?, ?, ?, ?, ?)
END
my $statement = $dbh->prepare($query);
$statement->execute( $address, $age, $city, $name, $occupation );
$dbh->disconnect();
my $URL = "http://.....:81/cgi-bin/showdata.cgi";
print "Location: $URL\n\n";
Note that I've removed many or die statements because you already have RaiseError set to a true value.
For simplicity's sake, I've also (reluctantly) turned on AutoCommit.

escape a sql query error message using Perl

J have my inventory data inside a csv file.
I get an error message because I have a special character on my insert values, I don't want to deal with those values because they were tagged with special characters $" to not use them anymore for insert. I use the code bellow:
#!/usr/bin/perl
# PERL MODULES WE WILL BE USING
use DBI;
use DBD::mysql;
# HTTP HEADER
print "Content-type: text/html \n\n";
# CONFIG VARIABLES
$platform = "mysql";
$database = "store";
$host = "localhost";
$port = "3306";
$tablename = "inventory";
$user = "username";
$pw = "password";
# DATA SOURCE NAME
$dsn = "dbi:$platform:$database:$host:$port";
# PERL DBI CONNECT
$connect = DBI->connect($dsn, $user, $pw);
# PREPARE THE QUERY
$query = "INSERT INTO inventory (id, product, quantity) VALUES (DEFAULT, 'tomatoes$"', '4')";
$query_handle = $connect->prepare($query);
# EXECUTE THE QUERY
$query_handle->execute();
How can I skipe the error message and move to the next insert.
You'll have to escape the $ in your query, because right now it's a perl syntax error:
$sql = "INSERT .... 'tomatoes$"', '4')";
^----
that quote TERMINATES the sql string, as it's not a valid variable. Try
$sql = "INSERT .... 'tomatoes\$"', '4')";
^--
instead.
Handle your insert sentence avoiding sql-injection:
my $query = qq!INSERT INTO inventory (id, product, quantity) VALUES (?,?,?)!;
my $query_handle = $connect->prepare($query);
$query_handle->execute('DEFAULT', 'tomatoes$', '4');