MySQL安全与审计之:`MySQL`的`Password Expiration`:其在密码生命周期管理中的应用。

好的,我们开始今天的讲座,主题是 MySQL 安全与审计之:MySQLPassword Expiration(密码过期)及其在密码生命周期管理中的应用。

引言:密码安全的重要性

在当今的信息安全环境中,数据库安全至关重要。而数据库密码的安全性是整个安全体系中的关键一环。弱口令、长期不更改的密码,都可能成为攻击者入侵的突破口。因此,制定完善的密码策略,并强制执行,是保障数据库安全的重要手段。MySQL 提供的 Password Expiration 功能,正是密码生命周期管理的重要组成部分。

一、MySQL Password Expiration 功能概述

Password Expiration,即密码过期功能,是指强制用户定期更改其数据库密码。这种机制可以显著降低因密码泄露或猜测而导致的风险。MySQL 从 5.7.4 版本开始引入了密码过期策略,并不断增强。

1.1 default_password_lifetime 系统变量

default_password_lifetime 是一个全局系统变量,用于设置密码的默认过期时间。它的值表示密码在多少天后过期。默认值为 0,表示密码永不过期。

要查看当前的 default_password_lifetime 值,可以使用以下 SQL 语句:

SHOW VARIABLES LIKE 'default_password_lifetime';

要修改全局的 default_password_lifetime 值,可以使用以下 SQL 语句:

SET GLOBAL default_password_lifetime = 90; -- 设置密码 90 天后过期

设置完成后,新创建的用户将继承这个过期策略。已经存在的用户,如果未单独设置过期策略,也会受到影响。

1.2 PASSWORD EXPIRE 子句

PASSWORD EXPIRE 子句用于控制单个用户的密码过期状态。它可以用于 CREATE USERALTER USER 语句中。

  • PASSWORD EXPIRE DEFAULT: 用户密码的过期时间由全局变量 default_password_lifetime 决定。
  • PASSWORD EXPIRE INTERVAL N DAY: 用户密码将在 N 天后过期。
  • PASSWORD EXPIRE NEVER: 用户密码永不过期。
  • PASSWORD EXPIRE: 用户密码立即过期,下次登录时必须更改密码。

例如,创建一个用户,密码 60 天后过期:

CREATE USER 'testuser'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE INTERVAL 60 DAY;

修改一个已存在用户的密码过期策略,使其密码立即过期:

ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE;

修改一个已存在用户的密码过期策略,使其密码永不过期:

ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE NEVER;

1.3 password_history 插件和变量

password_history 插件用于记录用户历史密码,防止用户在密码过期后,简单地将密码改回之前的密码。

  • password_history: 设置记住多少个历史密码。
  • password_reuse_interval: 设置在多少天内不能重用历史密码。

首先,安装 password_history 插件:

INSTALL PLUGIN password_history SONAME 'password_history.so';

然后,设置 password_historypassword_reuse_interval 的值:

SET GLOBAL password_history = 5;  -- 记住 5 个历史密码
SET GLOBAL password_reuse_interval = 90; -- 90 天内不能重用历史密码

注意:password_history 插件在 MySQL 8.0 中已被移除,在 MySQL 8.0 中,密码历史记录功能被内置到服务器中,不需要单独安装插件。相关变量的名字也略有不同。

二、密码生命周期管理流程

一个完整的密码生命周期管理流程应该包括以下几个阶段:

  1. 密码策略制定: 明确密码复杂度要求、过期时间、历史密码重用限制等。
  2. 密码创建/修改: 创建用户时,设置合适的密码过期策略。修改用户密码时,也要遵循密码策略。
  3. 密码过期提醒: 在密码即将过期时,提醒用户及时更改密码。
  4. 密码过期处理: 密码过期后,强制用户更改密码才能继续使用。
  5. 密码历史记录: 记录用户的历史密码,防止用户重用旧密码。
  6. 审计和监控: 定期审计密码策略的执行情况,监控异常的密码修改行为。

2.1 密码策略制定

密码策略应该根据实际的安全需求制定。以下是一些常见的密码策略要求:

  • 密码复杂度: 密码长度至少为 8 位,包含大小写字母、数字和特殊字符。
  • 密码过期时间: 密码每 90 天或 180 天过期一次。
  • 历史密码重用限制: 禁止重用最近 5 个密码。
  • 密码修改频率: 限制用户在短时间内频繁修改密码。

可以使用 MySQL 的密码验证插件(如 validate_password)来强制执行密码复杂度要求。

2.2 密码创建/修改

在创建用户时,应该根据密码策略设置合适的密码过期策略。例如:

CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'StrongPassword123!' PASSWORD EXPIRE INTERVAL 90 DAY;

修改用户密码时,也应该遵循密码策略。可以使用 ALTER USER 语句修改密码和过期策略:

ALTER USER 'newuser'@'localhost' IDENTIFIED BY 'NewStrongPassword456!' PASSWORD EXPIRE INTERVAL 90 DAY;

2.3 密码过期提醒

MySQL 本身没有提供密码过期提醒功能。需要通过其他方式实现。常见的做法包括:

  • 编写脚本: 编写一个脚本,定期查询数据库中密码即将过期的用户,并通过邮件或短信通知用户。
  • 集成到应用程序中: 在应用程序中,判断用户密码是否即将过期,并在用户登录时提醒用户更改密码。

以下是一个简单的 Python 脚本,用于查询密码即将过期的用户:

import mysql.connector
import datetime

def get_expiring_passwords(days_before_expiry=7):
    """
    获取密码在 days_before_expiry 天内即将过期的用户。
    """
    mydb = mysql.connector.connect(
        host="localhost",
        user="root",
        password="your_password",
        database="mysql"
    )

    mycursor = mydb.cursor()

    # 查询密码过期时间
    mycursor.execute("SELECT User, Host, password_last_changed, password_lifetime FROM mysql.user")

    expiring_users = []
    for user, host, last_changed, lifetime in mycursor:
        if lifetime is not None and lifetime > 0:
            expiry_date = last_changed + datetime.timedelta(days=lifetime)
            days_to_expiry = (expiry_date - datetime.date.today()).days
            if days_to_expiry <= days_before_expiry and days_to_expiry >= 0:
                expiring_users.append((user, host, days_to_expiry))

    mydb.close()
    return expiring_users

if __name__ == "__main__":
    expiring_users = get_expiring_passwords()
    if expiring_users:
        print("以下用户的密码即将过期:")
        for user, host, days_to_expiry in expiring_users:
            print(f"User: {user}@{host}, Days to expiry: {days_to_expiry}")
    else:
        print("没有密码即将过期的用户。")

2.4 密码过期处理

当用户密码过期后,MySQL 会阻止用户登录,并返回一个错误信息。用户必须更改密码才能继续使用。

可以使用 ALTER USER 语句更改密码:

ALTER USER 'expireduser'@'localhost' IDENTIFIED BY 'NewPassword123!';

在应用程序中,需要捕获密码过期错误,并提示用户更改密码。

2.5 密码历史记录

如前所述,可以使用 password_history 插件(MySQL 8.0 及其以上版本内置)来记录用户的历史密码,防止用户重用旧密码。

2.6 审计和监控

定期审计密码策略的执行情况,监控异常的密码修改行为。可以使用 MySQL 的审计日志功能来记录所有密码修改操作。

三、代码示例:完整的密码生命周期管理

以下是一个完整的密码生命周期管理的代码示例,包括密码策略制定、密码创建/修改、密码过期提醒和密码过期处理。

3.1 密码策略制定

假设密码策略如下:

  • 密码长度至少为 8 位,包含大小写字母、数字和特殊字符。
  • 密码每 90 天过期一次。
  • 禁止重用最近 5 个密码。

3.2 密码创建/修改

-- 安装密码验证插件
INSTALL PLUGIN validate_password SONAME 'validate_password.so';

-- 设置密码验证插件的参数
SET GLOBAL validate_password.policy = MEDIUM;
SET GLOBAL validate_password.length = 8;

-- 设置密码过期时间
SET GLOBAL default_password_lifetime = 90;

-- 安装密码历史记录插件 (MySQL 5.7)
-- INSTALL PLUGIN password_history SONAME 'password_history.so';
-- SET GLOBAL password_history = 5;
-- SET GLOBAL password_reuse_interval = 90;

-- 创建用户
CREATE USER 'user1'@'localhost' IDENTIFIED BY 'InitialPassword123!' PASSWORD EXPIRE INTERVAL 90 DAY;

-- 修改用户密码
ALTER USER 'user1'@'localhost' IDENTIFIED BY 'NewPassword456!' PASSWORD EXPIRE INTERVAL 90 DAY;

3.3 密码过期提醒 (Python 脚本)

import mysql.connector
import datetime
import smtplib
from email.mime.text import MIMEText

