云主机安全加固

1 用户安全策略调整

1.1 限制root登陆和 SSH登陆超时设置

1
2
3
4
5
6
7
8
9
10
11
# 创建用户
useradd ghjs
# 设置密码
passwd ghjs
# 加入wheel组(sudo)
usermod -G wheel ghjs
# 限制root登陆/etc/ssh/sshd_config
PermitRootLogin no
# 第一行表示每60秒检测一次,第二行表示检测到5次不活动就断开连接
ClientAliveInterval 60
ClientAliveCountMax 5

1.2 普通用户口令锁定策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 密码锁定策略/etc/pam.d/sshd
# 添加下边一行策略------------------------------
auth required pam_tally2.so onerr=fail deny=6 unlock_time=300

-----------补充说明------------------
# onerr=fail 表示定义了当出现错误时的缺省返回值;
# deny 表示设置普通用户和root用户连续错误登陆的最大次数,超过最大次数,则锁定该用户;
# unlock_time 表示设定普通用户锁定后,多少时间后解锁,单位是秒;
# even_deny_root 表示也限制root用户;
# root_unlock_time 表示设定root用户锁定后,多少时间后解锁,单位是秒;
# 锁定用户
passwd -l $user
# 查看用户状态
passwd -S $user
# 解锁用户
passwd -u $user

1.3 口令生存周期(维护人员使用的账号)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 表示将此用户的密码最长使用天数设为180,最短使用天数设为0,过期前15天警告用户。
chage -d 0 -m 0 -M 90 -W 15 ghjs
# 查看账号过期时间
chage -l ghjs
------------
[root@localhost ghjs]# chage -l ghjs
最近一次密码修改时间 :密码必须更改
密码过期时间 :密码必须更改
密码失效时间 :密码必须更改
帐户过期时间 :从不
两次改变密码之间相距的最小天数 :0
两次改变密码之间相距的最大天数 :1
在密码过期之前警告的天数 :15
------------
选项:
-d, --lastday 最近日期 将最近一次密码设置时间设为“最近日期”
-E, --expiredate 过期日期 将帐户过期时间设为“过期日期”
-h, --help 显示此帮助信息并推出
-I, --inactive INACITVE 过期 INACTIVE 天数后,设定密码为失效状态
-l, --list 显示帐户年龄信息
-m, --mindays 最小天数 将两次改变密码之间相距的最小天数设为“最小天数”
-M, --maxdays MAX_DAYS set maximum number of days before password
change to MAX_DAYS
-R, --root CHROOT_DIR chroot 到的目录
-W, --warndays 警告天数 将过期警告天数设为“警告天数”

1.4 口令重复次数限制

1
2
3
# /etc/pam.d/system-auth
# 禁止过去使用5次的密码,大概15行位置末尾添加 remember=5
password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok remember=5

1.5 空密码账号

1
2
3
# 空密码账号检查
awk -F: '($2=="")' /etc/shadow
# 查看UID为0的账号:awk -F: '($3==0)' /etc/passwd,确认UID为0的账号只有root

1.6 无关账号删除或禁用

1.7 关闭telnet服务,默认没装

2 服务安全漏洞修复

2.1系统ssl+ssh

TLS protocol中间人攻击漏洞(CVE-2015-4000)

处理方案

升级openssl版本3.0.3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 安装gcc
yum install gcc -y
# 安装perl 5 (安装openssl会提示缺少)
tar -zxvf perl-5.28.0.tar.gz
cd perl-5.28.0
./configure.gnu -des -Dprefix=$HOME/localperl
make
make test
make install
# 安装openssl
tar -zxvf openssl-3.0.3.tar.gz
cd openssl-3.0.3
./config --prefix=/usr/local/openssl/
make
make install
# 卸载历史openssl
yum erase -y openssl openssl-devel
ln -sf /usr/local/openssl/bin/openssl /usr/bin/openssl
# 查看版本报错 openssl version
openssl: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory
# 创建so文件软连接
ln -s /usr/local/openssl/lib/libssl.so.3 /usr/lib64/libssl.so.1.1
ln -s /usr/local/openssl/lib/libcrypto.so.3 /usr/lib64/libcrypto.so.1.1

OpenSSH用户枚举(CVE-2018-15473)【POC】

