赞
踩
最近公司一个项目进行等保测评过程中,对PostgreSQL数据提出了几点改进要求,如下:
1)未配置密码复杂度策略、口令未定期更换
修改建议:建议密码至少8位,需大小写字母数字、特殊符号两种及以上组成并且不为常见密码,密码有效期90天
2)未配置配置登陆失败处理功能
修改建议:建议配置登录错误6次锁定账户10分钟,超时 10分钟自动退出。
3) 默认账户未重命名
修改建议:建议重命名默认账户
针对以上几条要求,从网上查找资料以及自己的尝试,基本解决了问题。解决的办法记录如下:
(1)登陆navicat,创建查询,建立一个其他超级用户postgres1
- CREATE USER postgres1 WITH
- LOGIN
- SUPERUSER
- INHERIT
- CREATEDB
- CREATEROLE
- REPLICATION;
- ALTER USER postgres1 WITH PASSWORD '你希望设置的密码';
(2)用postgres1登陆navicat,创建查询,修改postgres名称
ALTER USER postgres RENAME TO admins;
用以下语句生成自动延期90天的修改语句
- select $$ alter user admins with password '你希望设置的密码'
- valid until '$$||now()::timestamp(0) + '90 day'||$$';$$;
注意,上面这个语句并没有执行修改操作,只是生成了延期的语句,需要拷贝生成的语句,执行后才会真正修改密码!
(3)删除postgres1
DROP USER postgres1;
这个分两部分,除了判断不常见密码的其他功能,只需要配置postgresql.conf文件,找到shared_preload_librarie行进行修改,添加passwordcheck库的引用即可
- shared_preload_libraries = 'passwordcheck'
-
- 注意去掉编码行之前的#
重启数据库,创建账号测试
- create user abc password 'abc';
- create user abc password 'abc123';
- create user abc password 'aaa123456';
增加常见密码库,参见下方链接,需要重新编译passwordcheck,比较麻烦,如有必要再尝试
【数据库】PostgreSQL增加密码复杂度校验_postgresql14数据库密码策略校验-CSDN博客
这个需要根据PostgreSQL的版本选择处理方式:
14版本及以上:
可直接配置postgresql.conf, 找到idle_session_timeout行进行修改,注意去掉编码行之前的#,数值单位为毫秒
idle_session_timeout = 600000
14版本以下:
需要安装pg_timeout库,安装方法分windows和linux两种方案:
1)Linux 安装pg_timeout
拷贝下载好的pg_timeout源代码文件夹到安装路径下,修改makefile中的PG_CONFIG = /postgreSQL安装路径/bin/pg_config, 命令行执行编译并安装 make | make install 即可
2)Windows安装pg_timeout
步骤一,下载当前使用的PostgreSQL的源代码包,并将pg_timeout源代码文件夹拷贝到PostgreSQL源码的\contrib目录下
步骤二,打开VS提供的命令行工具(注意,你要编译的是X86还是X64需要选择对应名字的命令行工具,参见下图,要与实际在用的库一致别选错了,否则编译出来的pg_timeout不好用)
从命令行进入src\tools\msvc 文件夹下,命令行执行 build.bat 然后耐心等待PostgreSQL编译完成(需要安装VS环境,以及C、C++、windows SDK啥的,才能正常编译源码,这部分不展开讲了,有问题上网查)
步骤三,编译成功后,在release目录下能够找到伴随编译的pg_timeout文件夹,从中拷贝出来编译后的dll文件,拷贝到项目中的库中即可。
另外,如果出现运行插件提示USE_FLOAT8_BYVAL兼容性问题,那么需要修改src\tools\msvc\config_default.pl,增加
float8byval=>1, # --disable-float8-byval, off by default
这样的配置,然后重新编译,再进行尝试。类似的兼容性问题,可能也可以从编译配置上解决。
3)配置pg_timeout
配置/var/lib/pgsql/12/data中的postgresql.conf,找到shared_preload_libraries行进行修改,注意去掉编码行之前的#,多个引用库之间用“,”分隔即可
- shared_preload_libraries = 'pg_timeout'
- pg_timeout.naptime = 30
- pg_timeout.idle_session_timeout = 600
如果不生效,查看postgresql.auto.conf文件对应的shared_preload_libraries是否有覆盖,如果有则删除
如果日志中看不到pg_timeout的记录,可以看一下postgresql.conf中log_min_messages设置是否正确,pg_timeout的日志等级是LOG,正常应该显示
功能逻辑:
Login函数实现锁定和解锁逻辑,会在每次用户登录到“函数所在的库”时,被调用(session_exec触发),调用后会从外部表postgres_log读取日志中的“密码验证失败”记录,插入到t_login表中,然后搜索t_login表中是否某个用户的失败次数大于等于6次,则执行锁定用户操作;如果锁定时间已超过10分钟,则执行解锁操作。
Linux
先说Linux的方案,参考了以下文章PostgreSQL用户登录失败自动锁定的解决办法_postgresql the account has been locked-CSDN博客
步骤一 安装session_exec
拷贝下载好的session_exec-master源代码文件夹到安装路径下,修改makefile中的PG_CONFIG = /pg安装目录/bin/pg_config
命令行执行编译并安装
- make
- make install
如安装失败可能是由于缺少postgresql12-devel,参考https://blog.csdn.net/bendywu/article/details/127745169安装
步骤二 配置postgresql.conf,使用session_exec并生成CSV格式日志
配置postgresql.conf,找到以下几个行进行修改,注意去掉编码行之前的#
- session_preload_libraries='session_exec' #找到并修改
- session_exec.login_name='login'
- #添加到session_preload_libraries行下,后面会写login函数,给这里调用
-
- log_destination = 'csvlog' #找到并修改,修改日志类型为csv,以配合外部表查询
- log_filename = 'postgresql-%a.log' #找到并修改,按星期几生成日志
- log_rotation_age = 1d #找到并修改,按天滚动
-
-
- lc_messages = 'en_US.UTF-8'
- #找到并修改,生产环境如果安装的是中文版,需要改变日志为英文,避免字符编码错误
步骤三 重启数据库以启用新配置
步骤四 进入需要管理的数据库进行后续操作,创建t_login表用于存储提取自数据库日志中登录失败的信息
执行以下语句
- create table t_login
- (
- login_time timestamp(3),
- user_name text,
- flag int4
- );
步骤五 使用file_fdw外部表记录数据库日志信息
执行以下语句
- create extension file_fdw;
- CREATE SERVER pglog FOREIGN DATA WRAPPER file_fdw;
步骤六 建立外部表postgres_log,关联数据库日志中登录失败的信息。
- DROP FOREIGN TABLE postgres_log;
- CREATE FOREIGN TABLE postgres_log(
- log_time timestamp(3),
- user_name text,
- database_name text,
- process_id integer,
- connection_from text,
- session_id text,
- session_line_num bigint,
- command_tag text,
- session_start_time timestamp,
- virtual_transaction_id text,
- transaction_id bigint,
- error_severity text,
- sql_state_code text,
- message text,
- detail text,
- hint text,
- internal_query text,
- internal_query_pos integer,
- context text,
- query text,
- query_pos integer,
- location text,
- application_name text
- ) SERVER pglog
- OPTIONS ( program 'find pg安装目录/data/log -type f -name "*.csv" -mtime -7 -exec cat {} \;', format 'csv' );

