Looping Through GET, POST, and COOKIE to Sanitize? - php

Considering that everyone is always worried about User Data (And Rightly So), would it be sufficient to simply loop through each external array when you get it, and apply a mysql_real_escape_string().
I'm curious to if this is a bad idea.
Something like:
function getExternalData($type='GET')
{
$type = strtoupper($type);
$data = $_$type;
foreach($data as $key => $value)
{
$clean[$key] = mysql_real_escape_string($value);
}
return $clean;
}
That would make all that data safe to use in databases. But, what are the cons of doing it this way?

The main con is if you have to process the input, for example in order to parse markup, you'll have to unescape the input then not forget to re-escape it. Also, it's quite inefficient. Query placeholders are a very good way to prevent SQL injection.
As for sanitization itself (not only for SQL) you should take a look at the Filter extension, available by default in PHP 5.2 and in PECL for 5.1.

Applying mysql_real_escape_string on all superglobal variables convey the impression that you either want to use them exclusively in MySQL queries or that you have no clue what mysql_real_escape_string is good for.

Cons:
You might forget there are other types of user input and so, not clean them.
And, of course, The excess loops.
And, I think DB cleaning should be done the latest as possible, just before you enter the data into the DB in your DL.
Cleaning data for presentation should be done just before presenting the data, etc.
That said, until you will try this approach, you will never know, as people usualy tend to disagree with approaches they don't use themselves (see above :-) )

Don't try to sanitize data. Use queries with placeholders. See bobby-tables.com

I think it's a bad idea to generalize validation and filtering logic. That was the idea behind magic quotes after all, which is now universally condemned.
Besides that, validating field inputs usually involves a lot of specific junk. Generic rules turn out to be a rather small part of validation, especially as apps grow in size and complexity.
It would be a better idea to come up with a mini framework that allows you to handle both generic and specific validation in the same place. Something like this...
class BrokenRules extends Exception {
protected $errors;
function __construct($errors) {
$this->errors = $errors;
}
function getErrors() {
return $this->errors;
}
}
class Foo {
protected $db;
function __construct(PDO $db) {
$this->db = $db;
}
function loadNew() {
return array('bar' => 'new foo', 'baz' => 5);
}
function loadById($id) {
$stmt = $this->db->prepare('SELECT * FROM foo WHERE id = ?');
$stmt->bindValue(1, $id, PDO::PARAM::INT);
$stmt->execute();
return $stmt->fetch();
}
function save($data) {
return isset($data['id']) ? $this->update($data) : $this->insert($data);
}
protected function validateForInsert($data) {
if ((int)$data['baz'] <= 3) $errors['baz'][] = 'Baz must be greater than 3';
if (isset($errors)) throw new BrokenRules($errors);
}
protected function validateForUpdate($data) {
// TODO: validateForUpdate
}
protected function insert($data) {
$this->validateForInsert($data);
$stmt = $this->db->prepare('INSERT INTO foo (x, y) VALUES (?, ?)');
$stmt->bindValue(1, $data['bar'], PDO::PARAM_STR);
$stmt->bindValue(2, $data['baz'], PDO::PARAM_INT);
$stmt->execute();
return $this->db->lastInsertId();
}
protected function update($data) {
$this->validateForUpdate($data);
$stmt = $this->db->prepare('UPDATE foo SET x = ?, y = ? WHERE id = ?');
$stmt->bindValue(1, $data['bar'], PDO::PARAM_STR);
$stmt->bindValue(2, $data['baz'], PDO::PARAM_INT);
$stmt->bindValue(3, $data['id'], PDO::PARAM_INT);
$stmt->execute();
return $data['id'];
}
}
try {
$foo = new Foo($pdo);
if ($_POST) {
$id = $foo->save($_POST);
redirect("edit_foo.php?id=$id");
} else if (isset($_GET['id'])) {
$data = $foo->loadById($_GET['id']);
} else {
$data = $foo->loadNew();
}
} catch (BrokenRules $e) {
$errors = $e->getErrors();
}
include 'templates/foo.php';

