PostgreSQL PL/Perl 钩子安全性分析

背景

plperl 是PostgreSQL支持的函数语言之一。

在使用plperl时,可以使用plperl提供的钩子功能,满足一些特殊场景的需求。

钩子分2种,一种是加载plperl.so库时的钩子,一种是加载perl语言解释器时的钩子。

钩子的使用有安全问题吗?

钩子用法介绍

加载plperl.so库时的钩子

相关参数
plperl.on_init (string)

Specifies Perl code to be executed when a Perl interpreter is first initialized, before it is specialized for use by plperl or plperlu.
The SPI functions are not available when this code is executed.
If the code fails with an error it will abort the initialization of the interpreter and propagate out to the calling query, causing the current transaction or subtransaction to be aborted.

The Perl code is limited to a single string.
Longer code can be placed into a module and loaded by the on_init string. Examples:

plperl.on_init = 'require "plperlinit.pl"'
plperl.on_init = 'use lib "/my/app"; use MyApp::PgInit;'

Any modules loaded by plperl.on_init, either directly or indirectly, will be available for use by plperl.
This may create a security risk.
To see what modules have been loaded you can use:

DO 'elog(WARNING, join ", ", sort keys %INC)' LANGUAGE plperl;

Initialization will happen in the postmaster if the plperl library is included in shared_preload_libraries, in which case extra consideration should be given to the risk of destabilizing the postmaster.
The principal reason for making use of this feature is that Perl modules loaded by plperl.on_init need be loaded only at postmaster start, and will be instantly available without loading overhead in individual database sessions. 

However, keep in mind that the overhead is avoided only for the first Perl interpreter used by a database session — either PL/PerlU, or PL/Perl for the first SQL role that calls a PL/Perl function.
Any additional Perl interpreters created in a database session will have to execute plperl.on_init afresh. Also, on Windows there will be no savings whatsoever from preloading, since the Perl interpreter created in the postmaster process does not propagate to child processes.

This parameter can only be set in the postgresql.conf file or on the server command line.

当设置了 shared_preload_libraries = 'plperl' 预加载时,plperl.on_init 只会被调用一次。

当没有设置 shared_preload_libraries = 'plperl' 预加载时,plperl.on_init 会在每个会话第一次装载plperl.so时被调用。

代码
src/pl/plperl/plperl.c

        /*
         * plperl.on_init is marked PGC_SIGHUP to support the idea that it might
         * be executed in the postmaster (if plperl is loaded into the postmaster
         * via shared_preload_libraries).  This isn't really right either way,
         * though.
         */
        DefineCustomStringVariable("plperl.on_init",
                                                           gettext_noop("Perl initialization code to execute when a Perl interpreter is initialized."),
                                                           NULL,
                                                           &plperl_on_init,
                                                           NULL,
                                                           PGC_SIGHUP, 0,
                                                           NULL, NULL, NULL);

/*
 * Create a new Perl interpreter.
 *
 * We initialize the interpreter as far as we can without knowing whether
 * it will become a trusted or untrusted interpreter; in particular, the
 * plperl.on_init code will get executed.  Later, either plperl_trusted_init
 * or plperl_untrusted_init must be called to complete the initialization.
 */
