What is the best way to protect Mysql Delete statements? - php

I thought you guys would know the best way to do this:
When I delete an order ($prodId) from the ORDERS table, this script then goes and deletes all the items-ordered lines from the ORDERED_ITEMS table, which houses all the items ordered from every order in the system.
Is there a best practice to ensure that what I want deleted is deleted and only that? I'm worried about something going wrong/injected/mistyped with/into the script and accidentally deleting all the ordered item lines for all orders by mistake.
This is how far I got.
$delete_prod_items = mysqli_real_escape_string($con,$_REQUEST['prodId']);
if (is_numeric($delete_prod_items)){
$sql3 = "DELETE from proteus.ordered_items where order_id = $orderId";
mysqli_query($con,$sql3) or die('DELETE Order $orderId from the Ordered Items table failed: ' . mysqli_error($con).'<br>');
}
This script is POSTed into by my form.
$orderID is the order number that the script uses to identify which ITEM rows should be deleted
$delete_prod_item is the escaped $prodID value. I was trying to be super cautious. perhaps I don't need this.
Am I missing anything?

Don't know why anyone is mentioning it, but the best way to really protect any statement is using PreparedStatements:
$delete_prod_items = mysqli_real_escape_string($con,$_REQUEST['prodId']);
$mysqli = new mysqli("localhost", "localuser", "password", "database");
if ($mysqli->connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli->connect_error;
}
if (!($stmt = $mysqli->prepare("DELETE FROM `proteus`.`ordered_items` WHERE `order_id` = ?"))) { //whatever query you want
echo "Prepare failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
$stmt->bind_param("s", $delete_prod_items);
$stmt->execute();
$stmt->close();
Also take after what #zerkms mentioned and use POST requests for your information.

First things first you need to take care of the sql injections. The link will give you an idea.
Secondly you could use javascript to get a pop-up which asks the user a confirmation before deletion.
Next, to avoid the unintentional deletion of more than one row is to include LIMIT 1 to your query.
N.B. You could also limit priveleges by creating a different user (username) to access the mysql database use it in your mysql_connect('host', 'username', 'password', 'database') function . If you are displaying something really important you may consider not giving deletion rights.

Escaping data is good for preventing SQL Injection
It's better to limit the request with $_POST instead of $_REQUEST
If this page is for admin only, then it's fine since only you have access, however, if users have access to it, therefore it's better to apply some condition to the request before proceeding to the deletion (example, verify that they are deleting something related to their accounts only)

As a first order of business, do the following...
Limit the request method to POST or even better, DELETE if you can get PHP to handle it.
Use prepared statements with bound parameters to avoid SQL injection vulnerabilities.
The main issue you will face is unauthorised requests. The best solution to this is to use a CSRF token.

Related

Best way to approach sending external php a variable

I was wondering if some one could direct me on the right path to take because every way I have tried has failed or really broken my code. To keep it simple I have page with a dynamically created select box populated with peoples names from a mySQL database its element id is 'insert'. This page also holds the php query
my query on the database works if I hard code a name in but I want to pass it as a variable from the select box. I can't seem to get it to post my variable and return me an id.
heres my query
<?php
function getElementById($id) {
$xpath = new DOMXPath(NEW domDocument);
return $xpath - > query("//*[#id='$id']") - > item(0);
}
$insertName = getElementById('insert');
printf($insertName);
$con = mysqli_connect("localhost", "root", "", "karaoke");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: ".mysqli_connect_error();
}
$sql = "Select id FROM queue where singer = '$insertName'";
$result = mysqli_query($con, $sql) or die("Bad SQL: $sql");
while ($row = mysqli_fetch_assoc($result)) {
$insertAt = ("{$row['id']}");
printf($insertName);
printf($insertAt);
};
?>
whats the best way to get my variable sent to the script and then return me the answer.
thanks
You can use either the POST or GET form methods to send data from your HTML form to your PHP script. In the form element, you will want to set the action to your PHP script like so: <form action = 'your_php_file.php' method = 'GET or POST'>. This means that when the form is submitted, you can get the data from this PHP file. Then, in your PHP, you will want to use the global variable for either POST or GET (depending on which you have used for the form method) to get the value from the select box. Using this method means you can replace your GetById function and assign the value from the form to the $insertName variable using the superglobals.
Another problem in your code is that you use your PHP variables in your SQL query. This means that your code is open to an SQL injection which could lead to problems such as people getting all of the database info (which is bad for a database storing poorly encrypted/hashed passwords, or even storing them in plain text)or could even lead to your database being deleted. To avoid this, you should use prepared statements and parameters whereby the statement is sent first without the variable and the variable is bound after.
Also, take a look at the links above about POST and GET and also about the PHP global variables which will allow you to get the data from your HTML form. Also, here are some links which explain prepared statements and parameters so that you can write more secure PHP code:
Mysqli prepare statement used to prepare the statement. The use of question marks are as placeholders as you later bind your variables to the query.
Mysqli Bind Param used to add in the variable to the SQL statement after the statement has been prepared which prevents SQL injection.
That's all for now, but be sure to ask any questions you may have and I will try my best to answer them all.
EDIT
ADDED CODE - hopefully will demonstrate what you were after, there are some small changes that may need to be made. There may be some extra code needed to fit in with any other code you have, but this should demonstrate the principle of POST and prepared statements with parameters. Written in OOP as opposed to your procedural as I find it cleaner and easier (personal opinion). If there are any problems integrating this be sure to tell me about any errors or issues/further questions. I too am fairly new to PHP.
<?php
$insertName = $_POST['insert']; // Get the value of the select box which will need to have the attribute 'name = "insert"' by POST
printf($insertName);
$con = new mysqli("localhost", "root", "", "karaoke");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: ".mysqli_connect_error();
}
$sql = "Select id FROM queue where singer = ?";
$stmt = $con->prepare($sql);
$stmt->bind_param("s", $insertName); //Binds the string insertName to the question mark in the query
$stmt->execute();
while ($row = $stmt->fetch_assoc()) { // Left as was because syntax is different from PDO which I use. Therefore, I am assuming this part is correct.
$insertAt = ("{$row['id']}");
printf($insertName);
printf($insertAt);
};
?>

