PostgreSQL触发器使用实践——数据审计

1、说明

PostgreSQL中审计可以通过自带的审计日志功能来实现,但是不足指出在于只能实现语句级别,数据库级别,用户级别的审计,审计的颗粒度太大。

因此,我们可以使用触发器来实现粒度更细的审计,例如:级别,行级别(带条件的),用户级别的数据库操作。但是不建议在数据库中大量使用触发器来审计,因为此方法开销较大。


2、使用场景

2.1、用户信息审计

我们使用触发器来记录:谁改变了数据、数据什么使用被改、什么操作改变了数据、被改变前后的数据分别是什么。

首先,创建表audit_log来记录相关的信息:


bill=# CREATE TABLE audit_log

bill-# (username text, -- who did the change 

bill(# event_time_utc timestamp, -- when the event was recorded 

bill(# table_name text, -- contains schema-qualified table name 

bill(# operation text, -- INSERT, UPDATE, DELETE or TRUNCATE 

bill(# before_value json, -- the OLD tuple value 

bill(# after_value json -- the NEW tuple value 

bill(# );

CREATE TABLE

1

2

3

4

5

6

7

8

9

简单说明下:


username即记录哪个用户修改了数据,可以通过session_user来获取;

event_time_utc用来记录数据被修改的时间;

table_name即被修改的表名;

operation表示数据被什么操作修改的;

before_value和after_value分别表示被修改前后的数据。

接着我们要创建触发器函数:


bill=# CREATE OR REPLACE FUNCTION audit_trigger() RETURNS trigger AS $$ 

bill$#  DECLARE 

bill$#   old_row json := NULL; 

bill$#   new_row json := NULL; 

bill$#  BEGIN

bill$#   IF TG_OP IN ('UPDATE','DELETE') 

bill$#   THEN

bill$#     old_row = row_to_json(OLD);

bill$#   END IF; 

bill$#   IF TG_OP IN ('INSERT','UPDATE') 

bill$#   THEN

bill$#     new_row = row_to_json(NEW);

bill$#     END IF; 

bill$#   INSERT INTO audit_log(username, event_time_utc, table_name, operation, before_value, after_value ) 

bill$#   VALUES (session_user, current_timestamp AT TIME ZONE 'UTC', TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, TG_OP, old_row, new_row ); 

bill$#   RETURN NEW; 

bill$#   END;

bill$#    $$ LANGUAGE plpgsql; 

CREATE FUNCTION

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

最后我们再创建对应的表和触发器即可:


bill=# create table notify_test(c1 int);

CREATE TABLE


bill=# create trigger audit_log

bill-#   after insert or update or delete 

bill-#   on notify_test

bill-#   for each row

bill-#   execute procedure audit_trigger();

CREATE TRIGGER

1

2

3

4

5

6

7

8

9

验证:

在audit_log表中记录了相关的信息。


bill=# insert into notify_test values(1);

INSERT 0 1

bill=# update notify_test set c1=2;

UPDATE 1

bill=# delete from notify_test ;

DELETE 1

bill=# select * from audit_log ;

 username |       event_time_utc       |     table_name     | operation | before_value | after_value 

----------+----------------------------+--------------------+-----------+--------------+-------------

 bill     | 2020-05-11 02:42:01.500445 | public.notify_test | INSERT    |              | {"c1":1}

 bill     | 2020-05-11 02:42:25.211593 | public.notify_test | UPDATE    | {"c1":1}     | {"c1":2}

 bill     | 2020-05-11 02:42:31.0065   | public.notify_test | DELETE    | {"c1":2}     | 

(3 rows)

1

2

3

4

5

6

7

8

9

10

11

12

13

2.2、禁止delete

某些重要的表中,我们可能只会允许用户去插入和更新数据,不允许用户删除数据,那么我们也可以使用触发器来实现。

和上面的例子类似,首先我们要创建一个触发器函数:


bill=# CREATE OR REPLACE FUNCTION cancel_op() RETURNS TRIGGER AS $$ 

bill$# BEGIN 

bill$#     IF TG_WHEN = 'AFTER' THEN

bill$#     RAISE EXCEPTION 'YOU ARE NOT ALLOWED TO % ROWS IN %.%', TG_OP, TG_TABLE_SCHEMA, TG_TABLE_NAME;

bill$#     END IF; RAISE NOTICE '% ON ROWS IN %.% WON"T HAPPEN', TG_OP, TG _ABLE_SCHEMA, TG_TABLE_NAME; 

bill$#     RETURN NULL; 

bill$#     END; 

bill$#     $$ LANGUAGE plpgsql; 

CREATE FUNCTION

1

2

3

4

5

6

7

8

9

接着创建对应的触发器:


bill=# create TRIGGER disallow_delete AFTER delete on notify_test

bill-#   for each row execute procedure cancel_op();

CREATE TRIGGER

1

2

3

然后我们可以验证了:


bill=# delete from notify_test where c1 = 1;

psql: ERROR:  YOU ARE NOT ALLOWED TO DELETE ROWS IN public.notify_test

CONTEXT:  PL/pgSQL function cancel_op() line 4 at RAISE

bill=# select * from notify_test ;          

 c1 

----

  1

(1 row)

1

2

3

4

5

6

7

8

2.3、禁止truncate

前面的例子中我们实现了禁止从某张表中delete数据,但是实际上我们可以直接truncate表来删除数据,所以我们想要禁止删除某张表的数据,还得加上禁止truncate的触发器才行。


bill=# create TRIGGER disallow_truncate AFTER truncate on notify_test

bill-#   for each statement execute procedure cancel_op();

CREATE TRIGGER

1

2

3

需要注意的是,因为truncate是针对整张表的,所以不能使用for each row。

验证:


bill=# truncate notify_test ;

psql: ERROR:  YOU ARE NOT ALLOWED TO TRUNCATE ROWS IN public.notify_test

CONTEXT:  PL/pgSQL function cancel_op() line 4 at RAISE

1

2

3

2.4、带有条件的触发器

某些情况下,我们的审计策略可能是这样的:对于某张表,我们在某一时间段不允许进行更新或者删除操作。那么这种情况我们需要创建的触发器必须得指定相应的条件。

例如前面禁止delte的例子,我们现在要求是不允许在周一上午delte这张表的数据,其它时间段可以。

首先我们还是得创建触发器函数,同上即可。

接下来关键就是我们要创建的带有条件的触发器:


bill=# drop trigger no_delete_on_monday_morning ON t1;

DROP TRIGGER

bill=# create TRIGGER no_delete_on_monday_morning

bill-#   after delete on t1 

bill-#   for each row

bill-#   when (current_time < '12:00' and extract(DOW from current_timestamp) = 1)

bill-#   execute procedure cancel_op(); 

CREATE TRIGGER

郑州不孕不育:http://www.ytsg120.cn/

查看当前时间:现在是周一上午,满足触发器的条件。


bill=# select current_timestamp;

       current_timestamp       

-------------------------------

 2020-05-11 11:21:10.339308+08

(1 row)

郑州市不孕不育医院:http://www.03913882333.com/

我们来验证下:无法删除数据。


bill=# delete from t1 where id=1;

psql: ERROR:  YOU ARE NOT ALLOWED TO DELETE ROWS IN public.t1

CONTEXT:  PL/pgSQL function cancel_op() line 4 at RAISE

郑州市不孕不育专科医院:http://www.zzchbb.com/

3、总结

对于数据库的审计,我们可以使用触发器来实现更细的粒度。但是我们实际应用中还是应该避免大量使用触发器来实现类似的功能,因为开销较大,并且可能会导致某些难以调试的问题。

————————————————

版权声明:本文为CSDN博主「foucus、」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_39540651/article/details/106050483


猜你喜欢

转载自blog.51cto.com/14510351/2494678
今日推荐