使用PDO实现安全可靠的面向对象插入
这段代码对SQL注入安全吗?它使用预准备和参数化语句。如果不是,我应该怎么做,因为我只想通过面向对象的过程来使用它,在这个过程中我可以插入列名和列值。
<?php
class CommunItY
{
const community_host = "localhost";
const community_db = "DB";
const db_username = "root";
const db_password = "";
private $conn = null;
public $trace = "";
public function insert($table ,$values = array())
{
try{
foreach ($values as $field => $v)
{
$ins[] = ':' . $field;
}
$ins = implode(',', $ins);
$fields = implode(',', array_keys($values));
$sql = "INSERT INTO $table ($fields) VALUES ($ins)";
$ready = $this->conn->prepare($sql);
foreach ($values as $f => $v)
{
$ready->bindValue(':' . $f, $v);
}
$ready->execute();
}
catch(Exception $e){
$this->trace .= " • insertion error • ". $e->getMessage();
}
}//end of method
public function __construct(){
$connectionString = sprintf("mysql:host=%s; dbname=%s; charset=utf8",
CommunItY::community_host, CommunItY::community_db);
try {
$this->conn = new PDO($connectionString, CommunItY::db_username, CommunItY::db_password);
$this->conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} //end of connection by PDO
catch(PDOException $e){
$this->trace .= " • ". $e->getMessage();
}
}//end of construct
public function __destruct(){
$this->conn = null; //close connection
} //end of destruct
}
calling...
$call = new Contact()
$call->insert(table_x, array('col1' => 'value1', 'col2' => 'value2'));发布于 2013-07-28 15:21:29
可能不会。您已经在字段名称中引入了注入。如果您可以保证字段名称总是由您的代码生成,而不是从外部来源生成,这可能是可以的。但这是一个公共方法。因此,系统中的任何一点代码都可能试图做一些“聪明”的事情,并最终通过向insert方法传递一个参数来打开一个漏洞。
您应该将传入的字段名放在引号中进行转义(或者,如果您使用的是MySQL,并且没有启用ANSI兼容性,请使用反引号)。您还必须对名称中的所有引号进行转义。
$fields = implode(',', array_map(function($name) {
return '"' . str_replace('"', '""', $name) . '"' ;
}, array_keys($values)));对于这些值,您应该只使用位置参数(?)或者自己起名字。我不认为PDO有转义:bind_param名称的机制。
还要注意的是,由于PHP对7位ASCII范围之外的字符的糟糕处理,如果有人开始使用字符串的内部字节编码,这可能仍然不是100%安全的。在这种情况下,唯一安全的方法是首先确保字段名只包含预期的字符,或者根据众所周知的字段名列表对其进行验证(可能是通过使用INFORMATION_SCHEMA检查表列)。
发布于 2013-07-28 17:01:08
您的代码可能不会受到SQL注入的影响。但也有一个缺陷。您不应允许使用任何类型的表名或字段名。更好的解决方案是为您想要表示的每个对象创建一个类。假设你有苹果和香蕉作为你的应用程序的重要部分。然后创建类Apple和类Banana,这两个类分别表示数据库中的一个香蕉或苹果条目。
对于数据库的实际编辑,您可以分别创建一个能够更新苹果或香蕉的类AppleEditor或BananaEditor。这样,您就有了一组固定的字段,并且只有值是可变的。
回到您的代码:我不会使用const作为数据库凭据。因此,任何脚本都可以访问它们。最好在私有静态变量中使用它们。
然后是类名的问题。在call部分下面,您有一个类Contact,但在顶部,您的类被命名为CommunItY。为了有一个最小的代码设置,我要重新注释以下内容:
class Contact
{
private static $community_host = "localhost";
private static $community_db = "DB";
private static $db_username = "root";
private static $db_password = "";
/**
* the database connection
* @var \PDO
*/
private $conn = null;
private $trace = "";
/**
* name of the contact
* @var string
*/
private $name = '';
/**
* address of the contact
* @var string
*/
private $address = '';
private $contactID = 0;
public function __construct($id) {
$this->connectToDatabase();
$this->contactID = intval($id);
}//end of construct
/**
* Updates a contact.
*
* @param string $name
* @param string $address
*/
public function update($name, $address)
{
$name = trim($name);
$address = trim($address);
try{
$sql = "UPDATE contact SET name = ?, address = ? WHERE contactID = ?";
$ready = $this->conn->prepare($sql);
$ready->bindValues(1, $name, \PDO::PARAM_STR);
$ready->bindValue(2, $address, \PDO::PARAM_STR);
$ready->bindValue(3, $this->contactID, \PDO::PARAM_INT);
$ready->execute();
}
catch(\PDOException $e){
$this->trace .= " • insertion error • ". $e->getMessage();
}
}//end of method
public function __destruct(){
$this->conn = null; //close connection
} //end of destruct
private function connectToDatabase() {
$connectionString = sprintf("mysql:host=%s; dbname=%s; charset=utf8",
self::$community_host, self::$community_db);
try {
$this->conn = new \PDO($connectionString, self::$db_username, self::$db_password);
$this->conn->setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
$this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
} //end of connection by PDO
catch(\PDOException $e){
$this->trace .= " • ". $e->getMessage();
}
}
}
$call = new Contact(1);
$call->update('name', 'address');这当然不是一个完美的情况。但它显示了如何处理它。
https://stackoverflow.com/questions/17675145
复制相似问题