OpenSSH <7.5

OpenSSH sshd拒绝服务漏洞(CVE-2016-10708)

OpenSSH设计漏洞(CVE-2017-15906)

OpenSSH用户枚举漏洞(CNVD-2018-20962)(CVE-2018-15919)

OpenSSH 命令注入漏洞

OpenSSH CBC模式信息泄露漏洞(CVE-2008-5161)

处理方案

升级openssh到8.9p1版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 备注:先升级openssl再升级openssh
# 警告:升级期间一定不要断开ssh窗口连接,否则无法重新连接,提前建立ssh和sftp连接。(高风险)
# 1) 关闭sshd
systemctl stop sshd
# 2) 备份旧版配置文件
mv /etc/ssh /etc/ssh.bak
# 3) 卸载旧版openssh
rpm -qa | grep openssh-*
yum erase -y openssh-clients openssh-server openssh
# 4) 完成卸载
ssh -V
无版本输出
# 5) 安装依赖
yum install -y pam-devel zlib zlib-devel openssl-devel
# 6) 安装openssh
tar -zxvf openssh-8.9p1.tar.gz
cd openssh-8.9p1
# --with-ssl-dir 指向openssl的安装目录,否则报错configure: error: *** working libcrypto not found
./configure --prefix=/usr/ --sysconfdir=/etc/ssh --with-openssl-includes=/usr/local/include --with-ssl-dir=/usr/local/openssl --with-zlib --with-md5-passwords --with-pam
make
make install
ssh -V # 确定openssh和openssl为安装版本
# 7) 配置服务及启动
cp -a openssh-8.9p1/contrib/redhat/sshd.init /etc/init.d/sshd
chmod +x /etc/init.d/sshd
chkconfig --add sshd
systemctl start sshd
systemctl status sshd
systemctl enable sshd
chkconfig sshd on
# 8) 修改sshd_config配置,否则无法登陆
vim /etc/ssh/sshd_config
添加如下配置
---------------
# 允许密码登录
PasswordAuthentication yes
# 允许root远程登录
PermitRootlogin yes
# 解决此报错信息 error: Could not get shadow information for <user>
UsePAM yes
---------------
# 9) 修改/etc/pam.d/sshd文件,否则无法登陆
vim /etc/pam.d/sshd

文件内容如下
-----------------------------------------------
# %PAM-1.0
auth required pam_sepermit.so
auth substack password-auth
auth include postlogin
# Used with polkit to reauthorize users in remote sessions
-auth optional pam_reauthorize.so prepare
account required pam_nologin.so
account include password-auth
password include password-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open env_params
session required pam_namespace.so
session optional pam_keyinit.so force revoke
session include password-auth
session include postlogin
# Used with polkit to reauthorize users in remote sessions
-session optional pam_reauthorize.so prepare
-----------------------------------------------
# 10) 重启sshd
systemctl restart sshd
# 11) 验证ssh登陆

image-20220622113703460

启用 SSH 弱密钥交换算法

SSH SHA-1 HMAC 算法已启用

处理方案

1
2
3
4
5
# sshd_config添加如下配置
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,arcfour
MACs hmac-sha2-256,hmac-sha2-256-etm@openssh.com
# 重启sshd
systemctl restart sshd

2.2 nginx

HTTP/2实现安全漏洞(CVE-2019-9516)

nginx拒绝服务漏洞(CNVD-2016-00982)(CVE-2016-0746)

HTTP/2实现安全漏洞(CVE-2019-9511)

nginx拒绝服务漏洞(CNVD-2018-22805)(CVE-2018-16843)

HTTP/2实现安全漏洞(CVE-2019-9513)

nginx 安全漏洞(CVE-2021-23017)

nginx拒绝服务漏洞(CVE-2016-0747)

nginx ngx_http_mp4_module组件内存泄露漏洞(CVE-2018-16845)

Nginx 环境问题漏洞(CVE-2019-20372)

处理方案

升级nginx到1.21.6版本


Web服务器错误页面信息泄露

Web服务器HTTP头信息公开

处理方案

1
2
3
4
5
6
# nginx配置
server {
listen 8080;
...
server_tokens off; #添加
}

image-20220622111333256


HTTP响应头使用X-XSS-Protection

处理方案

