MySQL 准备语句混乱
MySQL Prepared statement confusion
好的,所以我在准备语句方面遇到了很多麻烦。我已经做了几个小时的研究,但似乎仍然无法完全理解所有内容...
我真的觉得我需要了解准备好的语句,因为我正要在我的网站上发布一些新的免费 APIs(需要 API 键来执行 API) 但我最近意识到一切都是多么不安全....我可以简单地使用 SQL 注入来绕过 API 密钥检查,例如'OR'1'='1
这是我验证 API 密钥的方法:
$apikey = $_GET['key'];
$sql = "SELECT * FROM `table` WHERE `key` = '$apikey'";
$query = mysqli_query($con, $sql);
if($query)
{
$fetchrow = mysqli_fetch_row($query);
if(isset($fetchrow[0]))
{
echo "API Key is valid!";
}
else
{
echo "API KEY is invalid";
}
}
就像上面提到的,这可以很容易地通过执行我的 API 来绕过,就像这样
http://website.com/api.php?key='OR'1'='1
起初这真的让我很害怕,但后来我做了一些研究并了解到防止任何形式的 SQL 注入的好方法是使用准备好的语句,所以我做了很多研究并且它只是对我来说似乎很复杂:/
所以我想我的问题是,我怎样才能使用上面的代码,并使用准备好的语句使其以相同的方式运行?
$sql = "SELECT * FROM `table` WHERE `key` = ?";
if(stmt = $mysqli->prepare($sql)) {
$stmt->bind_param("i", $apikey);
$stmt->execute();
$stmt->bind_result($res);
$stmt->fetch();
$stmt->close();
}
可能你需要的一切:
class Database {
private static $mysqli;
连接到数据库:
public static function connect(){
if (isset(self::$mysqli)){
return self::$mysqli;
}
self::$mysqli = new mysqli("DB_HOST", "DB_USER", "DB_PASS", "DB_NAME");
if (mysqli_connect_errno()) {
/*Log error here, return 500 code (db connection error) or something... Details in $mysqli->error*/
}
self::$mysqli->query("SET NAMES utf8");
return self::$mysqli;
}
执行语句并得到结果:
public static function execute($stmt){
$stmt->execute();
if ($mysqli->error) {
/*Log it or throw 500 code (sql error)*/
}
return self::getResults($stmt);
}
将结果绑定到纯数组:
private static function getResults($stmt){
$stmt->store_result();
$meta = $stmt->result_metadata();
if (is_object($meta)){
$variables = array();
$data = array();
while($field = $meta->fetch_field()) {
$variables[] = &$data[$field->name];
}
call_user_func_array(array($stmt, "bind_result"), $variables);
$i = 0;
while($stmt->fetch()) {
$array[$i] = array();
foreach($data as $k=>$v)
$array[$i][$k] = $v;
$i++;
}
$stmt->close();
return $array;
} else {
return $meta;
}
}
Class结束:)
}
用法示例:
public function getSomething($something, $somethingOther){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE something = ? AND somethingOther = ?");
$stmt->bind_param("si", $something, $somethingOther); // s means string, i means number
$resultsArray = Database::execute($stmt);
$someData = $resultsArray[0]["someColumn"];
}
正在解决您的问题:
public function isKeyValid($key){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE key = ? LIMIT 1");
$stmt->bind_param("s", $key);
$results = Database::execute($stmt);
return count($results > 0);
}
PHP 自动关闭数据库连接所以不用担心。
好的,所以我在准备语句方面遇到了很多麻烦。我已经做了几个小时的研究,但似乎仍然无法完全理解所有内容...
我真的觉得我需要了解准备好的语句,因为我正要在我的网站上发布一些新的免费 APIs(需要 API 键来执行 API) 但我最近意识到一切都是多么不安全....我可以简单地使用 SQL 注入来绕过 API 密钥检查,例如'OR'1'='1
这是我验证 API 密钥的方法:
$apikey = $_GET['key'];
$sql = "SELECT * FROM `table` WHERE `key` = '$apikey'";
$query = mysqli_query($con, $sql);
if($query)
{
$fetchrow = mysqli_fetch_row($query);
if(isset($fetchrow[0]))
{
echo "API Key is valid!";
}
else
{
echo "API KEY is invalid";
}
}
就像上面提到的,这可以很容易地通过执行我的 API 来绕过,就像这样
http://website.com/api.php?key='OR'1'='1
起初这真的让我很害怕,但后来我做了一些研究并了解到防止任何形式的 SQL 注入的好方法是使用准备好的语句,所以我做了很多研究并且它只是对我来说似乎很复杂:/
所以我想我的问题是,我怎样才能使用上面的代码,并使用准备好的语句使其以相同的方式运行?
$sql = "SELECT * FROM `table` WHERE `key` = ?";
if(stmt = $mysqli->prepare($sql)) {
$stmt->bind_param("i", $apikey);
$stmt->execute();
$stmt->bind_result($res);
$stmt->fetch();
$stmt->close();
}
可能你需要的一切:
class Database {
private static $mysqli;
连接到数据库:
public static function connect(){
if (isset(self::$mysqli)){
return self::$mysqli;
}
self::$mysqli = new mysqli("DB_HOST", "DB_USER", "DB_PASS", "DB_NAME");
if (mysqli_connect_errno()) {
/*Log error here, return 500 code (db connection error) or something... Details in $mysqli->error*/
}
self::$mysqli->query("SET NAMES utf8");
return self::$mysqli;
}
执行语句并得到结果:
public static function execute($stmt){
$stmt->execute();
if ($mysqli->error) {
/*Log it or throw 500 code (sql error)*/
}
return self::getResults($stmt);
}
将结果绑定到纯数组:
private static function getResults($stmt){
$stmt->store_result();
$meta = $stmt->result_metadata();
if (is_object($meta)){
$variables = array();
$data = array();
while($field = $meta->fetch_field()) {
$variables[] = &$data[$field->name];
}
call_user_func_array(array($stmt, "bind_result"), $variables);
$i = 0;
while($stmt->fetch()) {
$array[$i] = array();
foreach($data as $k=>$v)
$array[$i][$k] = $v;
$i++;
}
$stmt->close();
return $array;
} else {
return $meta;
}
}
Class结束:)
}
用法示例:
public function getSomething($something, $somethingOther){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE something = ? AND somethingOther = ?");
$stmt->bind_param("si", $something, $somethingOther); // s means string, i means number
$resultsArray = Database::execute($stmt);
$someData = $resultsArray[0]["someColumn"];
}
正在解决您的问题:
public function isKeyValid($key){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE key = ? LIMIT 1");
$stmt->bind_param("s", $key);
$results = Database::execute($stmt);
return count($results > 0);
}
PHP 自动关闭数据库连接所以不用担心。