Making a form submit a variable safely - mysql

while($info = mysql_fetch_array( $data )) {
// Print out the contents of the entry
Print "<li><b>Name:</b> ".$info['name'] . " <br />";
Print "<b>ID:</b> ".$info['ID']." <br />";
Print "<b>Age:</b> ".$info['age'] ." <br />";
Print "<b>Location:</b> ".$info['location'] ."<br /> ";
Print "<form action=delete.php method=POST><input name=ID value="safe(.$info['ID']." type=hidden><input type=submit name=submit value=Remove class=submit></form></li>";
}
Print "</ol>";
That's my code. I am, however, focusing on this line:
Print "<form action=delete.php method=POST><input name=ID value=".$info['ID']." type=hidden><input type=submit name=submit value=Remove class=submit></form></li>";
As has been pointed out in one of my previous posts, it's not safe against a SQL injection attack.
I've borrowed this function from another post:
function safe($value){
return mysql_real_escape_string($value);
}
Now, how in the heck would I make this part safe from an attack?
value=".$info['ID']."
Your continued support is greatly appreciated.

YES YES YES YOU NEED ESCAPING
(not to be too emphatic)
To protect this code from attack, you need to make sure that you have an authenticated user. Is the user that is viewing this page logged in? If so, where is their session stored?
Whatever logic you used to read the data you need to use when deleting the data.
// Read data in at top to prevent E_STRICT errors if user messes with Query string
$User_ID = $_SESSION['User_ID']; //assuming it was authenticated
$a_filter = (isset($_POST['a_filter']) ? $_POST['a_filter'] : '');
$query = "
SELECT
a, b, c
FROM
table
WHERE 1
AND a = " . mysql_real_escape_string($a_filter) . "
AND User_ID = " . intval($User_ID) . "
";
That query will ensure:
That the user in question can only see their records.
That it doesn't matter what hacking data they send, it will be escaped.
Now when you PRINT it to HTML, make sure you protect against XSS attacks:
<td><?= htmlspecialchars($data); ?></td>
or if you are in an attribute:
<input type="text" name="a_filter" value="<? htmlspecialchars($data, ENT_QUOTES); ?>" />
Now, when you delete the record, just make sure you apply the same safeguards...
// Read data in at top to prevent E_STRICT errors if user messes with Query string
$User_ID = $_SESSION['User_ID']; //assuming it was authenticated
$ID = (int) (isset($_POST['id']) ? $_POST['id'] : 0);
$query = "
DELETE FROM
table
WHERE 1
AND ID = " . intval($ID) . "
AND User_ID = " . intval($User_ID) . "
";
This post covered escaping SQL, escaping HTML, escaping HTML attributes, and ensuring that the queries are authorized.
Here is a short post on where escaping is important:
http://blog.gahooa.com/answers/what-kind-of-data-needs-to-be-escaped/

PHP Filter (a php 5.2.x feature) allows you to both validate, and sanitize data.
http://us.php.net/manual/en/filter.examples.php

simply check whether the server has this entry or not.
To do this:
$query = 'SELECT * FROM `table` WHERE `ID` = '.(int)$_POST['ID'];
Casting it to integer should be quite safe. If it is a string, it will be converted as well. See PHP's Type Juggling: http://www.php.net/manual/en/language.types.type-juggling.php
Also, by checking whether this entry exists or not, you are double securing that the data is valid.
So:
$result=mysql_query('DELETE FROM `savannah` WHERE `ID`='.(int)$_POST['ID']);
if(!$result){
die(mysql_error());
}

the security here should probably be multi-level - there's not a lot else you can do on that specific field.
do you have a unique session to identify the user (even if you don't yet, it isn't too difficult to do this simply)?
if yes, then you can use that to determine if that user is allowed to delete items in general or even that item in particular.
that should protect against simple injection attacks...
as for escaping integer values - I generally cast the incoming value as an integer, and that typically removes the problem of rogue characters and invalid inputs:
$safe['value'] = (int) $_POST['value'];
If the string is not-numeric, your safe value will be 0.
Edit: I should mention that simply relying on the fact that the database field is an integer is not secure in anyway, and without escaping or casting the inputs before you create the query, you are opening yourself to SQL injection.

Related

Placing PHP variable inside SQL

I am trying to send city from a page to another and then show items from database where city is the mentioned city but this code does not return any results. Please guide. I am sure everything else is fine with the code.
$city = $_POST["city"];
$sql = "SELECT id,full_name, email, password,full_address,city,age,contact_number,gender,education FROM users WHERE city=$city";
// strip tags from the input
$city = strip_tags($_POST["city"]);
// escape the input to prevent sql injection (assuming you are using mysqli() as your connection method...)
$city = mysqli_real_escape_string($city);
// your query does not work because you need to put strings inside single quotes
$sql = "SELECT id,full_name, email, password,full_address,city,age,contact_number,gender,education FROM users WHERE city='$city'";
Actually, you're not even executing the request on your mysql server, but if you are using PDO (what you SHOULD do), just do something like this:
<?php
$bdd = new PDO(etc);
$req = $bdd->prepare("SELECT id,full_name, email, password,full_address,city,age,contact_number,gender,education FROM users WHERE city=?");
$req->execute(array($_POST['city']));
print_r($req->fetchAll());
?>
And here you go, $req->fetchAll() will return you an array with each element returned by your request, and the best part is that prepare will prevent you from every SQLi
Edit: You can use short syntax for array [$_POST['city']] or old and complete syntax: array($_POST['city'])

Resource Link Indetifier using PDO?

(new to PDO and limited experience with DB's in general) :)
I'm switching over to use PDO in a project..
and even though the data was getting dumped to the DB..there was a strange 'Warning'.. that displayed merging of table I was trying to use..(and another I wasnt)... SELECT()..etc even though I was trying to INSERT()..etc..
using the NON-PDO approach worked fine however (which seems a bit odd)..
example:
include("../_include/db_forms.php");
mysql_query("INSERT INTO $tablename (timestamp, fullname, email, formcode, ipaddress, alldata) VALUES ('" . date("YmdHis") . "','" . mysql_real_escape_string($_POST['name']) . "','" . mysql_real_escape_string($_POST['email']) . "','" . $formcode . "','" . $_SERVER['REMOTE_ADDR'] . "','" . mysql_real_escape_string($allData) . "')");
^ no warning/data dumps to DB just fine
after searching/asking around a bit... it looked like another open database connection for the CMS (for other aspects of the page/header/footer include files..etc) is being used for the other queries, (or vice versa).. and this is causing the warning/problem.
I was told adding a resource link identifier would solve this...
now I understand what it is.., and why it is used (multiple DB's.. this is a reference to what one should be used/opened..etc.)
However.. I'm not exactly sure HOW to implement one.. especially using PDO approach.
here is my PDO approach/code:
require_once("../_include/db_pdo_fulllog.php"); //just a .php file with DEFINE()'s for DB connection details
// new DB routine PDO approach //
$sql = "INSERT INTO $tablename (timestamp, fullname, email, formcode, ipaddress, alldata) VALUES (?,?,?,?,?,?)";
try {
$conn = new PDO("mysql:host=" . DBSERVER . ";dbname=" . DBNAME, DBLOGIN, DBPASSWORD);
$stmt = $conn->prepare($sql);
//$stmt = $conn->prepare("INSERT INTO fulllog(timestamp, fullname, email, formcode, ipaddress, alldata) VALUES (?,?,?,?,?,?)");
//timestamp
$ts = date('Y-m-d H:i:s');
$stmt->bindParam(1, $ts);
//name
$stmt->bindParam(2, $_POST['name']);
//email
$stmt->bindParam(3, $_POST['email']);
//formcode
$stmt->bindParam(4, $formcode);
//ip address
$stmt->bindParam(5, $_SERVER['REMOTE_ADDR']);
//all data
$allData = $_POST['session_title'] .' '. $_POST['session_type'] .' '. $_POST['description'] .' '. $_POST['goals'] .' ' . $_POST['faculty'] .' '. $_POST['chapter'];
$stmt->bindParam(6, $allData);
//execute built statement
$stmt->execute();
}
catch(PDOException $e) {
echo $e->getMessage();
die;
}
How can I add a resource link identifier to this...to stop the warning/error of having the multiple databases in use?
Thanks.
edit:
(since its been requested)
the error/warning message:
Warning: mysql_fetch_row() expects parameter 1 to be resource, boolean given in /usr/www/users/xxxxorg/xxxx.org/admincms/_includes/class.now_cms.php on line 126
Err (Table 'xxxx_forms.fulllogContent' doesn't exist) in query: SELECT n.NodeName, n.ContentId, n.ParentId, n.NodeOrder, n.ParentNode, n.ContentTitle, n.ContentType, n.Status, n.ShowOnNav, n.ShowNav, n.ShowOnSitemap, n.ForceSSL, n.TargetWindow, n.ResourceUrl, n.ShortTitle, n.Keywords, n.Description, n.CCount, n.COrderField, n.COrder, COALESCE(t.TemplateBody, dt.TemplateBody) as Template, COALESCE(ct.TemplateBody, dt.TemplateBody) as ChildTemplate, n.UserGroups, n.Comments, n.Sticky, IF(md.MetaId IS NULL, 0, 1) as HasMetaData FROM fulllogContent n LEFT JOIN fulllogTemplate t ON n.Template=t.TemplateId LEFT JOIN fulllogTemplate ct ON n.ChildTemplate=ct.TemplateId LEFT JOIN fulllogTemplate dt ON dt.TemplateId=dt.TemplateId AND dt.Status=2 LEFT JOIN fulllogSection s ON n.SectionId=s.SectionId LEFT JOIN fulllogMetaDefine md ON s.SectionId=md.SectionId WHERE n.Status=1 AND n.PubDate <= NOW() AND (n.ExpDate >= NOW() OR n.ExpDate = '0000-00-00 00:00:00') ORDER BY CONCAT(IF(n.ParentId=0, '', '/'), n.ParentNode), n.NodeOrder
Again.. my table is -not- 'xxxx_forms.fulllogContent' but in fact: 'xxxx_forms.fulllog'
and most of the other stuff doesnt even apply to my table/DB either.. (nor am I doing a SELECT.. I'm doing an INSERT)..
SO it (somehow) still has the CMS database open/in use.. and is getting merged/mixed up with the one I want.
To re-iterate.. the data IS being dumped to the DB... but the page doesnt refresh/resolve and shows this warning/error..
PDO object IS such a "link identifier" itself - so, you don't need anything else.
Therefore, your problem is not connected to PDO.
To get help on a particular error message you have to post this particular message whole and exact, instead of describing it in vague terms.
Solution to fix this seemed to be changing the define() constant names to be unique/different from the constant names the other connection was using. Thanks for the feedback

Retrieving email addresses with special characters using MySQL query

I am fairly new to PHP and Mysql. The question I am going to ask will be begging for someone to tell me to use prepared statements so first of all let me say I am learning this, but not quite there yet. I have a query that looks to see if an email address is in the database. The email addresses may contain unusual characters like - , / | "" etc etc. I can't seem to retrieve them - here is my code (the repeatemail is coming from a form). Works perfectly with email addresses without this characters.
$checkemail = $_POST['repeatemail'];
$checkemail = mysqli_real_escape_string($con, $checkemail);
//Perform database to see if email exists
$query = "SELECT email FROM scorers WHERE email = '{$checkemail}'";
$result = mysqli_query($con, $query);
$row = mysqli_fetch_row($result);
if ($row[0] == $checkemail){
echo "found";
} else {
echo "not found";
}
As it stands I have wondered if the escape string is stripping the unusual characters and therefore once its queried it been altered but that doesn't seem to be the case. Also, I have no problem entering addresses like simon.o'malley#nhs.uk but just can't check them with the above code. Looked up many explanations regarding UTF etc but its a bit above my head at this point. Could someone give me a solution to this....how do I alter the code above so it will pick out these funky email addresses? Many thanks
Got it...this works fine but if any of you have major concerns let me know. Its the magic quotes issue that seemed to be the only problem. All other characters seem fine
$checkemail = $_POST['repeatemail'];
$check_email_no_slashes = $checkemail;
$checkemail = mysqli_real_escape_string($con, $checkemail);
echo $check_email_no_slashes . "</br>";
//Perform database to see if email exists
$query = "SELECT email FROM scorers WHERE email = '{$checkemail}'";
$result = mysqli_query($con, $query);
$row = mysqli_fetch_row($result);
if ($row[0] == $check_email_no_slashes){ etc etc etc .......}
Thanks for your input Tim.
You really need to use prepared statements. If you don't, you're asking for SQL injection issues (see http://en.wikipedia.org/wiki/SQL_injection). For example, I could send you an email address that would delete all the rows in your table.
Prepared statements aren't hard; here's an example:
$stmt = $mysqli->prepare("SELECT email FROM scorers WHERE email = ?")
// use the string in $checkemail in place of the ?
$stmt->bind_param("s", $checkemail);
// run the query
$stmt->execute();
// put the result into $email
$stmt->bind_result($email);
if ($stmt->fetch()) {
// found a matching email; do something about it
}
$stmt->close();
You can read more about prepared statements in the PHP docs: http://php.net/manual/en/mysqli.prepare.php

Iterate through MYSQL database and replace data in every record in one column

I have this VERY inefficient way of updating the phone numbers in my database after cleaning them of all non-digits.
$san_phone = mysql_query('SELECT * FROM table');
while ($row = mysql_fetch_array($san_phone)) {
$row['phone_clean'] = preg_replace('#[^\d]#', '', $row['phone']);
echo $row['id'] . ' - ' . $row['phone_clean'] . '<br>';
mysql_query("UPDATE table SET phone = " . $row['phone_clean'] . " WHERE id = " . $row['id']);
}
That update part of the loop is causing me to timeout after only about 400 of my 2,400 records. It's obvious I'm doing something wrong so be gentle when schooling me. ;)
First off, stop using mysql_ functions as they are being deprecated. Use mysqli_ or PDO functions instead.
The method you are using to UPDATE your records is inefficient. You should instead create a temporary table, INSERT the new records in a single query, and finally run an UPDATE query to replace the data.
You can start out with this:
$san_phone = mysql_query('SELECT id, phone FROM table');
$insertArray = array();
while ($row = mysql_fetch_array($san_phone)) {
$phone_clean = preg_replace('#[^\d]#', '', $row['phone']);
echo $row['id'] . ' - ' . $row['phone_clean'] . '<br>';
$insertArray[] = "(" . $row[id] . ", '" . $phone_clean . "')";
}
$insertQuery = "INSERT INTO tempTable (id, phone) VALUES ";
$insertQuery = implode(", ", $insertArray);
mysql_query($insertQuery);
I've made a quick demo to illustrate this process. t1 is your original table, and t2 is the temporary table that contains the data to replace.
See it in action
You could use something like this user-defined function:
http://www.mysqludf.org/lib_mysqludf_preg/index.php#PREG_REPLACE_SECTION
or https://launchpad.net/mysql-udf-regexp
And rewrite your query to:
UPDATE table
SET phone = PREG_REPLACE('#[^\d]#', '', phone);
Well, multiple calls to your database incurs a speed hit, and (in my experience), it's a painful one. Even two trips to the database can lead to a noticeable delay over one.
To get around this, you want to minimize the calls to your database, which means doing as much in one call as possible. To this end, try rewriting this as a single SQL update where your replacement logic is in SQL itself. That would mean only one trip to the database and a massive speed improvement.
That's easier said than done, since last I checked, MySQL didn't have a regular expression string replacement function. You could try a work-around, or see about some of the UDF's. Another answer provided a link for one. I recommend looking into that.

Perl Mysql - How to search for specific data based on one "main" match?

User Form Input - City
User Form Input - Venue
User Form Input - Cover
User Form Input - Time
User Form Input - Date
User Form Input - Number1
User Form Input - Number2
(if any are blank they are coverted to '*' on the way in. But could be whatever works.)
my $grabgig = $hookup->prepare(qq{SELECT `VenueNumber`,`Venue`,`CoverCharge`,`SetLength`,`City`,`Owner`,`Date`,`Time`,`Image1`,`Number`
FROM `gigs`
WHERE VenueNumber > ? AND `City` = ? AND `Venue` = ? AND `CoverCharge` = ?
AND Date = ? AND `Number` > ? AND `Number` < ?
AND `Time` LIKE ? LIMIT ?,?});
##########################################
$grabgig->execute('100',$city,$venue,$cover,'*',$number1,$number2,?,'0','6')
or die "Did not execute";
That is a basic example above.
I want to be able to return results based on the City Input.
If more input is present, then narrow down results accordingly.
But the query returns nothing if fields are empty (*).
I tried wildcards and so on then, I experimented with LIKE and NOT LIKE.
This seemingly simple search is driving me nuts.
Can someone help this newbie?
OK, I'm pretty unsure what you mean, BUT, my best undererstanding of what you're trying to do is to query like you do now BUT if a particular field is not populated in the form, to avoid adding that field to the where clause; as opposed to current query which instead does and myField="*".
Correct?
If that's so, you need to build your query, and replacement list, in pieces:
my $sql = qq{SELECT MY_FIELD_LIST_TOO_LAZY_TO_TYPE FROM `gigs` WHERE 2=2};
my #replacement_values = (); # These go into execute() instead of "?"s
if ($city ne "*") {
$sql .= qq[AND city = ?];
push #replacement_values, $city;
}
if ($number1 ne "*") {
$sql .= qq[AND number > ?];
push #replacement_values, $number1;
}
# ... more values processed the same way
my $grabgig = $hookup->prepare($sql);
$grabgig->execute(#replacement_values) or die "Did not execute";
If you want to do it more intelligently (i.e. to generalize), you will have the form fields in a hash; have a config hash mapping the form field name to the DB column name and the operator, and instead do the above as:
my %fields = (
city => ["city" , "="]
,number1 => ["number", ">"]
,number2 => ["number", "<"]
);
my $sql = qq{SELECT MY_FIELD_LIST_TOO_LAZY_TO_TYPE FROM `gigs` WHERE 2=2};
my #replacement_values = (); # These go into execute() instead of "?"s
foreach my $field (keys %form_data) {
next unless exists $fields{$field};
if ($form_data{$field} ne "*") {
$sql .= qq[ AND $fields{$field}->[0] $fields{$field}->[1] ?];
push #replacement_values, $form_data{$field};
}
}
my $grabgig = $hookup->prepare($sql);
$grabgig->execute(#replacement_values) or die "Did not execute";
I am assuming that you want to construct a query where only a few input parameters have valid values and the rest are undefined. If that is indeed what you want, here is what you could do: Construct the query dynamically. Here are the steps you could take assuming you are using CGI.pm and assuming that the where clause is just a series of "this = that" - In your case you have different operators - but the idea is the same.
First construct a "where" string from the CGI query parameter (Sorry untested code):
my $qrystr = '';
foreach ($query->param) {
if (my $val = $query->param($_)) {
$qrystr .= "where $_ = " . $dbh->quote($val) . ' and ';
}
}
$qrystr .= "where 1 = 1";
Now you can just prepare and execute the query : "select * from table $qrystr"
If you want automatic quoting you will have to use bind parameters which is an easy extension of the code above
Update There was a missing "where" in the last true clause "1 = 1" - Sorry, added it now
Sorry, the formatting bar was not appearing so, I rebooted. Now I cannot edit my question or comment.
What I am trying to do is provide a search for the users.
They select a city from a dropdown then some optional data can be entered / selected to narrow the results.
The optional data May or May Not be present in the table, could be a blank field.
I would like the results to show based on the selected criteria of the search in that City.
So, WHERE selected "input city" = "tables city column" look for the other options (ignore that particular criteria if field is empty) and return any matches that exist for that city.
I am then pushing into array in a While for output display.
I guess it would be like a car query. Select make where doors = 2 and color = red and engine = hamsterwheel but, the color field may be empty in the database..