1
2
3
4
5
6
7
8
9
# nginx.conf添加如下配置
http {
add_header X-Xss-Protection "1; mode=block";
...
}

• 0:禁用XSS保护;
• 1:启用XSS保护;
• 1; mode=block:启用XSS保护,并在检查到XSS攻击时,停止渲染页面(例如IE8中,检查到攻击时,整个页面会被一个#替换)

image-20220622112226841


HTTP响应头部使用X-Frame-Options

HTTP响应头X-Content-Options:nosniff

处理方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# nginx.conf配置添加
# 该配置取消,影响页面嵌套
http {
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;

...
}

# 赋值有如下三种:

(1)DENY:不能被嵌入到任何iframe或frame中。

(2)SAMEORIGIN:页面只能被本站页面嵌入到iframe或者frame中。

(3)ALLOW-FROM uri:只能被嵌入到指定域名的框架中。

image-20220622114310329


2.3 docker

Docker Remote API 未授权访问漏洞【POC】

处理方案:

1
2
# docker启动命令配置修改只允许本机访问
-H tcp://127.0.0.1:4243

image-20220622114832945

2.4 tomcat

Apache Tomcat 代码问题漏洞(CVE-2020-9484)

Apache Tomcat 8.5.38 < 8.5.79 安全漏洞

Apache Tomcat 安全漏洞(CVE-2020-13943)

Apache Tomcat servlet / JSP容器默认文件

Apache Tomcat 8.5.0 < 8.5.68 漏洞

Apache Tomcat HTTP / 2请求标头混淆漏洞(CVE-2020-17527 )

更新到8.5.81版本

Apache ServerToken的信息披露

Apache HTTP Server版本

https://blog.51cto.com/meiling/2069452

https://wenku.baidu.com/view/0792e11e463610661ed9ad51f01dc281e53a56b1.html

1
2
3
在tomcat部署⽬录下lib中新建⽂件夹
/org/apache/catalina/util/
并在util中新建在 ServerInfo.properties⽂件,⽂件添加 server.info=Apache Tomcat

2.5 mysal

暂不处理

2.6 apache

Apache HTTP Server 安全漏洞(CVE-2021-33193)

Apache 2.4.x < 2.4.47 多个漏洞

Apache 2.4.x < 2.4.53 多个漏洞

Apache HTTP Server 代码问题漏洞(CVE-2021-40438)

升级到2.4.54版本

隐藏版本信息

2.7 consul

Hashicorp Consul Web UI和API访问

处理方案:

容器禁止端口暴漏,通过容器名连接。

2.8 mapserv

不存在的页面(404)物理路径披露

jQuery跨站脚本漏洞(CNVD-2019-11839)(CVE-2019-11358)

更新gdw.lua

移除mapdata/client目录

2.9 redis

Redis服务器不受密码认证的保护【POC】

配置redis密码验证。

3 日志审计

3.1 postgresql开启日志审计

1、审计清单说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
logging_collector   # 是否开启日志收集开关,默认off,推荐on

log_destination -- 日志记录类型,默认是stderr,只记录错误输出,推荐csvlog,总共包含:`stderr, csvlog, syslog, and eventlog,`

log_directory # 日志路径,默认是$PGDATA/pg_log,

log_filename # 日志名称,默认是postgresql-%Y-%m-%d_%H%M%S.log

log_file_mode # 日志文件类型,默认为0600

log_truncate_on_rotation # 默认为off,设置为on的话,文件内容覆盖方式:off后面附加,on:清空再加

log_rotation_age # 保留单个文件的最大时长,默认是1d,也有1h,1min,1s

log_rotation_size # 保留单个文件的最大尺寸,默认是10MB

log_error_verbosity # 默认为default,verbose表示冗长的

log_connections # 用户session登陆时是否写入日志,默认off,推荐为on

log_disconnections # 用户session退出时是否写入日志,默认off,推荐为on

log_statement --`记录用户登陆数据库后的各种操作`

1. none,即不记录
2. ddl(记录create,drop和alter)
3. mod(记录ddl+insert,delete,update和truncate)
4. all(mod+select)

log_min_duration_statement = 2s -- `记录超过2秒的SQL`
log_checkpoints = on # 检查点和重启点被记录再服务器日志中。一些统计信息也被包括再日志消息中,包括写入缓冲区的数据和写他们呢所花的时间。这个参数智能在postgresql.conf文件中或在服务器命令行上设置。默认是关闭(off)
log_lock_waits = on # 如果一个会话等待某个类型的锁的时间超过deadlock_timeout的值,该参数决定是否在数据库日志中记录这个信息。
deadlock_timeout = 1s # 数据库的锁通常可以在pg_locks这个系统表里找,但这只是当前的锁表/行信息,如果你想看一天内有多少个超过死锁时间的锁发生,可以在日志里设置并查看,log_lock_waits 默认是off,可以设置开启。这个可以区分SQL慢是资源紧张还是锁等待的问题。

2、推荐的设置参数

1
2
3
4
5
6
7
8
9
10
11
12
13
logging_collector = on
log_destination = 'csvlog'
log_truncate_on_rotation = on
log_connections = on
log_disconnections = on
log_error_verbosity = verbose
log_statement = ddl

# 这部分是postgresql.conf文件中没有的
log_min_duration_statement = 60s # 记录超过60s的SQL
log_checkpoints = on
log_lock_waits = on
deadlock_timeout = 1s
1
2
3
# 查看日志目录和日志文件名
show log_directory;
show log_filename;

3、参数修改方法

直接修改配置文件

postgresql.conf默认位于$PGDATA目录下。

vim /usr/data/pgsql/data/postgresql.conf

用超级用户运行:postgres=# SELECT pg_reload_conf();

show命令可以查询当前状态

4、 参考文档

http://t.zoukankan.com/tiandi-p-13568675.html

3.2 postgresql设置密码复杂度校验

3.2.1 默认密码校验策略

在PG中可以使用passwordcheck.so模块实现密码复杂度校验的功能,不过这个工具的默认要求很低,只可用于简单的密码复杂度校验,默认检查规则如下:

  1. 密码长度大于8位
  2. 密码不能与用户名相同
  3. 密码必须包括字母和数字

3.2.1.1 passwordcheck密码校验

安装完PostgreSQL之后,默认是没有开启密码复杂度,为了数据库安全以及应对等保测评等要求,有时我们需要设置密码复杂度。

PostgreSQL支持通过动态库的方式扩展PG的功能,pg在使用这些功能时需要预加载相关的共享库。而密码复杂度可以通过预加载passwordcheck.so模块实现。

有几种设置可用于将共享库预加载到服务器中,如下:

  • local_preload_libraries (string)
  • session_preload_libraries (string)
  • shared_preload_libraries (string)

下面介绍shared_preload_libraries (string)方式加载passwordcheck.so模块,此模块可以检查密码,如果密码太弱,他会拒绝连接;创建用户或修改用户密码时,强制限制密码的复杂度,限制密码不能重复使用例如密码长度,包含数字,字母,大小写,特殊字符等,同时排除暴力破解字典中的字符串。

3.2.1.2 shared_preload_libraries 方式启用passwordcheck.so模块

在PG库的数据目录下(centos默认路径为:/var/lib/pgsql/11/data,windows默认路径为:D:\PostgreSQL\11\data)找到postgresql.conf文件,修改

修改内容行为:shared_preload_libraries = 'passwordcheck' # (change requires restart)。

修改完成后重启服务服务生效(systemctl restart postgresql-11)。

imageimage

3.2.1.3 验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 提示密码太短
postgres=# create role ttt with password '123123';
ERROR: password is too short

# 提示密码必须包含字母和非字母
postgres=# create role ttt with password '12345678';
ERROR: password must contain both letters and nonletters

postgres=# create role ttt with password 'qweqweqwe';
ERROR: password must contain both letters and nonletters

# 提示密码不能包含用户名
postgres=# create role tttt with password 'tttt123456';
ERROR: password must not contain user name

3.2.2 增强密码校验策略

通过修改源代码实现。本文记录如何通过修改源码passwordcheck.c达到增强复杂度检验的目的,修改后验证规则如下:

  1. 密码长度大于8位
  2. 密码不能与用户名相同
  3. 密码必须包括字母
  4. 密码必须包括数字
  5. 密码必须包括特殊字符

3.2.2.1 步骤

3.2.2.1.1 实验环境

实验环境:CentOS7.6 + PG11.8 source code

源码下载地址: https://www.postgresql.org/ftp/source/v11.8/postgresql-11.8.tar.gz

源码安装文档:https://www.postgresql.org/docs/11/install-short.htm

3.2.2.1.2 使用方式
  • 替换目录 ../postgresql-11.4/contrib/passwordcheck 下的 passwordcheck.c
  • 编译安装 make && make install
  • postgresql配置文件内修改 (postgresql.conf)
  • shared_preload_libraries = ‘passwordcheck’
  • passwordcheck.level = ‘true’
3.2.2.1.3 效果

当密码长度足够,不符合规则的时候,无法新建用户

3.2.2.1.4 源码修改

将下载后的源码解压缩, 找到postgresql-11.8/contrib/passwordcheck/passwordcheck.c源文件,修改后保存退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*-------------------------------------------------------------------------
* Luckyness
* 20191202
* 在源代码上修改自用,配置pg密码必须包含特殊字符
* pg版本11.4
* 使用方式:
* 替换目录 ../postgresql-11.4/contrib/passwordcheck 下的 passwordcheck.c
* 编译安装 make && make install
* postgresql配置文件内修改 (postgresql.conf)
* shared_preload_libraries = 'passwordcheck'
* passwordcheck.level = 'true'
*-------------------------------------------------------------------------
*/
/*-------------------------------------------------------------------------
*
* passwordcheck.c
*
*
* Copyright (c) 2009-2018, PostgreSQL Global Development Group
*
* Author: Laurenz Albe <laurenz.albe@wien.gv.at>
*
* IDENTIFICATION
* contrib/passwordcheck/passwordcheck.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"

#include <ctype.h>

#ifdef USE_CRACKLIB
#include <crack.h>
#endif

#include "commands/user.h"
#include "libpq/crypt.h"
#include "fmgr.h"
/* 引入扩展 */
#include "utils/guc.h"


PG_MODULE_MAGIC;

/*
* 配置文件内passwordcheck.level='true' 为需要特殊字符
* passwordcheck.level='false' 为只需要英文和数字
*/
static bool passwordcheck_level = false;


/* passwords shorter than this will be rejected */
#define MIN_PWD_LENGTH 8

extern void _PG_init(void);

/*
* check_password
*
* performs checks on an encrypted or unencrypted password
* ereport's if not acceptable
*
* username: name of role being created or changed
* password: new password (possibly already encrypted)
* password_type: PASSWORD_TYPE_* code, to indicate if the password is
* in plaintext or encrypted form.
* validuntil_time: password expiration time, as a timestamptz Datum
* validuntil_null: true if password expiration time is NULL
*
* This sample implementation doesn't pay any attention to the password
* expiration time, but you might wish to insist that it be non-null and
* not too far in the future.
*/
static void

check_password(const char *username,
const char *shadow_pass,
PasswordType password_type,
Datum validuntil_time,
bool validuntil_null)
{
if (password_type != PASSWORD_TYPE_PLAINTEXT)
{
/*
* Unfortunately we cannot perform exhaustive checks on encrypted
* passwords - we are restricted to guessing. (Alternatively, we could
* insist on the password being presented non-encrypted, but that has
* its own security disadvantages.)
*
* We only check for username = password.
*/
char *logdetail;

if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("password must not equal user name")));
}
else
{
/*
* For unencrypted passwords we can perform better checks
*/
const char *password = shadow_pass;
int pwdlen = strlen(password);
int i;
/* bool pwd_has_letter,*/
bool
pwd_has_number,pwd_has_special,pwd_has_letter;

/* enforce minimum length */
if (pwdlen < MIN_PWD_LENGTH)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("password is too short")));

