用户
搜索

[web安全] MySQL安全加固

  • TA的每日心情
    奋斗
    2020-9-4 09:57
  • 签到天数: 118 天

    连续签到: 1 天

    [LV.6]常住居民II

    i春秋作家

    Rank: 7Rank: 7Rank: 7

    23

    主题

    213

    帖子

    709

    魔法币
    收听
    2
    粉丝
    3
    注册时间
    2017-9-14

    春秋游侠i春秋签约作者热心助人奖春秋文阁幽默灌水王

    发表于 2020-9-12 15:22:57 27345

    前言

    打算出一个系列文章——各种对象的安全加固

    近半年都在研究等保测评的相关内容,发现可以分享出来的东西就是整理一份安全加固的系列文章了。关于等保的测评点理解,FREEBUF上有位大佬写的非常好很细,值得细品。其它的也就不多说了。

    觉得好的看完记得留个评论哈,有什么问题一起探讨。

    等保测评2.0系列文章 - 起于凡而非于凡

    https://www.freebuf.com/author/%E8%B5%B7%E4%BA%8E%E5%87%A1%E8%80%8C%E9%9D%9E%E4%BA%8E%E5%87%A1?type=article

    MySQL介绍

    MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

    MySQL是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。

    MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。

    mysql目录结构

    1591340602905

    配置文件,也就是后缀为.cnf的文件,大家自己看看文件内容就知道每个文件的作用了。

    我们来看mysql的帮助信息,里面有一条指明了mysql的配置文件是哪个,然后根据我们的情况就可以确定,mysql配置文件为/etc/mysql/my.cnf,因为其它两个文件不存在。

    root@NF:~# mysql --help |grep -A 1 'Default options'
    Default options are read from the following files in the given order:
    /etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

    那我们查看一下/etc/mysql/my.cnf文件的内容(跟/etc/mysql/mysql.cnf内容一样),主要有下面这些信息,也说明了/etc/mysql/my.cnf是全局配置,~/.my.cnf(隐藏文件,本环境下无此文件)是个人用户设置。

    # The MySQL database server configuration file.
    #
    # You can copy this to one of:
    # - "/etc/mysql/my.cnf" to set global options,
    # - "~/.my.cnf" to set user-specific options.
    
    !includedir /etc/mysql/conf.d/
    #表示包含/etc/mysql/conf.d/这个路径下面的配置文件,前提是必须以为.cnf为后缀
    !includedir /etc/mysql/mysql.conf.d/
    #表示包含/etc/mysql/mysql.conf.d/这个路径下面的配置文件,前提是必须以为.cnf为后缀

    然后我们看/etc/mysql/mysql.conf.d/mysql.conf.d,这个文件里面提供了mysql常用的基本配置。

    那我们在此文件编辑mysql的配置,发现是可以失效的,它包含于/etc/mysql/my.cnf文件,所以编辑mysql配置的时候就编辑/etc/mysql/mysql.conf.d/mysql.conf.d文件即可。

    其它的相关文件,其实就是理解linux各个主要目录是什么作用的。

    /usr/lib/mysql        # 动态库文件(.so文件,so=shared object)
    /usr/bin/mysql        # mysql命令,安装的软件的命令,usr指Unix System Resource
    /usr/share/mysql      # mysql共享数据,主要是一些帮助文档
    /etc/mysql            # mysql配置文件目录
    /etc/init.d/mysql     # 服务管理脚本(启动,停止,关闭等)
    /var/lib/mysql        # 默认的数据文档存储目录
    /var/log/mysql        # mysql日志文件(查询语句记录,报错日志,慢查询日志等)

    Ubuntu中MySQL的参数文件my.cnf示例详析 - 潇湘隐者

    【MySQL】MySQL的配置文件的区别和说明 - 十二翼堕落天使

    linux .so文件详解 - 牧马人

    00 关注官方安全更新公告

    在不影响业务正常运行的情况下,及时更新软件,打补丁。

    https://www.oracle.com/security-alerts/

    01 禁止数据库用户的密码为空并设置密码有效期

    执行如下执行SQL语句检查密码是否为空:

    select user,host from mysql.user where length(authentication_string) = 0;
    或
    select user,host,authentication_string,password_lifetime,account_locked from mysql.user;

    1592363617095

    若存在空密码的数据库用户,则执行如下命令设置数据库用户密码,且密码必须满足密码策略的要求:

    set password for 'user'@'host' = password('yourpassword');
    set password for 'testtest'@'192.168.56.1' = password('testtest');

    另外还要注意:

    1. 禁用或限制匿名、默认账户、测试账户的访问权限;(禁用账户)
    2. 应重命名或删除默认账户,修改默认账户的默认口令;
    3. 应及时删除或停用多余的、过期的账户,避免共享账户的存在;
    4. 删除了默认数据库TEST。(旧版本会有默认的测试数据库)

    禁用数据库用户的语句

    ALTER USER 'user'@'host' ACCOUNT LOCK;

    查看密码有效期(全局变量)

    mysql> show global variables like 'default_password_lifetime';
    +---------------------------+-------+
    | Variable_name             | Value |
    +---------------------------+-------+
    | default_password_lifetime | 0     |
    +---------------------------+-------+
    1 row in set (0.00 sec)

    配置密码有效期全局变量,编辑mysql配置文件,添加下面内容。

    default_password_lifetime = 180

    配置某个用户的密码有效期,使用ALTER USER命令为每个具体的用户账户单独设置特定的值,它会自动覆盖密码过期的全局策略。要注意ALTER USER语句的INTERVAL的单位是“天”。  

    ALTER USER 'root'@'localhost' PASSWORD EXPIRE INTERVAL 180 DAY;

    02 检查数据库用户的密码是否为弱口令

    有些人为了方便,可能会把数据库用户的密码设置为弱口令,现在的数据库会以mysql5加密算法加密口令,可以去MD5解密的平台输入密文,看能否得出明文。

    https://www.cmd5.com/

    https://www.somd5.com/

    1592363815230

    如果是弱口令,则要求更改其数据库用户的口令。

    关于MySQL密码你应该知道的那些事 - cenalulu

    MySQL:密码加密方式 - xuejianbest

    03   密码复杂度配置

    在数据库配置文件/etc/mysql/mysql.conf.d/mysqld.cnf中添加如下配置(根据自身业务需要设置密码复杂度策略)

    [mysqld]
    plugin-load = "validate_password.so"
    validate-password = FORCE_PLUS_PERMANENT
    validate_password_length = 8 
    validate_password_policy = 1 
    validate_password_mixed_case_count = 1 
    validate_password_number_count = 1 
    validate_password_special_char_count = 1 

    validate-password = FORCE_PLUS_PERMANENT  值为FORCE_PLUS_PERMANENT表示强制启用该插件,并且不能被卸载。

    validate_password_policy表示密码策略,有三个值,与其对应的策略见下表:

    密码策略
    0  或  LOW 校验密码长度变量
    1  或  MEDIUM 校验密码长度、数字、小写/大写和特殊字符4个变量
    2  或  STRONG 校验密码长度、数字、小写/大写、特殊字符和字典文件5个变量

    简言之,对于密码数字、小写/大写和特殊字符这三个变量,只有当密码策略为中或强时才是有效的。

    其它参数含义见下面的引用文章。

    那么最低要求配置如下

    效果:密码不能与用户名一致,密码长度8位以上(包含8位)、至少有一个数字、一个大写字母、一个小写字母、一个特殊字符。

    mysql> show variables like '%validate_password%'; 
    +--------------------------------------+--------+ 
    | Variable_name                        | Value  | 
    +--------------------------------------+--------+ 
    | validate_password_check_user_name    | OFF    | 
    | validate_password_dictionary_file    |        | 
    | validate_password_length             | 8      | 
    | validate_password_mixed_case_count   | 1      | 
    | validate_password_number_count       | 1      | 
    | validate_password_policy             | MEDIUM | 
    | validate_password_special_char_count | 1      | 
    +--------------------------------------+--------+ 
    7 rows in set (0.00 sec)                          

    安装和卸载插件

    validate_password插件相关参数的介绍

    MySql5.6使用validate password 插件加强密码强度的安装及使用方法 - wangmm0218

    04 登录失败和连接超时设置

    mysql有个连接超时的插件,相当于登录失败锁定策略,可根据业务需要进行最低配置。

    默认配置如下:

    mysql> show variables like "%connection_control%";
    +-------------------------------------------------+-------+
    | Variable_name                                   | Value |
    +-------------------------------------------------+-------+
    | connection_control_failed_connections_threshold | 3     |
    | connection_control_max_connection_delay         | 86400 |
    | connection_control_min_connection_delay         | 1000  |
    +-------------------------------------------------+-------+
    3 rows in set (0.00 sec)

    connection_control_failed_connections_threshold
    失败尝试的次数,默认为3,表示当连接失败3次后启用连接控制,0表示不开启
    connection_control_max_connection_delay
    响应延迟的最大时间
    connection_control_min_connection_delay
    响应延迟的最小时间,默认1000微秒,1秒

    然后是超时时间设置。

    查看和设置 连接超时相关的两个参数interactive_timeout和wait_timeout,其值应当至多为30分钟。

    show global variables like 'interactive_timeout';
    show global variables like 'wait_timeout'; 
    
    set global interactive_timeout=1800;
    set global wait_timeout=1800;

    interactive_timeout:交互式连接超时时间(mysql工具、mysqldump等)
    wait_timeout:非交互式连接超时时间、默认的连接mysql api程序、jdbc连接数据库等

    简单来说,通过mysql客户端连接数据库是交互式连接,通过jdbc连接数据库是非交互式连接。

    连接控制插件安装

    MySQL安全插件:Connection-Control Plugins 的利与弊 - leonpenn

    MySQL 插件之 连接控制插件(Connection-Control) - ZhenXing_Yu

    MySQL连接超时相关的两个参数interactive_timeout和wait_timeout的区别和解释 - young5201314

    MySQL参数max_connect_errors分析释疑 - 潇湘隐者

    MySQL状态变量Aborted_connects与Aborted_clients浅析 -海东潮

    05 启用SSL

    查看是否启用SSL(如果启用了SSL,需要进行配置,才能正常远程连接管理)。

    mysql> show variables like '%ssl';
    +---------------+----------+
    | Variable_name | Value    |
    +---------------+----------+
    | have_openssl  | DISABLED |
    | have_ssl      | DISABLED |
    +---------------+----------+
    2 rows in set (0.01 sec)

    说明:如果数据库禁止远程管理(select user,host from mysql.user;),则已经符合安全要求,此情况下已无需启用SSL。

    MySQL SSL配置(mysql5.7和mysql5.6) - Yuki_xiong

    MYSQL SSL配置与使用 - 德莱華

    06 远程管理限制

    其实最好还是应当禁止远程登录,至少要禁止root直接远程登录管理数据库。

    执行select user,host from mysql.user where user='root';

    看访问地址是否仅为 127.0.0.1 或 localhost 或 ::1

    其它用户如需远程连接,应做访问范围限制。

    执行select user,host from mysql.user where host = '%';

    若host字段为:% (允许任何IP连接),则说明不合规。

    远程连接管理配置方法:

    GRANT ALL PRIVILEGES ON <databases-name>.* TO 'user'@'<ip>' IDENTIFIED BY '<password>' WITH GRANT OPTION;
    FLUSH PRIVILEGES;

    <databases-name>  指定单个数据库名 也可以用 *(即所有数据库名)

    user 指定一个数据库用户名

    <ip> 指定一个IP、一个网段(包括B段、C段 192.168.1.%)、%(所有IP)

    <password> 指定远程连接时使用的密码,与本地密码可不同(但需符合密码复杂度要求)

    # 举例,给数据库用户teacher分配student数据库,只允许192.168.56.%网段远程连接并设置口令为Admin123。
    GRANT ALL PRIVILEGES ON student.* TO 'teacher'@'192.168.56.%' IDENTIFIED BY 'Admin123' WITH GRANT OPTION;
    FLUSH PRIVILEGES;

    效果如下

    1592381561275

    最后可以在mysql.user表中看到账号的情况,也是从这里删除。

    select user,host from mysql.user where account_locked='N' and host!='localhost';
    drop user 'user'@'host';

    知识补充-drop和delete的区别

    • drop
      drop user XXX;

      删除已存在的用户,默认删除的是'XXX'@'%'这个用户,如果还有其他的用户(其它主机名),如'XXX'@'localhost'等,不会一起被删除。如果要删除'XXX'@'localhost',使用drop删除时需要加上host即drop user 'XXX'@'localhost'

    • delete
      delete from user where user='XXX' and host='localhost';

      其中XXX为用户名,localhost为主机名(即需指定主机名)。

    • drop和delete的区别
      drop不仅会将user表中的数据删除,还会删除其他权限表的内容。

      而delete只删除user表中的内容,所以使用delete删除用户后需要执行FLUSH PRIVILEGES;刷新权限,否则下次使用create语句创建用户时会报错。

    07 会话连接数配置

    配置同样是在mysql配置文件中添加相关参数(max_connections = 100)。

    mysql> show variables like "%connections";
    +----------------------+-------+
    | Variable_name        | Value |
    +----------------------+-------+
    | max_connections      | 100   |
    | max_user_connections | 0     |
    +----------------------+-------+
    2 rows in set (0.01 sec)

    max_connections是对整个服务器的用户做出限制,
    max_user_connections是对每个用户的限制,
    为0表示不限制。

    root@NF:~$ grep max_connections /etc/mysql/mysql.conf.d/mysqld.cnf
    max_connections        = 100

    MySQL参数最大连接数max_connections -  paul_hch

    08 启用日志审计

    mysql默认启用日志审计,记录的内容也符合相关安全要求,此项默认符合。

    查看日志启用情况

    查看变量,看相关日志是否启用show variables like 'log%';

    或看mysql的配置文件,看相关日志的配置情况(根据业务需要启用相关日志和设置日志保存路径。)

    # * Logging and Replication
    #
    # Both location gets rotated by the cronjob.
    # Be aware that this log type is a performance killer.
    # As of 5.1 you can enable the log at runtime!
    #通用日志,将所有到达MySQL Server的SQL语句记录下来
    general_log_file        = /var/log/mysql/mysql.log
    general_log             = 1
    log_timestamps = SYSTEM
    #
    # Error log - should be very few entries.
    #错误日志,文件内容不会很多
    log_error = /var/log/mysql/error.log
    #
    # Here you can see queries with especially long duration
    #慢查询日志,记录SQL执行语句(执行时间超过2秒才会记录)
    slow_query_log          = 1
    slow_query_log_file     = /var/log/mysql/mysql-slow.log
    long_query_time = 2
    log-queries-not-using-indexes
    #
    # The following can be used as easy to replay backup logs or for replication.
    # note: if you are setting up a replication slave, see README.Debian about
    #       other settings you may need to change.
    #二进制日志
    server-id               = 1
    log_bin                 = /var/log/mysql/mysql-bin.log
    expire_logs_days        = 10
    max_binlog_size   = 100M
    #binlog_do_db           = include_database_name
    #binlog_ignore_db       = include_database_name

    来看一下日志内容,日志默认是分天存储的,一天一个文件并压缩保存。

    • 相关日志(主要是查询日志和错误日志)应留存6个月以上。
    • 日志记录的日期和时间应当是正确的,服务器需开启了NTP服务进行时间校对。

    1592374677497

    1592374811349

    #Linux 检查NTP服务时间同步情况
    ntpq -p -n
    ntpstat

    09 禁止.mysql_history文件记录信息

    .mysql_history文件会记录MySQL操作历史(即数据库查询语句),包含敏感信息。为了避免敏感信息泄露,需要禁止使用。
    检查所有.mysql_history文件是否链接到dev/null,若没连接到,则以root用户执行如下命令:

    find / -name ".mysql_history" | xargs
    rm <your_path>/.mysql_history 
    ln -s /dev/null <your_path>/.mysql_history

    1585725484010

    10 禁止mysql对系统文件进行读写操作

    local_infile变量表示能否使用load data local infile命令。该变量默认为ON。 该变量为OFF时,禁用客户端使用load data local infile命令。避免通过数据库查询语句造成的任意文件读写漏洞。

    执行如下SQL语句:

    show variables like 'local_infile';

    若返回结果不为OFF,则在/etc/my.cnf配置文件中修改

    [mysqld] 
    local_infile = 0

    11  用户权限合理分配

    执行下面语句,查看各账户和权限分配情况。请根据业务需求进行合理的权限分配,应遵循三权分立原则(分为系统管理员、安全管理员、安全审计员等,并检查系统各用户所属的权限组。如:系统管理员不能进行业务操作、审计操作,审计员不能进行业务操作、系统管理操作,安全员不能进行添加账号操作等)

    select user,host,account_locked from mysql.user;
    show grants for 'user'@'host';
    select * from mysql.user where user='user' and host='host' \G;
    • 不能存在特权用户
    • 不存在越权访问情况(绕过访问控制策略)
    • mysql 数据库应当只允许root用户进行访问和管理

    others

    1.其它建议

    • 最小权限原则

      1.对于数据库,可以一个数据库用户分配一个数据库

      2.对于mysql进程,不得以root用户运行,默认是采用了mysql用户运行。

    • 更改默认开放端口3306

    • 站库分离

    2.一些常见语句记录

    SET GLOBAL default_password_lifetime = 180;
    #设置全局变量及赋值。
    INSTALL PLUGIN validate_password SONAME 'validate_password.so';
    #安装插件,这里是安装配置密码复杂度策略的插件。

    3.关于更改数据库用户密码

    update user set password=password('123') where user='root' and host='localhost';
    # mysql 5.7以下
    
    update mysql.user set authentication_string=PASSWORD('newpassword') where user='username' and host='localhost';
    # mysql 5.7以上
    
    alter user 'root'@'localhost' identified by 'newpassword';
    # mysql 8.0以上

    其实就是要注意密码的列名是什么,版本不一样,列名不同,可使select * from mysql.user \G;查看密码的列名。(\G 即把列数据逐行显示)

    4.实验环境

    ubuntu 18.04.1
    mysql 5.7.30

    扩展阅读

    MySQL 5.7参考手册(官方)

    本帖被以下淘专辑推荐:

    逆向/破解/病毒分析板块  专属QQ交流群:496266893
    发表于 2020-9-12 15:27:07
    不错,期待后面的大作
    使用道具 举报 回复
    发表于 2020-9-15 11:22:43
    AOE师傅厉害
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册