i think you're actually looking for array_map(). This removes the need for a loop. And yes, this is acceptable for making requests safe for database.
One thing though, you might want to use $_SERVER['REQUEST_METHOD'] here. (unless you're using it to pass in as the param to this function.)

Related

mysqli::$error contains no error message if I move its access too far away in return chain

As per the documentation at https://www.php.net/manual/en/mysqli.error.php I expect an empty string to be indicative of there being no error. All my classes are using a database object that gets passed around each and retrieved by this class method getConn:
public function getConn() {
return $this->_conn;
}
And the database class contains exactly:
public function __construct() {
$this->_conn = new mysqli(self::$host, self::$user, self::$password, self::$schema);
$this->_conn->set_charset(self::$charset);
...
}
...
The destructor indicates that it is the last thing to destruct and this is where the connection is forcibly closed.
My API is invoked in a way which results in this class method executing in file1.php which in turn calls create on $this->_p:
class PAPI {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
$this->_p = new APPP($DB);
...
}
...
private function _newC() {
...
for ($i = 0; $i < $numFiles; $i ++) {
if ($_FILES['p']['size'][$i] > 0) { /// files not guaranteed to be present
if (($res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid)) !== APPP_STATUS::APPP_SUCCESS) {
switch ($res) {
...
case APPP_STATUS::APPP_SQLERR:
APPCore::Log(json_encode($this->DB->error));
return new PAPIError(PAPI_ERROR::PAPISQLERR, PAPI_ERROR_MSG[PAPI_ERROR::PAPISQLERR], $this->DB->error);
...
}
} else {
...
}
}
}
...
}
...
}
create is seen here in the second class in file2.php, it calls setP on $this->_c:
class APPP {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
$this->_c = new APPC($DB);
...
}
...
public function create(string $tmp = null, int $cid = null, int $uid = null, string $table = null) {
...
if ($this->_c->setP($cid, $hash, $table) === APPC_STATUS::APPC_SUCCESS) {
...
} else {
APPCore::Log(json_encode($this->DB->error));
return APPP_STATUS::APPP_SQLERR;
}
}
}
And setP is seen here finally in file3.php:
class APPC {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
}
...
public function setP(int $id = null, string $hash = null, string $table = null) {
...
$target = self::_tableFromHash($table);
$params = [
json_encode([$hash]),
$id
];
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
/*!< since table is an argument, this could fail if the table doesn't exist */
if ($updateCQuery === false)
return APPC_STATUS::APPC_SQLERR; /*!< this is where it the error propagates out from */
...
}
}
It is expected that APPC_STATUS::APPC_SQLERR should be returned from the final line included in the snippet - this occurs and it is seen by file2.php. In file2.php the APPCore::Log(json_encode($this->DB->error)); succeeds if it is left uncommented, and the error makes it out to the log. Note that this has crossed execution over one return from another file. If I instead return APPP_STATUS::APPP_SQLERR; in file2.php and log from the switch in file1.php instead of the successful position that would otherwise be left in file2.php, then the value of $this->DB->error is an empty string.
Why is it empty when the only path the execution has taken, is a return into a switch statement? I have verified that only one instance of the database class exists and the a server disconnection is only made much after the empty mysqli::$error is observed. I.e., I cannot find anywhere where the the mysqli object performs any tasks between returning, and accessing its error property.
I have verified with mysql's general_log, that the last query is indeed the last query and that a quit is made very shortly after.
2020-09-14T06:36:06.025182Z 10 Prepare INSERT INTO `p` (`code`, `uid`, `cid`, `private`, `created`, `modified`) VALUES(?, ?, ?, ?, ?, ?, NOW(), NOW())
2020-09-14T06:36:06.025222Z 10 Execute INSERT INTO `p` (`code`, `uid`, `cid`, `private`, `created`, `modified`) VALUES('c288b073d41f26753a937d02056a14864ae1959d', NULL, 22353, NULL, NOW(), NOW())
2020-09-14T06:36:06.029293Z 10 Close stmt
2020-09-14T06:36:06.030761Z 10 Quit
I thought it worth pointing out that although referring to the mysqli::$error property doesn't consume it, that I am shifting the reference to it from file2.php to file1.php where it makes more sense in the surrounding software.
What is happening and how do I prevent it happening such that I can access the error appropriately?
Debugging shows the following sequence:
The differences between each of these images are two steps 1 to 2, call $this->DB->prepare(...); and return because of the error and each of the other steps are single steps (F11 in VSCode with XDebug server).
Regarding your actual problem: there is no magic. Either there is a new connection is created somewhere, or there is another successful query is called, thus nullifying the error message.
Either way, that's a minor issue compared to that outdated method of checking errors you are using. It's 2020 now and time to use exceptions. Seriously, 90% of this code will just disappear in a puff of smoke.
Just tell mysqli to throw exceptions. Now, you can start writing your code straight away, without a single if statement intended for the error checking.
Let me to recreate the full elaborate code for a single query execution:
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
if ($updateCQuery === false)
return APPC_STATUS::APPC_SQLERR;
}
$updateBind = $updateCQuery->bind_param("ss", $hash, $id);
if ($updateBind === false)
return APPC_STATUS::APPC_SQLERR;
}
$updateExecute = $updateCQuery->execute();
if ($updateExecute === false)
return APPC_STATUS::APPC_SQLERR;
}
and compare this monster of a code with the following code snippet:
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
$updateCQuery->bind_param("ss", $hash, $id);
$updateCQuery->execute();
Moreover, the same goes for all the levels above. There is no need for the elaborate error checking in the other files either
if ($this->_c->setP($cid, $hash, $table) === APPC_STATUS::APPC_SUCCESS) {
...
} else {
APPCore::Log(json_encode($this->DB->error));
return APPP_STATUS::APPP_SQLERR;
}
will become just
$this->_c->setP($cid, $hash, $table);
and
if (($res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid)) !== APPP_STATUS::APPP_SUCCESS) {
switch ($res) {
...
case APPP_STATUS::APPP_SQLERR:
APPCore::Log(json_encode($this->DB->error));
return new PAPIError(PAPI_ERROR::PAPISQLERR, PAPI_ERROR_MSG[PAPI_ERROR::PAPISQLERR], $this->DB->error);
...
}
will become just
$res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid);
but surely you would like to know where to put that logging and returning code. And I have an answer for you: in the error/exception handler. Just create a single place where all errors being logged and the appropriate message is returned to the client. A simple exception handler would catch all errors that would ever occur, and handle them in a single place in the uniform manner, without the need of writing the elaborate error checking code on the every level.