/* check if the password contains the username */
if (strstr(password, username))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("password must not contain user name")));

if(passwordcheck_level)
{
/* check if the password contains both letters and number and specialchar */
pwd_has_number = false;
pwd_has_special = false;
pwd_has_letter = false;
for (i = 0; i < pwdlen; i++)
{
if (isalpha((unsigned char) password[i]))
pwd_has_letter = true;
else if (isdigit((unsigned char) password[i]))
pwd_has_number = true;
else
pwd_has_special = true;
}
if (!pwd_has_number || !pwd_has_letter || !pwd_has_special)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("password must contain both letters and number and specialchar")));
}
else
{
/* check if the password contains both letters and non-letters */
pwd_has_letter = false;
pwd_has_number = false;
for (i = 0; i < pwdlen; i++)
{
if (isalpha((unsigned char) password[i]))
pwd_has_letter = true;
else
pwd_has_number = true;
}
if (!pwd_has_letter || !pwd_has_number)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("password must contain both letters and nonletters")));
}

#ifdef USE_CRACKLIB
/* call cracklib to check password */
if (FascistCheck(password, CRACKLIB_DICTPATH))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("password is easily cracked")));
#endif
}

/* all checks passed, password is ok */
}

/*
* Module initialization function
*/
void
_PG_init(void)
{
/* 密码级别参数 */
DefineCustomBoolVariable(
"passwordcheck.level",
gettext_noop("passwordcheck_level true: Password must contain leter, number, special characters;false : Password must contain leter, special characters"),
NULL,
&passwordcheck_level,
false,
PGC_POSTMASTER,
GUC_SUPERUSER_ONLY,
NULL, NULL, NULL);

/* activate password checks when the module is loaded */
check_password_hook = check_password;
}
3.2.2.1.5 编译
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
##编译安装pg server