注意,这里的表字段不是随便写的,是受postgresql版本影响的,具体有那些字段,可以从这个连接里找PostgreSQL: Documentation: 15: 20.8. Error Reporting and Logging,这个网页最上面可以选版本查看
步骤七 创建登录函数
以下函数需要在希望管理的库中创建
- create or replace function login() returns void as $$
- declare
- lastFail timestamp(3) with time zone;
- uname text;
- locktime timestamp(3) with time zone;
- minuteInv int4;
- begin
- --获取当前日志中最新时间,作为基础从日志中取登录失败记录
- select login_time from public.t_login order by login_time desc limit 1 into lastFail;
- if lastFail IS NULL then
- lastFail = CURRENT_DATE;
- end if;
- raise notice '检测开始时间 %',lastFail;
- --将新的登录失败记录数据插入t_login表
- insert into public.t_login select log_time,user_name from public.postgres_log
- where command_tag='authentication' and error_severity= 'FATAL' and log_time > lastFail;
- update public.t_login set flag = 0 where login_time > lastFail;
- --检查登录失败次数是否大于6,若大于6则锁定用户
- for uname, locktime in select user_name,MAX(login_time) from public.t_login where flag = 0 group by user_name having count(*) >= 6
- loop
- raise notice '%最后登录失败时间 %',uname,locktime;
- SELECT round(cast(date_part('epoch', CURRENT_TIMESTAMP - locktime)/60 as numeric ),1) into minuteInv;
- raise notice 'minuteInv %',minuteInv;
- if minuteInv > 10 then
- --解锁用户
- raise notice '锁定超过10分钟,解锁用户%',uname;
- EXECUTE format('alter user %I login',uname);
- update public.t_login set flag = 1 where user_name = uname;
- else
- --锁定用户
- EXECUTE format('alter user %I nologin',uname);
- --断开当前被锁定用户会话
- EXECUTE 'select pg_catalog.pg_terminate_backend(pid) from pg_catalog.pg_stat_activity where usename=$1' using uname;
- raise notice '累计6次输入错误密码,用户%锁定,10分钟后再尝试!',uname;
- end if;
- end loop;
- end;
- $$ language plpgsql strict security definer set search_path to 'public';

