gpt4 book ai didi

php - 切换到准备好的语句

转载 作者:可可西里 更新时间:2023-11-01 07:24:43 25 4
gpt4 key购买 nike

我刚刚开始我的第一个项目(为了好玩)。我正在学习 PHP 和 MySQL,并且已经完成了我的第一个工作应用程序。它有效,但我现在正在学习如何保护我的应用程序,从而防止 SQL 注入(inject)。我有大约 50 多个 PHP 文件来管理与我的 MySQL 数据库的交互。它们看起来都像这样:

<?php
$inputvalues = $_POST;
$errors = false;
$result = false;
session_start();
$uid = $_SESSION['usr_id'];
$mysqli = new mysqli('localhost', "root", "", "testdb");

if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

foreach ($inputvalues as $key => $value) {
if(isset($value) && !empty($value)) {
$inputvalues[$key] = $mysqli->real_escape_string( $value );
} else {
$errors[$key] = 'The field '.$key.' is empty';
}
}

if( !$errors ) {
$addresult = "
SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id WHERE b.id = '".$inputvalues['schoolid']."'
";

if( $result = $mysqli->query($addresult) ) {
while($row = $result->fetch_all())
{
$returnResult = $row;
}
}
}
mysqli_close($mysqli);
echo json_encode(['result' => $returnResult, 'errors' => $errors]);
exit;
?>

这是我在整个应用程序中用于从数据库读取和写入数据的格式。如果我需要将它们更改为准备好的语句,我不插入任何信息而只是检索它们,我将如何处理?

此外,如果我没有向数据库输入任何数据,它是否仍然容易受到注入(inject)攻击?

能否请您提供一个示例,说明如何使我当前的代码适应准备好的语句,我将不胜感激。

最佳答案

我也遇到过这种情况。我也在使用连接语句,然后我将我的应用程序切换为准备好的语句。

坏消息是您将更改通过将客户端数据连接到 SQL 语句而构建的每个 SQL 语句,这几乎是您 50 个源文件中的每个 SQL 语句。

好消息是切换到预处理语句的好处是无价的,例如:

1-你永远不会担心所谓的“SQL 注入(inject)攻击”

PHP manual

If an application exclusively uses prepared statements, the developer can be sure that no SQL injection will occur (however, if other portions of the query are being built up with unescaped input, SQL injection is still possible).

对我来说,这个理由——内心的平静——足以支付更改我的源代码的成本。 , 现在您的客户可以在表单名称字段中键入 robert; DROP 表学生; -- ;) 并且您感到安全,不会发生任何事情

2- 您不再需要转义客户端参数。您可以直接在 SQL 语句中使用它们,例如:

$query = "SELECT FROM user WHERE id = ?";
$vars[] = $_POST['id'];

代替

$id = $mysqli->real_escape_string($_POST['id']);
$query = "SELECT FROM user WHERE id = $id";

这是你在使用准备好的语句之前必须做的事情,这让你有忘记像普通人一样转义一个参数的危险。攻击者破坏您的系统所需要做的只是 1 个未转义的参数。


更改代码

通常更改源文件总是有风险并且会带来痛苦,尤其是当您的软件设计很糟糕并且您没有明显的测试计划时。但我会告诉你我做了什么让它尽可能简单。

我做了一个每个数据库交互代码都会用到的函数,所以你以后可以在一个地方改变你想要的东西——那个函数——你可以做这样的东西

class SystemModel
{
/**
* @param string $query
* @param string $types
* @param array $vars
* @param \mysqli $conn
* @return boolean|$stmt
*/
public function preparedQuery($query,$types, array $vars, $conn)
{
if (count($vars) > 0) {
$hasVars = true;
}
array_unshift($vars, $types);
$stmt = $conn->prepare($query);
if (! $stmt) {
return false;
}
if (isset($hasVars)) {
if (! call_user_func_array(array( $stmt, 'bind_param'), $this->refValues($vars))) {
return false;
}
}
$stmt->execute();
return $stmt;
}

/* used only inside preparedQuery */
/* code taken from: https://stackoverflow.com/a/13572647/5407848 */
protected function refValues($arr)
{
if (strnatcmp(phpversion(), '5.3') >= 0) {
$refs = array();
foreach ($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
}

现在,您可以在源文件中的任何地方使用此接口(interface),例如,让我们更改您在问题中提供的当前 SQL 语句。让我们改变这个

$mysqli = new mysqli('localhost', "root", "", "testdb");
$addresult = "
SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined
FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id
WHERE b.id = '".$inputvalues['schoolid']."'";

if( $result = $mysqli->query($addresult) ) {
while($row = $result->fetch_all())
{
$returnResult = $row;
}
}

进入这个

$mysqli = new mysqli('localhost', "root", "", "testdb");
$sysModel = new SystemModel();
$addresult = "
SELECT a.firstnames, a.surname, a.schoolrole, a.datejoined
FROM teachers a LEFT JOIN schools b ON a.schoolid = b.id
WHERE b.id = ?";
$types = "i"; // for more information on paramters types, please check :
//https://php.net/manual/en/mysqli-stmt.bind-param.php
$vars = [];
$vars[] = $inputvalues['schoolid'];

$stmt = $sysModel->preparedQuery($addresult, $types, $vars, $mysqli);
if (!$stmt || $stmt->errno) {
die('error'); // TODO: change later for a better illustrative output
}
$result = $stmt->get_result();
$returnResult = [];
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
$returnResult[] = $row;
}

Also, If I am not entering any data to the DB, is it still vulnerable to injection?

是的,Sql 注入(inject)攻击是通过将错误的字符串连接到您的 SQL 语句来应用的。它是 INSERTSELECTDELETEUPDATE。例如

$query = "SELECT * FROM user WHERE name = '{$_GET['name']}' AND password = '{$_GET['pass']}'"

类似的东西可以被利用

// exmaple.com?name=me&pass=1' OR 1=1; -- 

这将产生一个 SQL 语句

$query = "SELECT * FROM user WHERE name = 'me' AND password = '1' OR 1=1; -- '"
//executing the SQL statement and getting the result
if($result->num_rows){
//user is authentic
}else{
//wrong password
}
// that SQL will always get results from the table which will be considered a correct password

祝你好运,将你的软件切换到准备好的语句,并记住,无论发生什么,你都不会受到 SQL 注入(inject)攻击,这让你高枕无忧,这值得改变源文件的成本

关于php - 切换到准备好的语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45031956/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com