def get_expiring_passwords(days_before_expiry=7):
    """
    获取密码在 days_before_expiry 天内即将过期的用户。
    """
    mydb = mysql.connector.connect(
        host="localhost",
        user="root",
        password="your_password",
        database="mysql"
    )

    mycursor = mydb.cursor()

    # 查询密码过期时间
    mycursor.execute("SELECT User, Host, password_last_changed, password_lifetime FROM mysql.user")

    expiring_users = []
    for user, host, last_changed, lifetime in mycursor:
        if lifetime is not None and lifetime > 0:
            expiry_date = last_changed + datetime.timedelta(days=lifetime)
            days_to_expiry = (expiry_date - datetime.date.today()).days
            if days_to_expiry <= days_before_expiry and days_to_expiry >= 0:
                expiring_users.append((user, host, days_to_expiry))

    mydb.close()
    return expiring_users

def send_email_alert(user, host, days_to_expiry, recipient_email="[email protected]"):
    """
    发送密码过期提醒邮件。
    """
    sender_email = "[email protected]"
    sender_password = "your_email_password"  # Be careful storing this!

    subject = "密码即将过期提醒"
    body = f"尊敬的用户 {user}@{host},nn您的 MySQL 密码将在 {days_to_expiry} 天后过期。请及时更改密码。nn谢谢!"

    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = sender_email
    msg['To'] = recipient_email

    try:
        with smtplib.SMTP_SSL('smtp.example.com', 465) as server: # Replace with your SMTP server details
            server.login(sender_email, sender_password)
            server.sendmail(sender_email, recipient_email, msg.as_string())
        print(f"成功发送邮件到 {recipient_email}")
    except Exception as e:
        print(f"发送邮件失败:{e}")

if __name__ == "__main__":
    expiring_users = get_expiring_passwords()
    if expiring_users:
        print("以下用户的密码即将过期:")
        for user, host, days_to_expiry in expiring_users:
            print(f"User: {user}@{host}, Days to expiry: {days_to_expiry}")
            send_email_alert(user, host, days_to_expiry)
    else:
        print("没有密码即将过期的用户。")

3.4 密码过期处理 (应用程序代码)

在应用程序中,需要捕获密码过期错误,并提示用户更改密码。 以下是一个简单的示例代码 (伪代码):

try:
    # 连接数据库
    connection = connect_to_database()

    # 执行查询
    result = execute_query(connection, "SELECT * FROM users WHERE username = 'user1'")

    # 处理查询结果
    process_result(result)

except mysql.connector.Error as err:
    if err.errno == 1862:  # CR_PASSWORD_EXPIRED
        print("密码已过期,请更改密码。")
        # 跳转到密码修改页面
        redirect_to_password_change_page()
    else:
        print(f"数据库错误:{err}")

finally:
    # 关闭连接
    if connection:
        connection.close()

四、实际应用中的注意事项

  • 测试: 在生产环境中使用 Password Expiration 功能之前,务必在测试环境中进行充分的测试。
  • 通知: 提前通知用户密码过期策略,并提供详细的密码修改指南。
  • 兼容性: 确保应用程序能够正确处理密码过期错误。
  • 监控: 定期监控密码策略的执行情况,及时发现和解决问题。
  • 备份: 定期备份数据库,以防止数据丢失。
  • 自动化: 尽量使用自动化工具来管理密码生命周期,减少人工干预。
  • 用户教育: 对用户进行安全培训,提高用户的安全意识。

五、MySQL 8.0 的变化

MySQL 8.0 对密码管理进行了一些改进:

  • 内置密码历史记录: 密码历史记录功能被内置到服务器中,不需要单独安装插件。
  • 变量名称变更: password_historypassword_reuse_interval 变量被 password_previous_historypassword_reuse_time 取代。
  • 更灵活的密码策略: 提供了更多的密码策略选项,例如可以设置密码的最大长度。

六、表格总结

功能 描述 系统变量/子句
密码过期时间设置 设置密码的默认过期时间或单个用户的密码过期时间。 default_password_lifetime, PASSWORD EXPIRE
密码历史记录 记录用户的历史密码,防止用户重用旧密码。 password_history (MySQL 5.7), password_previous_history (MySQL 8.0), password_reuse_time (MySQL 8.0)
密码复杂度验证 强制执行密码复杂度要求。 validate_password 插件
密码过期提醒 定期查询数据库中密码即将过期的用户,并通过邮件或短信通知用户。 需要编写脚本或集成到应用程序中
密码过期处理 密码过期后,强制用户更改密码才能继续使用。 ALTER USER
审计和监控 定期审计密码策略的执行情况,监控异常的密码修改行为。 MySQL 审计日志

密码过期功能是数据库安全的重要一环,通过合理的配置和使用,可以有效降低安全风险。希望今天的讲座能帮助大家更好地理解和应用 MySQL 的 Password Expiration 功能,提升数据库的安全性。

密码过期功能是提高MySQL安全性的关键,合理配置和使用可显著降低安全风险。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注