前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >病毒攻击PostgreSQL暴力破解系统,防范加固系统方案(内附分析日志脚本)

病毒攻击PostgreSQL暴力破解系统,防范加固系统方案(内附分析日志脚本)

作者头像
AustinDatabases
发布2024-11-25 10:56:20
发布2024-11-25 10:56:20
1070
举报
文章被收录于专栏:AustinDatabases

最近国际与安全的网站Hacker New,里面的一篇报道,这里简单的翻译一下

网络安全研究人员解开了一种名为PG_MEM的新型恶意软件,设计用于在暴力破解方式进入PostgreSQL数据库实例后进行加密货币挖矿。

“Aqua安全研究人员Assaf Morag在一份技术报告中表示:暴力破解Postgres的攻击涉及反复尝试猜测数据库凭据,直到获取访问权限,利用弱密码。”

“一旦获得访问权限,攻击者可以利用COPY ... FROM PROGRAM SQL命令在主机上执行任意shell命令,从而允许他们执行恶意活动,如数据窃取或部署恶意软件。”

写到这里,我们针对这样的暴力破解有什么办法,或者有什么方案来让PostgreSQL尽量在安全的状态和设置下工作,是我们第一个要考虑的。

1 使用强密码:这点说起来容易,做起来难。默认PostgreSQL建立用户是不会限制我们输入密码的字符数,和字符的组合难度。这里我们可以先使用PostgreSQL自带的passwordcheck插件来支持这个工作。为什么要使用这个插件,熟悉PostgreSQL的同学都清楚,一般PG的extension 部分都是针对逻辑库的,都需要在创建库后,加载,这个passswordcheck的好处就是,不需要,直接在配置文件该完,马上就可以应用,我们来看看。

代码语言:javascript
复制
shared_preload_libraries = 'passwordcheck'      # (change requires restart)
passwordcheck.level='true'

然后重启数据库,因为要加载到内存中。

代码语言:javascript
复制
postgres=# create user test_password superuser password 'test';
ERROR:  password is too short
postgres=# create user test_password superuser password 'test1234';
CREATE ROLE
postgres=# drop user test_password;
DROP ROLE
postgres=# create user test_password superuser password 'test12';
ERROR:  password is too short
postgres=# create user test_password superuser password 'Test12';
ERROR:  password is too short
postgres=# create user test_password superuser password 'Test1234';
CREATE ROLE
postgres=# create user test_password superuser password 'testtest';
ERROR:  password must contain both letters and nonletters
postgres=# create user test_password superuser password 'testtes@';
CREATE ROLE

在加载后,我们在进行数据库的建立用户的操作,我们可以发现,当前的插件passwordcheck,已经工作,必须8位字符,且其中要求是字母+数字,或字母+特殊符号等。这个插件从一定的角度上避免了,设置的密码不符合一些要求和标准的情况。

2 用户的有效期用户的有效期这个问题是管理上的问题,从PostgreSQL是支持检查用户的预设的有效期,通过有效期来防止一些临时的用户长时间在系统中,这点是一个安全的意识,但需要注意以下的一些账号不要用有效期来控制。

1 核心的应用访问数据库的账号,不能使用过期的账号 2 核心的管理人员不能使用过期账号

其他的临时或非重要的,都可以使用过期的账号的方式来管理。

具体的操作方法

代码语言:javascript
复制
postgres=# create user test_password superuser password 'testtes@' valid until '2024-10-24 08:00:00';
CREATE ROLE
postgres=# 
postgres=# alter user test_password with valid until '2024-10-24 10:00:00';
ALTER ROLE
postgres=# 

3 通过auth_delay.so 来对暴力破解密码进行一定的防护,这个功能模块主要的目的是在验证失败后,一定时间后才能在进行验证密码,这样如果是暴力破解密码,则会给暴力破解密码延长暴力破解的时间。

暴力破解最大的干扰项就是一个密码错误的确认时间,我们现在做的就是要加大他每次密码错误的确认时间。

代码语言:javascript
复制
[postgres@postgresql13 ~]$ psql -Utest -h 192.168.198.100 -p 5432 
Password for user test: 
psql: error: FATAL:  password authentication failed for user "test"
[postgres@postgresql13 ~]$ psql -Utest -h 192.168.198.100 -p 5432 
Password for user test: 
psql: error: FATAL:  password authentication failed for user "test"
[postgres@postgresql13 ~]$ 