How to use same query to insert data

I am new at programming.
I am trying to create a simple guestbok.
i have one index page where you can register a firstname, lastname and email.
And if you click on one name you redirect to a new page with id.
How can i now insert text to this ID with the same codeblock using the ID.
My code looks like this.
<?php
require('dbconfig.php');
try {
$conn = new PDO("mysql:host=$servername;dbname=projektone", $username, $password);
//Set PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Insert to database
$sql = "INSERT INTO user (firstname, lastname, email)
VALUE ('".$_POST["first_name"]."','".$_POST["last_name"]."','".$_POST["email"]."')";
$sql = "INSERT INTO user (guestbok)
VALUE ('".$_POST["guestbok"]."')";
$conn->query($sql);
}
catch(PDOException $e)
{
echo $e->getMessage();
}
header('Location: /');
?>
Thanks in advance
/Daniel
Joining up raw bits of text and passing them on to your database to process is not a good idea. It opens up your system to SQL injection. While it's unlikely that someone could compromise your site when only INSERT statements are exposed in this way, it does mean that:
anyone with an apostrophe in their name will break the logic of the request
you are exposing a method by which someone can carry out a stored XSS attack by submitting javascript to your guestbook
Regarding the SQL Injection problem, there are 2 methods to protect your system - one is to transform the data in which a way that it cannot break the SQL string it is added to (e.g. using mysqli_real_escape_string()) but the recommended approach when using PDO to mediate your code's interaction with the DBMS is to use variable binding. Here you compose your SQL command with placeholders for the data and substitute them at run time.
If your ID is generated from a mysql auto insert id, then you can read the value from $conn->lastinsertid
$stmt=$conn->prepare("INSERT INTO user (firstname, lastname, email)
VALUES (:fnm,:lnm,:eml)");
$stmt->execute(array(
':fnm' => $_POST["first_name"],
':lnm' => $_POST["last_name"],
':eml' => $_POST["email"]));
$id=$conn->lastinsertid();
Your next problem is how to communicate this securely to the page where the user submits their guestbook comment (in your example code you try to do both operations in the same page).
Sending it in a round trip to the browser, as a cookie or as form variable means that it could be tampered with. There are esoteric stateless solutions where you can do this but with the data encrypted or cryptographically signed, however the simplest solution is to use sessions - add session_start() at the top of all your pages and any data you want available across requests can be stored in the $_SESSION superglobal.
(there are security issues relating to sessions as well)
When you receive the POST containing the guestbook data, then you should use an UPDATE user SET guestbook=:gstbk WHERE id=:id_from_session (or you could INSERT it into a seperate table with id as a foreign key)
Lastly, when you output the message the person left in your guestbook, make sure you protect the browser from any nasties in there:
print htmlentities($guestbook);
Ok, probably I managed to get what you need. Put the following two lines in your dbconfig.php:
$conn = new PDO("mysql:host=$servername;dbname=projektone", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
and then require it wherever you need a database connection:
file one:
require('dbconfig.php');
$sql = "sql 1";
$conn->query($sql);
then in another file
require('dbconfig.php');
$sql = "sql 2";
$conn->query($sql);

update two table in single query

i have two table in sql the 1st table is for account while the 2nd table is for testimonial . i am trying to update the two tables in single query. The update is successful if the account already have a testimonial but fails to update if the account has no testimonial yet .How can i fix this heres my code for the update ....
if(!$update=mysql_query(
"UPDATE
tblapplicant,
tbltestimonial
SET
tblapplicant.ImagePath='".$name."',
tbltestimonial.pic = '".$name."'
WHERE
tblapplicant.appid=tbltestimonial.appid"
)
)
1) You're working with a database, it defeats the purpose to use the same data being inserted into two different tables.
2) One gentleman also mentioned stop using MySQL... heres some reference code for you. Assuming you're using php.
3) If you want to use a single query to update 2 tables with the same info against recommendation. Use a stored procedure to update them both.
4) At which point are these account's interconnected in this query? I'm somehow intrigued if this system is in beta or testing?
With your "Where" conditions without matching a specific record, this will update every record that has a matching ID. This is highly not recommended until you add further conditions like username = .... or a condition that's specific to someone or a specific set of rows.
**I strongly advise you post the tables you're working with and what results you want achieve for the best advise. **
Can't really give a good consultation with you playing the whole overview close to the chest. Using this plain-Jane without further detail on what you're asking for is at your own risk my friend.
include/dbconnect.php optional recommended update
<?php
if (isset($mysqli)){
unset($mysqli);
}
define("HOST", "yo.ur.ip.addr"); // The host you want to connect to.
define("USER", "myfunctionalaccount"); // The database username.
define("PASSWORD", "superdoopersecurepassword!"); // The database password.
define("DATABASE", "thegoods");
$mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
if ( $mysqli->connect_error ) {
die('Connect Error: ' . $mysqli->connect_error);
}
?>
functions.php <-- shouldn't be called functions if its going to be your form response
<?php
// SHOULD BE SOME MASSIVE LOGIC UP HERE FOR FORM DATA DECISIONING
include_once "include/dbconnect.php";
$name = addslashes($_FILES['image']['name']);
$image = mysql_real_escape_string(addslashes(file_get_contents($_FILES['image']['tmp_name'])));
if ($stmt = $mysqli->prepare("CALL UpdateTestimonials(?,?,?)"){
$stmt->bind_param($name, $image, $userid);
$stmt->execute();
// optional to show affected rows
$stmt->affected_rows
//
// use if you want to return values from DB
// $stmt->bind_result($result);
// $stmt->fetch;
}
$stmt->close
?>
MySQL build a stored procedure - fyi; definer is optional. Definer will allow you to run a query that only elevated privileges can access due to the safety of such a query. You can use create procedure w/o the definer parameter. dT is just an abbreviation for datatype. You would put varchar or int... etc..
use 'database';
DROP procedure if exists 'UpdateTestimonials';
DELIMITER $$
use 'UpdateTestimonials' $$
CREATE DEFINER='user'#'HOSTNAME/LOCALHOST/%' PROCEDURE 'mynewprocedure' (IN varINPUT varchar, IN varIMG blob, IN varAppID int)
BEGIN
UPDATE tblapplicant
SET imagepath = varINPUT,
pic = LOAD_FILE(varIMG)
WHERE appid = varAppID
END $$
DELIMITER;
Use LEFT JOIN:
if (!$update = mysql_query(
"UPDATE
tblapplicant
LEFT JOIN tbltestimonial ON tblapplicant.appid = tbltestimonial.appid
SET
tblapplicant.ImagePath = '" . $name . "',
tbltestimonial.pic = '" . $name . "'"
)
)
Also, if you need some additional filters for tbltestimonial, add them into LEFT JOIN condition
You could try with a transaction. BTW also please use prepared statements to prevent SQL Injection attacks.
<?php
// prefer mysqli over mysql. It is the more modern library.
$db = new mysqli("example.com", "user", "password", "database");
$db->autocommit(false); // begin a new transaction
// prepare statements
$update_applicant =
$db->prepare("UPDATE tblapplicant
SET tblapplicant.ImagePath = ?"));
$update_applicant->bind_param("s", $name));
$update_applicant->execute();
$update_testimonial =
$db->prepare("UPDATE tbltestimonial
SET tbltestimonial.pic = ?"));
$update_testimonial->bind_param("s", $name))
$update_testimonial->execute();
$db->commit(); // finish the whole transaction as successful,
// when everything has succeeded.
?>
Of course that would not create any testimonials, that don't exist. It just updates those, that do. When you want to insert new entries in tbltestimonial, do so explicitly with an INSERT statement inside the transaction.
MySQL does not fully support transactions. The tables have to use a table type, that can handle them, e.g. innodb. When that is the case, the transaction will make sure, that everyone else either sees all changes from the transactions, or none.
In many cases transactions allow you to perform a group of simple steps, that otherwise would need complex single queries or would not be possible without transactions at all.
Alternative Solution
Another approach of course would be an update-trigger. Create a trigger in your database, that fires whenever e.g. tblapplicant is updated and updates tbltestimonial accordingly. Then you don't have to care about that in your application code.

PHP - Reducing an independent record stock value by 1

I'm currently doing a school project and I'm using dreamweaver along with a backend database using phpMyAdmin.
Now, what i need to do is, when I click the button, it will reduce the stock column value in the "products" table by 1.
However there are different products in the table. Shown below:
http://i.stack.imgur.com/vLZXQ.png
So lets say, A user is on the game page for "Destiny" and clicks on the Buy now button, how can i make it reduce the stock level by one, but only for the Destiny record and not for the Fifa 15 column. So Destiny stock becomes 49, but Fifa stays 50. Will i just need to make each button have a different script or?
Currently, I made a button in the page, which links to an action script, but im not sure what sort of code i will be using.
Thank you
xNeyte is giving you some good advice, but it comes across to me that you - Xrin - are completely new to programming database contents with PHP or similar?
So some step by steps:
MYSQL databases should be connected with one of two types of connection - PDO and MySQLi_ . MySQL databases will also always work using the native MySQL but as xNeyte already mentioned - this is deprecated and highly discouraged .
So what you have is you pass your information to the PHP page, so your list of games is on index.php and your working page that will update the number of games ordered would be update.php, in this example.
The Index.php file passes via anchor link and $_GET values (although I highly recommend using a php FORM and $_POST as a better alternative), to the update.php page, which needs to do the following things (in roughly this order) to work:
Update.php
Load a valid database login connection so that the page can communicate with the database
Take the values passed from the original page and check that they are valid.
establish a connection with the database and adjust the values as required.
establish the update above worked and then give the user some feedback
So, step by step we'll go through these parts:
I am going to be a pain and use MySQLi rather than PDO - xNeyte used PDO syntax in his answer to you and that is fully correct and various better than MySQLi, for the sake of clarity and your knowledge of MySQL native, it may be easier to see/understand what's going on with MySQLi.
Part 1:
Connection to the database.
This should be done with Object Orientated - Classes,
class database {
private $dbUser = "";
private $dbPass = ""; //populate these with your values
private $dbName = "";
public $dbLink;
public function __construct() {
$this->dbLink = new mysqli("localhost", $this->dbUser, $this->dbPass, $this->dbName);
}
if (mysqli_connect_errno()) {
exit('Connect failed: '. mysqli_connect_error());
}
if ( ! $this->dbLink )
{
die("Connection Error (" . mysqli_connect_errno() . ") "
. mysqli_connect_error());
mysqli_close($this->dbLink);
}
else
{
$this->dbLink->set_charset("UTF-8");
}
return true;
} //end __construct
} //end class
The whole of the above code block should be in the database.php referenced by xNeyte - this is the class that you call to interact with the database.
So using the above code in the database.php object, you need to call the database object at the top of your code, and then you need to generate an instance of your class:
include "database.php"; ////include file
$dataBase = new database(); ///create new instance of class.
Now When you write $dataBase->dbLink this is a connection to the database. If you do not know your database connection use the details PHPMyAdmin uses, it carries out its tasks in exactly the same way.
Sooo
Part 2:
That is that your database connection is established - now you need to run the update: First off you need to check that the value given is valid:
if (is_numeric($_GET['id']) && $_GET['id'] >0 ){
$id = (int)$_GET['id'];
}
This is simple code to check the value passed from the link is a integer number. Never trust user input.
It is also a good idea never to directly plug in GET and POST values into your SQL statements. Hence I've copied the value across to $id
Part 3:
$sql = "UPDATE <TABLE> SET STOCK = STOCK-1 WHERE Product_ID = ? LIMIT 1";
The table name is your table name, the LIMIT 1 simply ensures this only works on one row, so it will not effect too many stocked games.
That above is the SQL but how to make that work in PHP:
first off, the statement needs to be prepared, then once prepared, the value(s) are plugged into the ? parts (this is MySQLi syntax, PDO has the more useful :name syntax).
So:
include "database.php"; ////include file
$dataBase = new database(); ///create new instance of class.
if (is_numeric($_GET['id']) && $_GET['id'] >0 ){
$id = (int)$_GET['id'];
$sql = "UPDATE <TABLE> SET STOCK = STOCK-1 WHERE Product_id = ? LIMIT 1";
$update = $dataBase->dbLink->prepare($sql);
$update->bind_param("i",$id);
$update->execute();
$counter = $update->affected_rows;
$update->close();
//////gap for later work, see below:
}
else
{
print "Sorry nothing to update";
}
There's probably quite a lot going on here, first off the bind_param method sets the values to plug into the SQL query, replacing the ? with the value of $id. The i indicates it is meant to be an Integer value. Please see http://php.net/manual/en/mysqli-stmt.bind-param.php
The $counter value simply gets a return of the number of affected rows and then something like this can be inserted:
if ($counter > 0 ){
print "Thank you for your order. Stock has been reduced accordingly.";
}
else {
print "Sorry we could not stock your order.";
}
Part 4
And finally if you wish you can then just output the print messages or I tend to put the messages into a SESSION, and then redirect the PHP page back.
I hope this has helped a bit. I would highly recommend if you're not used to the database interactions in this way then either use PDO or MySQLi but do not combine the two, that will cause all sorts of syntax faults. Using MySQLi means that everything you know MySQL can do, is done better with the addition of the letter "i" in the function call. It is also very good for referencing the PHP.net Manual which has an excellent clear detailed examples of how to use each PHP function.
The best is to set a link on each button with the ID of your game (1 for destiny, 2 for Fifa15).
Then your script which the user will launch by clicking will be :
<?php
include('database.php'); // your database connection
if($_GET['id']) {
$id=$_GET['id'];
} else throw new Exception('Invalid parameter');
$statement = myPDO::getInstance->prepare(<<<SQL
UPDATE TABLE
SET STOCK = STOCK-1
WHERE Product_id = :id
SQL
);
$statement->execute(array(":id" => $id));
This script will do the job

