使用函数从数据库中抓取内容。这样安全吗?

时间:2016-09-07 19:34:29

标签: php mysql pdo

我有一个简单的问题。我不太擅长编程,但这样安全吗?

目前我正在使用函数来获取用户名,头像等。

看起来像这样:

    try {
    $conn = new PDO("mysql:host=". $mysql_host .";dbname=" . $mysql_db ."", $mysql_username, $mysql_password);
    // set the PDO error mode to exception
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);


    }
catch(PDOException $e)
    {
    echo "Connection failed: " . $e->getMessage();
    }

config.php ^^

function getUsername($userid) {
        require "config/config.php";
        $stmt = $conn->prepare("SELECT username FROM accounts WHERE id = ? LIMIT 1"); 
        $stmt->execute([$userid]); 
        $name = $stmt->fetch();
        return $name["username"];
    }
    function getProfilePicture($userid) {
            require "config/config.php";
            $stmt = $conn->prepare("SELECT profilepicture FROM accounts WHERE id = ? LIMIT 1"); 
            $stmt->execute([$userid]); 
            $image = $stmt->fetch();
            return $image["profilepicture"];
        }

这是正确的,更重要的是,这样安全吗?

3 个答案:

答案 0 :(得分:1)

看起来假设你的配置文件是正确的,它会起作用。因为它是一个准备好的声明,就安全性而言看起来很好。

他们只传递身份证。添加一些安全性可以做的一件事是确保传入的$ userid是正确的类型。 (我假设一个int)。

例如,如果您希望有一个整数ID,并且您得到的字符串可能是phishy(可能是SQL注入),但是如果您可以确认它是一个int(如果不是则可能会抛出错误)然后你可以确定你得到了你想要的东西。

您可以使用:

is_int($userid);

确保它是一个int

http://php.net/manual/en/function.is-int.php

的is_int()的更多详细信息

希望这有帮助。

答案 1 :(得分:1)

这是安全的(至少这部分代码,我不知道@icecub指出的数据库连接部分),但有些事情你应该注意:

  1. 您只需要在文件开头require config.php一次
  2. 您只需要prepare语句一次,然后在函数上调用它,每次都可以减慢脚本速度:
      

    查询只需要解析(或准备)一次,但可以使用相同或不同的参数执行多次。准备好查询后,数据库将分析,编译和优化其执行查询的计划。 - PHP Docs

  3. (不是错误,但我个人推荐)使用Object Orientation帮助您更好地整理代码并更轻松地理解/理解
  4. 如@BHinkson所述,您可以使用is_int来验证用户的ID(如果您使用ID作为数字)
  5. 关于HTML转发,我建议您注册用户名等.HTML已转义。

答案 2 :(得分:1)

是的,SQL注入是安全的。

其他一些答案是将主题转移到XSS保护中,但是您显示的代码不会回显任何内容,它只是从数据库中获取并从函数返回值。当你从函数中返回时,我建议不要使用预转义值,因为你不确定是否要调用该函数以将结果回显给HTML响应。

没有必要使用is_int(),因为当您在数字上下文中使用参数时,MySQL会自动转换为整数。非数字字符串被解释为零。换句话说,以下谓词给出了相同的结果。

WHERE id = 0
WHERE id = '0'
WHERE id = 'banana'

我建议不要在每个函数中连接数据库。 MySQL的连接代码相当快(特别是与其他一些RDBMS相比),但为每个 SQL查询建立新连接仍然很浪费。而是连接到数据库一次并将连接传递给函数。

当您连接到数据库时,您捕获异常并回显错误,但随后您的代码将继续,就像连接成功一样。相反,如果出现问题,您应该让脚本死掉。此外,不要向用户输出系统错误消息,因为他们无法对该信息执行任何操作,并且可能会显示有关您的代码的过多信息。记录错误以进行自己的故障排除,但输出更通用的内容。

您也可以考虑为您的连接定义一个函数,并为您的用户定义一个类。这是一个例子,虽然我还没有测试过:

function dbConnect() {
    try {
        $conn = new PDO("mysql:host=". $mysql_host .";dbname=" . $mysql_db ."", $mysql_username, $mysql_password);
        // set the PDO error mode to exception
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    }
    catch(PDOException $e)
    {
        error_log("PDO connection failed: " . $e->getMessage());
        die("Application failure, please contact administrator");
    }
}

class User {
    protected $row;

    public function __construct($userid) {
        global $conn;
        if (!isset($conn)) {
            $conn = dbConnect();
        }
        $stmt = $conn->prepare("SELECT username, profilepicture FROM accounts WHERE id = ? LIMIT 1"); 
        $stmt->execute([$userid]); 
        $this->row = $stmt->fetch(PDO::FETCH_ASSOC);
    }

    function getUsername() {
        return $this->row["username"]
    }

    function getProfilePicture() {
        return $this->row["profilepicture"]
    }
}

用法:

$user = new User(123);
$username = $user->getUsername();
$profilePicture = $user->getProfilePicture();