PDO update issue

I'm trying to write a bit of a controller to do some CRUD with PDO and have this issue where nothing happens and doesn't throw. I am pretty sure i am binding correctly but after dumping out, I can't see any issues with this and the table doesn't update.
Can someone please have a look?.
public function update($query, $array)
{
try
{
$stm = $this->conn->prepare($query);
foreach($array AS $key=>$value)
{
if(gettype($value) == "integer")
{
$stm->bindParam($key,$value,PDO::PARAM_INT);
}
if(gettype($value) == "string")
{
$stm->bindParam($key,$value,PDO::PARAM_STR);
}
}
$stm->execute();
return ($stm->rowCount()<=0) ? FALSE : $stm->rowCount();
}
catch(Exception $e)
{
echo 'Error with the query on line: ' . __LINE__ . ' in file: ' . __FILE__;
}
}
$test = new SQL('127.0.0.1', 'test', '*************', 'mike_test');
$pull = $test->update('UPDATE names SET name=:name WHERE id=:id;',[':name'=>'James',':id'=>2]);
The problem is the use of bindParam() which binds to a variable, and since you bind each parameter to the same variable ($value), you essentially use the last value for all parameters.
Solution: use bindValue().
I can't see any issues with this and the table doesn't update.
Then the ID value doesn't match anything in the table, as it did update on my test system.
You just made you code too bloated. But there is a rule: the more code you write, the more errors you introduce. So make this function this way
public function update($query, $array)
{
$stm = $this->conn->prepare($query);
$stm->execute($array);
return $stm->rowCount();
}

Error handling prepared statements in a production environment

In a production environment when using prepared statements and all other validation is done, do I need to error check every every step of the way or can I just check end result of $stmt for true or false?
I am trying to clean up about 2000 lines of a function file and a lot of it just seems like wasted space when there is so much validation already done(ie checking for empty values, required values, empty fields etc).
Here is a rough, extremely simple, example of what I would like to do.
$sql = "SELECT count(*) FROM foo WHERE somecol = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s",$value);
$stmt->execute();
$stmt->bind_result($c);
$stmt->fetch();
if(false === $stmt){
//My error report
trigger_error("Something bad happened!" );
//Error user sees
$userErrorMsg[] 'Some generic msg here';
}
EDIT: I probably should have mention the $conn has been checked previously.
You have to decide if it's necessary in your case or not. But some programmers would say, that the code to catch an error is almost that much like the normal code.
In short: If there can be an error CATCH IT ;)
Otherwise I would recommend you to create a Wrapper class for your DB functions.
Just a small example to point you in the right direction:
class MyDBClass {
private static $con = null;
private static $instance = null;
public static function getInstance() {
if( null === self::$instance) {
// add error handling ;)
self::$instance = new MyDBClass();
self::$instance->setConnection();
}
return self::$instance;
}
private function setConnection() {
if( null === self::$con) {
// add error handling ;)
self::$con = mysqli_connect("localhost","my_user","my_password","my_db");
}
}
private function __construct() {}
public function select( $tableName, $columns = "*", $conditions = array(), $numRows = null ) {
// add your stuff with error handling
}
public function selectRow( $tableName, $columns = "*" , $conditions = array() ) {
// add your stuff with error handling
}
}
// use of class
$db = MyDBClass::getInstance();
$db->select( "mytable" );// would (for example) select * from mytable
NOTE: This is not a working example and I would recommend to get use a good framework or a small wrapper class

Php database models