以上实现了登录库时的锁定和解锁操作,但是用户必须得登录login函数所在的库,才能触发login函数,否则login函数不会执行。
不能在多个库中添加login表和t_login表,因为相互之间数据是隔离的,会导致重复锁定。
解决办法是通过使用dblink,在多个库之间共享login函数,参见步骤八
步骤八 其他库关联login方法
在其他库中,执行以下语句,增加dblink扩展。
create extension dblink;
添加login函数,远程调用已有的login方法
- create or replace function login() returns void as $$
- declare
- lname text;
- v_log text;
- a int;
- begin
- lname = CURRENT_TIMESTAMP;
- perform dblink_connect(lname,'dbname=库名 host=localhost port=端口 user=账号 password=密码');
- SELECT * from dblink(lname,'select public.login()') AS T(id TEXT) into v_log;
- perform dblink_disconnect(lname);
- end;
- $$ language plpgsql strict security definer set search_path to 'public';
这样,在用户登录别的库的时候,也会触发锁定和解锁了。
Windows
对于windows版的postgresql,与Linux的不同主要有以下几个步骤:
步骤一 安装session_exec,这里需要参照 《3、超时10分钟自动退出》中的windows编译方式,生成dll文件,放到postgresql安装目录下使用
步骤二 配置postgresql.conf,使用session_exec并生成CSV格式日志
除了Linux段落中提到的修改,还需要修改
- log_filename = 'postgresql.log' #找到并修改
- log_rotation_age = 30d #找到并修改,按月滚动
-
- #整个日志都生成到一个文件内,一个月清理一次,为了配合外部表关联的windows的命令行指令
- #如果你比较熟悉windows命令行,可以进行优化
步骤三、四、五,参见Linux部分
步骤六 建立外部表postgres_log,关联数据库日志中登录失败的信息
这里主要是options中的指令与linux不同,需要注意
- CREATE FOREIGN TABLE postgres_log(
- log_time timestamp(3),
- user_name text,
- database_name text,
- process_id integer,
- connection_from text,
- session_id text,
- session_line_num bigint,
- command_tag text,
- session_start_time timestamp,
- virtual_transaction_id text,
- transaction_id bigint,
- error_severity text,
- sql_state_code text,
- message text,
- detail text,
- hint text,
- internal_query text,
- internal_query_pos integer,
- context text,
- query text,
- query_pos integer,
- location text,
- application_name text,
- backend_type text,
- leader_pid integer,
- query_id bigint
- ) SERVER pglog
- OPTIONS ( filename 'pg安装目录\data\log\postgresql.csv', format 'csv' );

步骤七 创建登录函数
- create or replace function login() returns void as $$
- declare
- lastFail timestamp(3) with time zone;
- uname text;
- locktime timestamp(3) with time zone;
- minuteInv int4;
- begin
- --获取当前日志中最新时间,作为基础从日志中取登录失败记录
- select login_time from public.t_login order by login_time desc limit 1 into lastFail;
- if lastFail IS NULL then
- lastFail = CURRENT_DATE;
- end if;
- raise notice '检测开始时间 %',lastFail;
- --将新的登录失败记录数据插入t_login表
- insert into public.t_login select log_time,user_name from public.postgres_log
- where sql_state_code='28P01' and error_severity= 'FATAL' and log_time > lastFail;
- update public.t_login set flag = 0 where login_time > lastFail;
- --检查登录失败次数是否大于6,若大于6则锁定用户
- for uname, locktime in select user_name,MAX(login_time) from public.t_login where flag = 0 group by user_name having count(*) >= 6
- loop
- raise notice '%最后登录失败时间 %',uname,locktime;
- SELECT round(cast(date_part('epoch', CURRENT_TIMESTAMP - locktime)/60 as numeric ),1) into minuteInv;
- raise notice 'minuteInv %',minuteInv;
- if minuteInv > 10 then
- --解锁用户
- raise notice '锁定超过10分钟,解锁用户%',uname;
- EXECUTE format('alter user %I login',uname);
- update public.t_login set flag = 1 where user_name = uname;
- else
- --锁定用户
- EXECUTE format('alter user %I nologin',uname);
- --断开当前被锁定用户会话
- EXECUTE 'select pg_catalog.pg_terminate_backend(pid) from pg_catalog.pg_stat_activity where usename=$1' using uname;
- raise notice '累计6次输入错误密码,用户%锁定,10分钟后再尝试!',uname;
- end if;
- end loop;
- end;
- $$ language plpgsql strict security definer set search_path to 'public';

其中where sql_state_code='28P01' and error_severity= 'FATAL' 这部分判断条件与linux下不同,可能是postgreSQL版本不一致(linux是pg12,windows是pg14)造成的。
步骤八参见Linux部分
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。