##进入源码解压目录, 执行

cd postgresql-11.8

./configure--prefix=/u01/pgsql11.8 --without-zlib --without-readline -->>>>指定安装目录为/u01/pgsql11.8, 处于演示目的,所以没有安装zlib和readline包。

make

make install

##编译安装contrib,是一些第三方组织贡献出来的工具代码。

##进入源码解压目录执行

cd postgresql-11.8/contrib

make

make install

##编译完成后,在postgresql-11.8/contrib/passwordcheck/目录下增加passwordcheck.so文件
##编译后的文件,可以迁移到同版本数据库内使用

3.2.2.3 验证

首先开启passwordcheck验证, 修改参数文件postgresql.conf

1
2
3
# 修改如下
shared_preload_libraries= 'passwordcheck'
passwordcheck.level='true‘

重启实例生效

1
pg_ctl restart

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 提示密码太短
postgres=# create role btest with login password '123';
ERROR: password is too short


# 提示必须同时包括字母、数字、特殊字符
postgres=# create role btest with login password '12345678';
ERROR: password must contain both letters and number and specialchar

postgres=# create role btest with login password 'qqqqqqqq';
ERROR: password must contain both letters and nonletters

postgres=# create role btest with login password '!!!!!!!!!!!!';
ERROR: password must contain both letters and nonletters

