如何在Postgresql中参数化ALTER ROLE语句? [英] How to parameterize an ALTER ROLE statement in Postgresql?

查看:142
本文介绍了如何在Postgresql中参数化ALTER ROLE语句?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我的PSQL数据库建立一个PHP接口,并且我希望一些在PSQL上注册的本地用户登录到我的数据库。我首先要为每个用户创建一个用户名,并使用通用密码(例如 Password123),然后用户可以更改其密码。

I'm trying to make a PHP interface to my PSQL database, and I would like a few local users registered on PSQL to log into my DB. I would first create each username for each user, with a generic password like 'Password123', and then the user could latter change his/her password.

为此,我想到了使用简单的PHP表单:

To do that, I thought of using a simple PHP form:

<form action="" method="post">
    <table>
    <tr> <td> User: </td> <td> <input type="text" name="user" /> </td> </tr>
    <tr> <td> Old password: </td> <td> <input type="password" name="old" /> </td> </tr>
    <tr> <td> New password: </td> <td> <input type="password" name="new1" /> </td> </tr>
    <tr> <td> Repeat new password: </td> <td> <input type="password" name="new2" /> </td> </tr>
     <tr> <td> <input type="submit" name="submit" value="Change password" /> </td> </tr>
</form>

<?php
    if ($_POST) {
        $user = $_POST["user"];
        $old = $_POST["old"];
        $new1 = $_POST["new1"];
        $new2 = $_POST["new2"];

        $link = pg_connect("dbname=mydb host=localhost user=$user password=$old connect_timeout=1");
        if (!$link) { 
            die("Error connecting to mydb: ".pg_last_error($link)); 
        }

        if ($new1 <> $new2) {
            pg_close($link);
            die("New passwords do not match.");
        }

        $res = @pg_query($link,"ALTER ROLE $user WITH ENCRYPTED PASSWORD '$new1';");
        if ($res) {
            echo "Password successfully changed!<br>";
        } else {
            echo "Failed to change password...<br>";
        }

        pg_close($link);
    }
?>

它确实按我的预期工作!

It does work just as I expected!

但是我读到,可能会对像这样的表达式进行一些SQL注入攻击,其中SQL查询内部有简单的变量插值。因此,我读到 PREPARE 语句是执行此类查询的最安全方法。我希望这样做:

But I read that there are some SQL Injection Attacks that could be made on expressions like this where there is a simple variable interpolation inside the SQL query. And so I read that PREPARE statements are the safest way to do such queries. I expected to do something like:

pg_prepare($link,"change_user","ALTER ROLE $1 WITH ENCRYPTED PASSWORD '$2';");

但是我遇到语法错误,即使我尝试 PREPARE pgAdminIII中的此命令。实际上, Postgres手册表示 PREPARE 只能准备任何SELECT,INSERT,UPDATE,DELETE或VALUES语句。

But I get a syntax error, even if I try to PREPARE this command in pgAdminIII. Indeed, Postgres manual indicates that PREPARE can only prepare "Any SELECT, INSERT, UPDATE, DELETE, or VALUES statement."

然后我尝试使用 pg_escape_string()函数:

$user = pg_escape_string($_POST["user"]);
...

当我使用普通密码时,如果我尝试设置'' 这样的密码,之后便无法更改。我尝试了 pg_escape_literal 相对于 pg_escape_string 而言,它是首选到手册上,但我的PHP版本是5.3.13,此命令仅适用于5.4.4及更高版本。

This works well when I use normal passwords, and if I try to set a password like ''", I cannot change it afterwards. I have tried pg_escape_literal which is preferred to pg_escape_string according to the manual, but my PHP is version 5.3.13, and this command is only for 5.4.4 onwards.

问题是:这是预防的最佳方法吗?

The question is: is this the best way to prevent injection attacks in this case? Does this really prevent attacks?

另一个问题:如果用户想在密码中使用撇号,是否有可能出现此错误?

Another question: if a user wants to use an apostrophe in his/her password, is it possible without this error?

推荐答案

请注意,我建议为此使用存储过程,如果需要的话,准备好的语句总是可以调用存储的

As a note, I would recommend using a stored procedure for this. A prepared statement if needed could always call the stored procedure.

下面是一些非常简单的示例代码,大部分从LedgerSMB复制(并进行了一些编辑):

Here's some very simple sample code, largely copied from LedgerSMB (and edited a bit):

CREATE OR REPLACE FUNCTION save_user(
    in_username text,
    in_password TEXT
) returns bool
SET datestyle = 'ISO, YMD' -- needed due to legacy code regarding datestyles
AS $$
    DECLARE

        stmt text;
        t_is_role bool;
    BEGIN
        -- WARNING TO PROGRAMMERS:  This function runs as the definer and runs
        -- utility statements via EXECUTE.
        -- PLEASE BE VERY CAREFUL ABOUT SQL-INJECTION INSIDE THIS FUNCTION.

       PERFORM rolname FROM pg_roles WHERE rolname = in_username;
       t_is_role := found;

       IF t_is_role is true and t_is_user is false and in_pls_import is false THEN
          RAISE EXCEPTION 'Duplicate user';
        END IF;

        if t_is_role and in_password is not null then
                execute 'ALTER USER ' || quote_ident( in_username ) ||
                     ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password)
                     || $e$ valid until $e$ ||
                      quote_literal(now() + '1 day'::interval);
        elsif  t_is_role is false THEN
            -- create an actual user
                execute 'CREATE USER ' || quote_ident( in_username ) ||
                     ' WITH ENCRYPTED PASSWORD ' || quote_literal (in_password)
                     || $e$ valid until $e$ || quote_literal(now() + '1 day'::interval);
       END IF;

       return true;

    END;
$$ language 'plpgsql' SECURITY DEFINER;

请注意,这是一个安全定义器函数。通常建议您将此功能设置为非数据库超级用户的用户所有。这样的用户将需要createrole特权并有权访问您要在此处管理的任何表。还要注意警告。如果您没有适当地转义参数,则可以进行数据库内SQL注入。请注意,此功能既可以创建用户,也可以更改密码。此处省略了有关检测用户是否设置以及是否处理用户的LSMB特定部分。

Note that this is a security definer function. It is generally recommended that you set this function to be owned by a user which is not a database superuser. Such a user would need createrole privileges and access to whatever tables you want to manage here. Also note the warning heavily. In-db SQL-injection is possible if you don't appropriately escape your parameters. Note that this function can both create a user and change a password. The LSMB-specific portions regarding detecting whether the user is set up and handling the case if not are omitted here.

这篇关于如何在Postgresql中参数化ALTER ROLE语句?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