<?php
class ann {
public function __construct($context, $orgs_id, $created_at) {
$this->context = $context;
$this->orgs_id = $orgs_id;
$this->created_at = $created_at;
}
function create(){
$createann = mysql_query("INSERT INTO anns(context,
orgs_id, created_at)
VALUES('$this->context',
$this->orgs_id, '$this->created_at'");
if($createann) echo "Duyuru Başarıyla Eklendi"; else echo "Duyuru
Eklenemedi";
}
function read($id){
$readann = mysql_query("SELECT * FROM anns WHERE id = $id");
$context = mysql_result($readann,0, "context");
$orgs_id = mysql_result($readann,0, "orgs_id");
$created_at = mysql_result($readann,0,
"created_at");
$ann = new ann($context, $orgs_id, $created_at);
return $ann;
}
function update($id, $context){
$updateann = mysql_query("UPDATE anns SET context =
'$context' WHERE id = $id");
if($updateann) echo "Update success"; else echo
"Update failed";
}
function delete($id){
$deleteann = mysql_query("DELETE FROM anns WHERE id
= $id");
if($deleteann) echo "Delete success"; else echo "Delete not success";
}
//crud fonksiyonlari burda bitiyor
}
?>
There is something wrong with our logic here but we are very new to php. We tried to create rails like models, but it think something with our class-object notation is wrong. So the code did not work. We cannot even create any object with it.
Thank you guys
context, orgs_id and created_at must be should be first declared either as public, private or protected before you use them.
In your create method, you don't filter user input. This may cause to your application SQL injection, you have to you always filter user input. Use either mysql_real_escape_string or prepared statment by PDO.
You may check this tutorial.
two things (which maybe only apply to your codesample here):
In your sample, you dont close your
Class, because the last "}" is
commented out.
You never opened a connection to your database, so the query would fail.
a few observations:
declaring the attributes in the constructor is possible, but it's not elegant. I'd rather do:
class ann {
private $context;
private $orgs_id;
the "->" operator won't work inside a string. You'll need to concatenate the query:
"INSERT INTO anns(context,orgs_id, created_at) VALUES('".$this->context."',".$this->orgs_id".", '".$this->created_at."'"
but be careful on sql injection
The rest should be fine! Good Luck.

OOP or MySQL Calls. Make object or call directly from MySQL?

I'm not sure what's a better practice or a more real-world practice. I'm looking to create a Catalog System from scratch, but unsure what the best approach would be.
I was thinking that I use objects when I need to display information like, info.php?id=100. Have code like this for displaying
Game.class.php file
class Game {
private $name;
private $type;
private $database;
public function __construct($database, $id) {
$information = $database->select($id); // returns mysql_fetch_array
$this->database = $database;
$this->setName($information['name']);
$this->setType($information['type']);
}
public function __destruct() {
unset($this->name);
...
}
...
private function setName($name) {
$this->name = $name;
}
...
public function getName() {
return $this->name;
}
...
public function addToDatabase() {
// stuff here to check if it exists in the database or not
$database->insert('test', array('id' => '', 'name' => $this->name, 'type' => $this->type));
}
}
info.php?id=100 file
// already have __autload()
$id = sanitize($_POST['id']);
$item = new Game($db, $id);
echo "Item name: " . $item->getName() . " <br />";
But I don't really need to create objects for like when I add or update things add.php?name=XMen&type=GameBoy
I wouldn't want to do this:
$name = sanitize($_POST['name']); // sanitize
$type = sanitize($_POST['type']); // sanitize
$newObject = new Game($name, $type);
$newObject->addToDatabase();
I should instead I should just skip creating an object and just insert directly
$name = sanitize($_POST['name']); // sanitize
$type = sanitize($_POST['type']); // sanitize
$sql = "INSERT INTO test ('id', 'name', 'type') VALUES ('', $name, $type)";
mysql_query($sql);
or if I had a database class
$name = sanitize($_POST['name']); // sanitize
$type = sanitize($_POST['type']); // sanitize
$db->insert('test', array('id' => '', 'name' => $name, 'type' => $type));
To get complete abstraction you might look into 3rd-party ORMs. At the very, very least you should create your own database class. I usually use a Singleton design pattern if I only plan on having one database connection.
You don't want raw SQL statements all over the place because you may need to switch to and/or support different versions of MySQL or even a different database all together. It'd be easier to replace a single database management object than to fix all those queries throughout your code.
One of my database classes is much like that third option you posted and it works quite well. It has a bunch of conveniences like insertArray(), getOneRow(), getOne(), getOneFromAllRows(), executeExternalFile(), and an iteratable result set for all SELECT-like queries.
For this example, it doesn't seem necessary to create the object first. Just insert straight into the database.

Categories