postgres=# create role btest with login password '12345678a';
ERROR: password must contain both letters and number and specialchar

postgres=# create role btest with login password '1234567!';
ERROR: password must contain both letters and nonletters

postgres=# create role btest with login password '12345678a!';
CREATE ROLE

# 修改密码提示必须同时包含字母、数字和特殊字符
postgres=# alter role btest password '12345678!';
ERROR: password must contain both letters and number and specialchar

# 提示密码不能包含用户名
postgres=# alter role btest password '123456btest!';
ERROR: password must not contain user name

4.2.2.3 问题

警告
这里还存在个问题,就是通过\password命令修改的话,可以输入不满足长度的密码,原因是使用\passwordpasswordcheck检查的是加密后的口令,官方文档提到过,检查md5加密后的口令是很困难的,所以当passwordcheck检查加密的口令时,只检查密码是否与用户名相同这一项,实际上是将用户名通过md5加密后与数据库中的md5密码做比较,如果相同,则报错口令不能与用户名相同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
postgres=#\password bert

Enter new password: ---->>>>>可以输入小于8位的口令, 而不被阻. 但是输入的是与用户名相同的话可以被检测出来。

Enter itagain:

postgres=#



postgres=#\set ECHO_HIDDEN ON

postgres=#\password bert

Enter newpassword:

Enter itagain:

*********QUERY **********

ALTERUSER bert PASSWORD 'md5efabd7549b98ddce9b14ba5e2e83eae1'

3.2.2.4 参考

https://www.modb.pro/db/171257

https://www.cnblogs.com/Luckyness/p/11996834.html

https://github.com/Luckyness/passwordcheck/blob/master/passwordcheck.c

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!