static PerlInterpreter *
plperl_init_interp(void)
{

...
        if (plperl_on_init && *plperl_on_init)
        {
                embedding[nargs++] = "-e";
                embedding[nargs++] = plperl_on_init;
        }
...

/*
 * _PG_init()                   - library load-time initialization
 *
 * DO NOT make this static nor change its name!
 */
void
_PG_init(void)
{
...
        /*
         * Create the first Perl interpreter, but only partially initialize it.
         */
        plperl_held_interp = plperl_init_interp();
...

plperl.on_init (string) 只能设置在配置文件中,或者在启动postgres时命令行指定。
对只开放普通数据库用户的环境来说没有安全问题。

加载perl语言解释器时的钩子

plperl 函数语言钩子,当在会话中第一次加载perl语言解释器时,perl 函数解释器将自动调用

plperl.on_plperl_init (string)
plperl.on_plperlu_init (string)  

或设置的串。
具体调用哪个,和函数语言有关,plperl则调用on_plperl_init, plperlu则调用on_plperlu_init。

需要注意的是,这两个函数可以在参数中设置,也能在会话中设置,但是在会话中设置的话,如果perl解释器已经加载了,不会触发修改后的值。

另外需要注意on_plperl_init是在plperl安全化后执行的,所以即使在这里配置了不安全的属性,也不怕,因为会直接报错。(与调研plperl的用户权限无关,plperl是不允许执行不安全操作的,例如调研system接口)

这两个参数的解释 :
https://www.postgresql.org/docs/9.5/static/plperl-under-the-hood.html
plperl.on_plperl_init (string)
plperl.on_plperlu_init (string)

These parameters specify Perl code to be executed when a Perl interpreter is specialized for plperl or plperlu respectively. 

This will happen when a PL/Perl or PL/PerlU function is first executed in a database session, or when an additional interpreter has to be created because the other language is called or a PL/Perl function is called by a new SQL role. 

This follows any initialization done by plperl.on_init.
The SPI functions are not available when this code is executed. 

The Perl code in plperl.on_plperl_init is executed after "locking down" the interpreter, and thus it can only perform trusted operations.  

If the code fails with an error it will abort the initialization and propagate out to the calling query, causing the current transaction or subtransaction to be aborted.   

Any actions already done within Perl won't be undone; however, that interpreter won't be used again. If the language is used again the initialization will be attempted again within a fresh Perl interpreter.

Only superusers can change these settings.
Although these settings can be changed within a session, such changes will not affect Perl interpreters that have already been used to execute functions.

代码如下

src/pl/plperl/plperl.c

        DefineCustomStringVariable("plperl.on_plperl_init",
                                                           gettext_noop("Perl initialization code to execute once when plperl is first used."),
                                                           NULL,
                                                           &plperl_on_plperl_init,
                                                           NULL,
                                                           PGC_SUSET, 0,
                                                           NULL, NULL, NULL);

        DefineCustomStringVariable("plperl.on_plperlu_init",
                                                           gettext_noop("Perl initialization code to execute once when plperlu is first used."),
                                                           NULL,
                                                           &plperl_on_plperlu_init,
                                                           NULL,
                                                           PGC_SUSET, 0,
                                                           NULL, NULL, NULL);

src/backend/utils/misc/guc.c
                case PGC_SUSET:
                        if (context == PGC_USERSET || context == PGC_BACKEND)
                        {
                                ereport(elevel,
                                                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                                 errmsg("permission denied to set parameter \"%s\"",
                                                                name)));
                                return 0;
                        }
                        break;

只有超级用户能设置这两个值,普通用户在会话中设置plperl.on_plperl_init时,触发设置则报错。

postgres=> set session plperl.on_plperl_init='';
SET
postgres=> SELECT * FROM test_munge();
WARNING:  42501: permission denied to set parameter "plperl.on_plperl_init"
LOCATION:  set_config_option, guc.c:5794

测试例子

postgresql.conf 参数

#plperl.on_plperlu_init = ' system("touch /home/digoal/t123") '
plperl.on_plperl_init = ' system("touch /home/digoal/t123") '
#plperl.on_init=' system("touch /home/digoal/tttt") '

测试

CREATE TABLE test (
    i int,
    v varchar
);

INSERT INTO test (i, v) VALUES (1, 'first line');
INSERT INTO test (i, v) VALUES (2, 'second line');
INSERT INTO test (i, v) VALUES (3, 'third line');
INSERT INTO test (i, v) VALUES (4, 'immortal');

CREATE OR REPLACE FUNCTION test_munge() RETURNS SETOF test AS $$
    my $rv = spi_exec_query('select i, v from test;');
    my $status = $rv->{status};
    my $nrows = $rv->{processed};
    foreach my $rn (0 .. $nrows - 1) {
        my $row = $rv->{rows}[$rn];
        $row->{i} += 200 if defined($row->{i});
        $row->{v} =~ tr/A-Za-z/a-zA-Z/ if (defined($row->{v}));
        return_next($row);
    }
    return undef;
$$ LANGUAGE plperl;

SELECT * FROM test_munge();

使用 stat touch /home/digoal/t123 查看时间戳的变化
判断是否触发。

plperl和plperlu语言的区别

plperl是trust语言,在创建它的函数时,会监测安全性,例如过滤一些OS操作,等。普通用户和超级用户都可以创建plperl语言的函数。

plperlu则是untruste语言,允许任何操作,只有超级用户能创建plperlu的函数。

如果已经设置了plperl.on_plperl_init是一个不安全的值,则新建plperl函数会报错。

postgres=# show plperl.on_plperl_init;
        plperl.on_plperl_init
-------------------------------------
  system("touch /home/digoal/t123")
(1 row)

postgres=# CREATE OR REPLACE FUNCTION test_munge() RETURNS SETOF test AS $$
    my $rv = spi_exec_query('select i, v from test;');
    my $status = $rv->{status};
    my $nrows = $rv->{processed};
    foreach my $rn (0 .. $nrows - 1) {
        my $row = $rv->{rows}[$rn];
        $row->{i} += 200 if defined($row->{i});
        $row->{v} =~ tr/A-Za-z/a-zA-Z/ if (defined($row->{v}));
        return_next($row);
    }
    return undef;
$$ LANGUAGE plperl;

ERROR:  38000: 'system' trapped by operation mask at line 2.
CONTEXT:  while executing plperl.on_plperl_init
compilation of PL/Perl function "test_munge"
LOCATION:  plperl_trusted_init, plperl.c:1016

调用时,如果触发了不安全的plperl.on_plperl_init,也会报错。

$ vi postgresql.conf
plperl.on_plperl_init = ' system("touch /home/digoal/t123") '
$ pg_ctl reload

postgres=# SELECT * FROM test_munge();
ERROR:  38000: 'system' trapped by operation mask at line 2.
CONTEXT:  while executing plperl.on_plperl_init
compilation of PL/Perl function "test_munge"
LOCATION:  plperl_trusted_init, plperl.c:1016

小结

  1. PostgreSQL将函数语言分为两类,一类是trust的另一类是untrust的。

trust的语言,不允许执行有破坏性的操作,例如系统命令,文件访问等。普通用户可以创建trust语言的函数。
untrust的语言,允许执行任何操作,只有superuser能创建untrust语言的函数。
如果只开放普通数据库用户出去,是没有安全风险的。

  1. PostgreSQL 为 plperl或plperlu语言设置了两种钩子,分别允许在加载libperl.so时被触发(在_PG_init(void)里面实现);

或者在加载perl解释器时被触发,其中加载解释器时又分为两种,plperl和perlu的设置。
用户利用钩子,可以实现一些特殊场景的应用。

  1. 数据库普通用户无法修改钩子参数
#plperl.on_plperlu_init = ' system("touch /home/digoal/t123") '
plperl.on_plperl_init = ' system("touch /home/digoal/t123") '
#plperl.on_init=' system("touch /home/digoal/tttt") '
  1. 即使设置了危险的plperl.on_plperl_init参数,因为这个参数的内容是在plperl函数风险评估后执行的,所以如果有风险也不允许执行,不存在安全风险。
plperl.on_plperl_init = ' system("touch /home/digoal/t123") '
postgres=# SELECT * FROM test_munge();
ERROR:  'system' trapped by operation mask at line 2.
CONTEXT:  while executing plperl.on_plperl_init
compilation of PL/Perl function "test_munge"

综上,PostgreSQL对语言的管理是非常安全的,只要不随意把超级用户放出去,不随意使用untrust语言创建不安全的函数。

时间: 2024-08-22 07:43:31

PostgreSQL PL/Perl 钩子安全性分析的相关文章

Mssql和Mysql的安全性分析

mysql|安全|安全性 数据库是电子商务.金融以及ERP系统的基础,通常都保存着重要的商业伙伴和 客户信息.大多数企业.组织以及政府部门的电子数据都保存在各种数据库中,他们 用这些数据库保存一些个人资料,还掌握着敏感的金融数据.但是数据库通常没有象 操作系统和网络这样在安全性上受到重视.数据是企业,组织的命脉所在,因此选择 一款安全的数据库是至关重要的.大型网站一般使用oracle或DB2,而中小型网站大 多数使用更加灵活小巧的mssql数据库或者mysql数据库.那么,在同样的条件下,微 软

安全技术—RSA公钥密码体制安全性分析_漏洞研究

 引言   RSA密码系统是较早提出的一种公开钥密码系统.1978年,美国麻省理工学院(MIT)的Rivest,Shamir和Adleman在题为<获得数字签名和公开钥密码系统的方法>的论文中提出了基于数论的非对称(公开钥)密码体制,称为RSA密码体制.RSA是建立在"大整数的素因子分解是困难问题"基础上的,是一种分组密码体制.   介绍公钥密码体制(背景)   1.对称密码体制   对称密码体制是一种传统密码体制,也称为私钥密码体制.在对称加密系统中,加密和解密采

您好!我最近要写一偏论文,是电子商务的环境安全性分析。不知道从哪些方面写,请大家帮帮我!

问题描述 您好!我最近要写一偏论文,是电子商务的环境安全性分析.不知道从哪些方面写,请大家帮帮我! 解决方案 解决方案二:ding!解决方案三:写完了么?

PHP临时文件的安全性分析_php技巧

一.简介 临时文件,顾名思义是临时产生的文件,且文件的生命周期很短. 然而,很多应用的运行都离不开临时文件,临时文件在我们电脑上无处不在,主要有以下几种形式的临时文件: 1.文件或图形编辑程序,所生成的中间文件 2.数据库查询时,生成的临时缓存文件,提供之前的结果数据而,以减少再次访问数据库的代价:通常用于远程数据库或远程xml的服务 3.文件被上传后在服务端的临时储存,其文件名为php的全局变量$_FILES['userfile']['tmp_name']的值 4.在http请求中,用于存放s

OpenStack安全性分析:开源云软件的利与弊

有人要求我对OpenStack开源云计算平台进行简要的安全性分析,并简要分析我们的企业是否应该把开源云计算平台作为云基础设施建设的基础去追求.我的初步评估是,像Apache和 Linux,平台的开放性让我们能迅速发现和修复安全漏洞,这可以降低溢出的可能性.你们同意吗?还有其他值得考虑的OpenStack安全点(利与弊)吗? OpenStack是一个倡导执行简便.功能丰富.扩展性强的基础设施即服务云计算平台.于2010年7月由托管供应商Rackspace有限公司和NASA发布,它由构建云基础设施解

云存储访问控制方案的安全性分析与改进

云存储访问控制方案的安全性分析与改进 王冠 范红 杜大海 对Tang等(TANG Y,LEE P,LUI J,et al. Secure overlay cloud storage with access control and assured deletion. IEEE Transactions on Dependable and Secure Computing,2012,9(6):903-916)提出的一种云存储的细粒度访问控制方案进行安全性分析,发现其存在不能抵抗合谋攻击的问题,并给出

云计算安全性分析

云计算安全性分析 李山   李娜 随着用户对云计算使用量的增多,这就对云计算的安全性提出了更多的要求,一旦云计算出现安全问题,将会给用户带来巨大的损失.本文基于此背景,首先分析了当前云计算中所应用的安全技术,然后详细的分析了云计算所面临的安全风险,最后再针对性的提出了几点对策建议,帮助解决云计算安全问题. 云计算安全性分析

[文档]基于P2P的云计算模型及其安全性分析

基于P2P的云计算模型及其安全性分析 沈静波  李金龙 文章通过将P2P技术和云计算相结合,提出了一种可靠的.可扩展的.低成本的文件共享模型PA-Cloud.该文介绍了PA-Cloud的文件下载和更新过程,并对系统的安全性进行了分析.通过对PA-Cloud进行对比实验表明,PA-Cloud可以有效提高系统的可扩展性,降低服务提供商的成本. 关键词: 对等网络 云计算 文件共享 可扩展性   temp_12060215109101.pdf

用IIS+ASP建网站的安全性分析

iis|安全|安全性|iis|安全|安全性    随着Internet的发展,Web技术日新月异,人们已经不再满足于静态HTML技术,更多的是要求动态.交互的网络技术.继通用网关接口(CGI)之后,微软推出的IIS+ASP的解决方案作为一种典型的服务器端网页设计技术,被广泛应用在网上银行.电子商务.网上调查.网上查询.BBS.搜索引擎等各种互联网应用中.与此同时,Access数据库作为微软推出的以标准JET为引擎的桌面型数据库系统,由于具有操作简单.界面友好等特点,具有较大的用户群体.目前,II