明码验证介绍
passwordcheck 模块是在 CREATE ROLE 或者 CREATE USER 期间检查用户明码是否合乎指定的规定模块如果明码比拟弱,那么在此期间将会拒绝执行明码并返回一个谬误。 该模块位于 srcpkg/contrib 目录下,装置后位于 $libdir 目录下,应用 shared_preload_libraries加载并重新启动服务器后失效。在该模块中,次要有两个规定判断,一个是用户名本身的判断,一个是明码长度少于8位的判断,一个是对是否蕴含用户名自身的判断。
明码验证加强性能
明码验证加强性能次要是在原有明码查看模块的根底上,减少了对明码中是否蕴含至多一个大小写字母,一个数字和一个特殊字符的判断。
实现
/*------------------------------------------------------------------------- * * passwordcheck_enchance.c * * Author: Sungsasong * * IDENTIFICATION * contrib/passwordcheck_enhance/passwordcheck_enhance.c * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#ifdef USE_CRACKLIB#include <crack.h>#endif#include "commands/user.h"#include "catalog/namespace.h"#include "libpq/crypt.h"#include "fmgr.h"#include "utils/guc.h"#if PG_VERSION_NUM < 100000#include "libpq/md5.h"#endifPG_MODULE_MAGIC;/* passwords shorter than this will be rejected */#define MIN_PWD_LENGTH 8#define MIN_UPPER_LETTER 1#define MIN_LOWER_LETTER 1#define MIN_DIGIT_CHAR 1#define MIN_SPECIAL_CHAR 1extern void _PG_init(void);/********************************************************************** *Function:passwordcheck_enhance * *Verifying the password at least need contains one upper letter,lower* *letter,digit and specital character * *********************************************************************/#if PG_VERSION_NUM >= 100000 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 contain 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, // pwd_has_nonletter; int PWD_UPPER_LETTER_COUNT = 0; int PWD_LOWER_LETTER_COUNT = 0;int PWD_SPECIAL_CHAR_COUNT = 0; int PWD_DIGIT_COUNT = 0; int PWD_CONTAINS_LETTER_COUNT = 0; //如果满足至多8位明码的条件,那么判断明码中是否蕴含至多一个大小写字母和特殊字符for(i = 0; i < pwdlen; i++) { /* enforce minimum length */ if(pwdlen < MIN_PWD_LENGTH) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码长度至多须要 %d 位,并且至多须要蕴含一个大小写字母和特殊字符 ",MIN_PWD_LENGTH))); } //判断是否蕴含字母if(isalpha((unsigned char) password[i])) { PWD_CONTAINS_LETTER_COUNT++; if(islower((unsigned char) password[i])) { PWD_LOWER_LETTER_COUNT++; }else if(isupper((unsigned char) password[i])) { PWD_UPPER_LETTER_COUNT++; } }else if(isdigit((unsigned char) password[i])) { PWD_DIGIT_COUNT++; }else { PWD_SPECIAL_CHAR_COUNT++; } } //判断是否至多蕴含了一个数字,大小写字母和特殊字符 if(PWD_LOWER_LETTER_COUNT < MIN_LOWER_LETTER) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个小写字母", MIN_LOWER_LETTER))); }else if(PWD_UPPER_LETTER_COUNT < MIN_UPPER_LETTER) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个大写字母", MIN_UPPER_LETTER))); }else if(PWD_DIGIT_COUNT < MIN_DIGIT_CHAR) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个数字", MIN_DIGIT_CHAR))); }else if(PWD_SPECIAL_CHAR_COUNT < MIN_SPECIAL_CHAR) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个特殊字符", MIN_DIGIT_CHAR))); } /* check if the password contains the username */ if (strstr(password, username)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码不能与用户名同名"))); } } /* all checks passed, password is ok */ }#else static void check_password(const char *username, const char *password, int password_type, Datum validuntil_time, bool validuntil_null) { int namelen = strlen(username); int pwdlen = strlen(password); char encrypted[MD5_PASSWD_LEN + 1]; int i; bool pwd_has_letter, pwd_has_nonletter; int PWD_UPPER_LETTER_COUNT = 0; int PWD_LOWER_LETTER_COUNT = 0; int PWD_SPECIAL_CHAR_COUNT = 0; int PWD_DIGIT_COUNT = 0; int PWD_CONTAINS_LETTER_COUNT = 0; switch (password_type) { case PASSWORD_TYPE_MD5: /* * 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. */ if (!pg_md5_encrypt(username, username, namelen, encrypted)) { elog(ERROR, "password encryption failed"); } if (strcmp(password, encrypted) == 0) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("password must not contain user name"))); } break; case PASSWORD_TYPE_PLAINTEXT: /* * For unencrypted passwords we can perform better checks */ /* enforce minimum length */ //如果满足至多8位明码的条件,那么判断明码中是否蕴含至多一个大小写字母和特殊字符 for(i = 0; i < pwdlen; i++) { /* enforce minimum length */ if(pwdlen < MIN_PWD_LENGTH) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码长度至多须要 %d 位,并且至多须要蕴含一个大小写字母和特殊字符 ",MIN_PWD_LENGTH))); } //判断是否蕴含字母 if(isalpha((unsigned char) password[i])) { PWD_CONTAINS_LETTER_COUNT++; if(islower((unsigned char) password[i])) { PWD_LOWER_LETTER_COUNT++; }else if(isupper((unsigned char) password[i])) { PWD_UPPER_LETTER_COUNT++; } }else if(isdigit((unsigned char) password[i])) { PWD_DIGIT_COUNT++; }else { PWD_SPECIAL_CHAR_COUNT++; } } //判断是否至多蕴含了一个数字,大小写字母和特殊字符 if(PWD_LOWER_LETTER_COUNT < MIN_LOWER_LETTER) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个小写字母", MIN_LOWER_LETTER))); }else if(PWD_UPPER_LETTER_COUNT < MIN_UPPER_LETTER) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个大写字母", MIN_UPPER_LETTER))); }else if(PWD_DIGIT_COUNT < MIN_DIGIT_CHAR) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个数字", MIN_DIGIT_CHAR))); }else if(PWD_SPECIAL_CHAR_COUNT < MIN_SPECIAL_CHAR) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码至多须要蕴含 %d 个特殊字符", MIN_DIGIT_CHAR))); } /* check if the password contains the username */ if (strstr(password, username)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("明码不能与用户名同名"))); } break; default: elog(ERROR, "unrecognized password type: %d", password_type); break; } /* all checks passed, password is ok */ }#endif/* * Module initialization function */void_PG_init(void){ /* activate password checks when the module is loaded */ check_password_hook = check_password;}
取得源码
[passwordcheck_enhance](https://github.com/DeveloperHonor/passwordkcheck-enhance-for-postgresql.git "passwordcheck_enhance module")
装置
*下载源码文件并传入到 PostgreSQL 源码包 contrib 目录下* *解压下载的源码包文件* `[postgres@sungsasong contrib]$ unzip passwordkcheck-enhance-for-postgresql-main` *切换到解压目录* *执行 make && make install* *在 $PGDATA/postgresql.auto.conf或者 $PGDATA/postgresql.conf文件中退出如下* `shared_preload_libraries = 'passwordcheck_enhance` *重新启动 PostgreSQL 服务器*
验证
postgres=# CREATE USER user_test WITH PASSWORD 'user';ERROR: 明码长度至多须要 8 位,并且至多须要蕴含一个大小写字母和特殊字符 postgres=# CREATE USER user_test WITH PASSWORD 'useruser';ERROR: 明码至多须要蕴含 1 个大写字母postgres=# CREATE USER user_test WITH PASSWORD 'useruseA';ERROR: 明码至多须要蕴含 1 个数字postgres=# CREATE USER user_test WITH PASSWORD 'useruseA1';ERROR: 明码至多须要蕴含 1 个特殊字符postgres=# CREATE USER user_test WITH PASSWORD 'useruseA1!';CREATE ROLE