上面就是一个例子,在这里我们设置了auth_delay的设置后,密码输入错误后,至少等待5秒钟,这极大的摧毁了暴力破解中的给数据库系统尝试密码中暴力破解系统的压力。

如何进行设置

代码语言:javascript
复制
shared_preload_libraries = 'auth_delay' # (change requires restart)
auth_delay.milliseconds = 5000

以上在PostgreSQL中的postgresql.conf文件中进行修改,需要重启系统

4 最后我们通过一个脚本来亡羊补牢,通过这个脚本可以快速的分析日志中的关于登陆的异常,比如有人连续登陆失败,或者使用一个账号连续用错误的密码登陆。

不知道如何安装Node.js 可以先看这篇(通过脚本自动管理PG方案)

PostgreSQL 远程管理越来越简单,6个自动化脚本开胃菜

这个脚本通过node.js,加上fs文件模块,来对我们的PostgreSQL中的日志进行分析,通过文件中发现连续的登陆错误的信息,或者黑客尝试暴力破解的信息。通过脚本可以分析出每个数据库账号在一个日志中登陆失败的次数,通过这个来发现异常。他会分析日志中到底发生了多少次以用户登录失败,且主要原因是因为密码错误的问题导致的登陆失败,且在日志中连续5次的密码登陆失败,将会在日志中提出警告,引起管理人员的注意。

小结:随着PostgreSQL越来越热,用的人越来越多,针对PostgreSQL的恶意软件就会越来越多,针对PostgreSQL的账户安全工作,需要更多的关注。

代码语言:javascript
复制
const fs = require('fs');
const readline = require('readline');
const path = require('path');

// 日志文件目录这里修改你的日志文件的目录,同时日志必须是postgresql名开头的日志(拷贝文件时把这行中文去掉)

const logDirPath = '/pgdata/errorlog'; 

async function analyzeFailedLogins() {
    const logFiles = fs.readdirSync(logDirPath).filter(file => file.startsWith('postgresql'));

    for (const file of logFiles) {
        console.log(`正在分析文件: ${file}`);

        const fileStream = fs.createReadStream(path.join(logDirPath, file));
        const rl = readline.createInterface({
            input: fileStream,
            crlfDelay: Infinity,
        });

        let failedLogins = {};
        let previousUser = null;
        let previousTimestamp = null;
        let failureCount = 0;

        let consecutiveFailures = 0;
        let consecutiveFailureDetails = [];

        for await (const line of rl) {
          
            if (line.includes('password authentication failed') || line.includes('authentication failed')) {
                const matchUser = line.match(/user "([^"]+)"/);
                const matchTime = line.match(/^(\d+-\d+-\d+ \d+:\d+:\d+)/); // 匹配时间戳

                if (matchUser && matchUser[1] && matchTime && matchTime[1]) {
                    const user = matchUser[1];
                    const timestamp = matchTime[1];

                   
                    failedLogins[user] = (failedLogins[user] || 0) + 1;

                   
                    if (user === previousUser && failureCount > 0) {
                        failureCount++;
                        consecutiveFailures++;
                        consecutiveFailureDetails.push({ user, timestamp });
                    } else {
                       
                        if (failureCount >= 5) {
                            console.log(`账号 ${previousUser} 连续登录失败超过 5 次:`);
                            console.table(consecutiveFailureDetails);
                        }

                        previousUser = user;
                        previousTimestamp = timestamp;
                        failureCount = 1;
                        consecutiveFailures = 1;
                        consecutiveFailureDetails = [{ user, timestamp }];
                    }
                }
            } else {
               
                if (failureCount >= 5) {
                    console.log(`账号 ${previousUser} 连续登录失败超过 5 次:`);
                    console.table(consecutiveFailureDetails);
                }
                failureCount = 0;
                previousUser = null;
                consecutiveFailures = 0;
                consecutiveFailureDetails = [];
            }
        }

       
        if (failureCount >= 5) {
            console.log(`账号 ${previousUser} 连续登录失败超过 5 次:`);
            console.table(consecutiveFailureDetails);
        }

        console.log('各账号登录失败统计:');
        for (const user in failedLogins) {
            console.log(`用户: ${user}, 登录失败次数: ${failedLogins[user]}`);
        }

        console.log('-----------------------------');
    }
}

analyzeFailedLogins().catch(err => {
    console.error('分析失败:', err);
});
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-10-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AustinDatabases 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档