PHP MYSQL: Correct Code to Increment a views column every time a page is loaded in the browser

I am trying to create a "views" system on my books website.
I have the following tables with the following columns:
Books
-bookid
-bookname
-authorid
-views
my webpage is set up to display a book based on the $_GET['bookid'] variable and I want to add 1 (increment the views column by one for that particular book)
I tried using the following code but it didn't update my table:
<?php $sql = "UPDATE `books` \n" . "SET views = views+1 WHERE" . $_GET['bookid'] .= "bookid"; ?>
ALSO: I used dreamweaver to run the recordset query) so maybe something is different.
Please Help!
Sidenote: Can you please recommend a good book/video or written tutorial to learn php and mysql for absolute beginners like my self!
This is important: don't include $_GET paramaters directly in your SQL query.
This makes your website vulnerable to an SQL Injection attack. Sanatise your inputs by using:
$book_id = mysql_real_escape_string($_GET['book_id']); // If it is a string
$book_id = intval($_GET['book_id']); // It it is an integer
// Assuming it is an integer
$sql = "UPDATE books SET views = views+1 WHERE bookid = $book_id";
You obviously need to execute that query, are you doing that?
$user="username";
$password="password";
$database="database";
mysql_connect(localhost,$user,$password);
mysql_select_db($database) or die( "Unable to select database");
mysql_query($sql);
mysql_close();
EDIT:
Also, just a tip, since you're using $_GET you should be executing something like yourscript.php?book_id=12345, is that what you're doing?
you've already found some of the best ways to learn PHP: writing code and coming here when you don't know further :) (don't have a real good tutorial on my hands beyond that ;)
As for your question:
check the value of $_GET['bookid']
check the value of $sql
if all looks as intended, run the query directly
oh wait.
you're not actually executing the sql in your code, just generating a string with the query. you need to open a connection etc, or are you doing that and leaving it out here?
Your query looks slightly off. Try this:
$sql = 'UPDATE books SET views = views+1 WHERE bookid = ' . intval($_GET['book_id']);

Categories