有什么更好的方法可以使这个插入物更安全,更安全,免受注射和操纵

What's a better way to make this insert more secure and safe from injection and manipulation

我一直在尝试以一种更安全的方式组合函数,通过调用要更新的不同列来避免注入或操纵插入。在您看来,此功能是否安全,如果不安全,您建议的更好方法是什么,为什么。

当用户更新他们的个人资料或他们个人资料的特定部分时调用此函数,如您所见,我已经制作了一个包含项目的数组,他们可以在 table 中更新这些项目。此外,我得到的 user_id 来自附加到他们会话的安全加密 JSON 令牌,他们没有发送那个。谢谢你的时间。

function updateProfile( $vars, $user_id ) {
    $db = new Database();
    $update_string = '';
    $varsCount = count($vars);
    $end = ',';
    $start = 1;
    $safeArray = array( "gradYear", "emailAddress", "token", "iosToken", "country", 
"birthYear", "userDescription" );
    foreach($vars as $key => $value) {
        if(in_array( $key, $safeArray )) {
            if($start == $varsCount) {
                $end = '';
            }
         
            $update_string .= $key . '=' . '"' . $value . '"' . $end;
        }
            $start++;
    }

    if($start > 0) {
        $statement = "update users set " . $update_string . " where userId = '$user_id'";
        $query = $db->updateQuery( $statement );
        if($query) {
            $response  = array( "response" => 200 );
        } else {
            $response  = array( "response" => 500, "title" => "An unknown error occured, 
please try again");
        }

    }

正如上面的评论所暗示的,使用查询参数来保护自己免受 SQL 注入是值得的。

您要求举例说明如何进行恶意操作。事实上,它甚至不需要是恶意的。任何合法包含撇号的无辜字符串都可能破坏您的 SQL 查询。恶意 SQL 注入利用了这个弱点。

通过将动态值与您的 SQL 查询分开直到解析查询之后,该弱点已得到修复。我们在 SQL 字符串中使用查询参数占位符,然后使用 prepare() 对其进行解析,然后在您 execute() 准备好的查询时组合这些值。这样它就安全了。

这是我编写函数的方式。我假设使用 PDO 支持命名查询参数。我建议使用 PDO 而不是 Mysqli。

function updateProfile( $vars, $userId ) {
    $db = new Database();
    $safeArray = [
        "gradYear",
        "emailAddress",
        "token",
        "iosToken",
        "country",
        "birthYear",
        "userDescription",
    ];
    // Filter $vars to include only keys that exist in $safeArray.
    $data = array_intersect_keys($vars, array_flip($safeArray));

    // This might result in an empty array if none of the $vars keys were valid.
    if (count($data) == 0) {
        trigger_error("Error: no valid columns named in: ".print_r($vars, true));
        $response = ["response" => 400, "title" => "no valid fields found"];
        return $response;
    }
    
    // Build list of update assignments for SET clause using query parameters.
    // Remember to use back-ticks around column names, in case one conflicts with an SQL reserved keyword.
    $updateAssignments = array_map(function($column) { return "`$column` = :$column"; }, array_keys($data));
    $updateString = implode(",", $updateAssignments);

    // Add parameter for WHERE clause to $data. 
    // This must be added after $data is used to build the update assignments.
    $data["userIdWhere"] = $userId;
    
    $sqlStatement = "update users set $updateString where userId = :userIdWhere";

    $stmt = $db->prepare($sqlStatement);
    if ($stmt === false) {
        $err = $db->errorInfo();
        trigger_error("Error: {$err[2]} preparing SQL query: $sqlStatement");
        $response = ["response" => 500, "title" => "database error, please report it to the site administrator"];
        return $response;
    }
    
    $ok = $stmt->execute($data);
    if ($ok === false) {
        $err = $stmt->errorInfo();
        trigger_error("Error: {$err[2]} executing SQL query: $sqlStatement");
        $response = ["response" => 500, "title" => "database error, please report it to the site administrator"];
        return $response;
    }

    $response = ["response" => 200, "title" => "update successful"];
    return $response;
}

除了 Bill 的出色回答之外,还有一个小建议:始终让您的方法一次做一件事。如果一个方法的工作是更新数据库,那么它应该只更新数据库而不是其他任何东西,包括 HTTP 交互。想象一下,这个方法可以在 non-AJAX 上下文中使用,或者根本没有 web-server,而是来自命令行实用程序。那些 HTTP 代码和 JSON 响应看起来完全偏离轨道。所以有两个类:一个更新数据库,一个与客户端交互。它将使您的代码更加简洁和可重用。

此外,永远不要为每个查询创建到数据库的新连接。相反,有一个现成的连接并将其用于所有数据库交互。

function updateProfile($db, $vars, $userId )
{
    $safeArray = array( "gradYear", "emailAddress", "token", "iosToken", "country", 
"birthYear", "userDescription" );

    // let's check if all columns are safe
    if (array_diff(array_keys($vars), $safeArray)) {
        throw new InvalidArgumentException("Unknown columns provided");
    }
        
    $updateAssignments = array_map(function($column) { 
        return "`$column` = :$column"; }, array_keys($vars)
    );
    $updateString = implode(",", $updateAssignments);

    $vars["userIdWhere"] = $userId;
    
    $sqlStatement = "update users set $updateString where userId = :userIdWhere";    
    $db->prepare($sqlStatement)->execute($vars);
}

看,它使您的代码简洁易读。而且,最重要的是 - 可重复使用。您不必使您的方法臃肿。 PHP是一门非常简洁的语言,如果使用得当