スキップしてコンテンツを表示

Aurora PostgreSQL 互換 DB クラスターへの接続におけるパスワードの複雑さと有効期限を管理する方法を教えてください。

所要時間4分
0

Amazon Aurora PostgreSQL 互換エディションデータベース (DB) クラスターへの接続におけるパスワードの複雑さと有効期限を管理したいと考えています。

簡単な説明

Aurora PostgreSQL 互換 DB クラスターのパスワードを管理するには、以下の方法を使用します。

解決策

TLE を使用してパスワードチェックフックを利用する

TLE を使用してパスワードチェックフックを利用するには、まず Aurora PostgreSQL 互換 DB クラスターに TLE をセットアップする必要があります。次に、パスワードチェックフックを作成します。 

注: フックの名前を付けるときに user-password-check-rules は使用できません。

パスワードチェックフックのコード例:

SELECT pgtle.install_extension(
  'user-password-check-rules',
  '1.0',
  'Do not let users use the 10 most commonly used passwords',
  $_pgtle_$
    CREATE SCHEMA password_check;
    REVOKE ALL ON SCHEMA password_check FROM PUBLIC;
    GRANT USAGE ON SCHEMA password_check TO PUBLIC;
    -- Create table for bad passwords
    CREATE TABLE password_check.bad_passwords (plaintext) AS
    VALUES
      ('123456'),
      ('password'),
      ('12345678'),
      ('qwerty'),
      ('123456789'),
      ('12345'),
      ('1234'),
      ('111111'),
      ('1234567'),
      ('dragon');
    -- Create table for tracking user password changes
    CREATE TABLE password_check.usertime (
      user_name text PRIMARY KEY,
      lasttime timestamp
    );
    CREATE UNIQUE INDEX ON password_check.bad_passwords (plaintext);
    CREATE FUNCTION password_check.passcheck_hook(username text, password text, password_type pgtle.password_types, valid_until timestamptz, valid_null boolean)
    RETURNS void AS $$
      DECLARE
        invalid bool := false;
        userexists bool;
        last_password_change timestamp;
      BEGIN
        -- Check if user already exists      
        SELECT EXISTS (SELECT user_name FROM password_check.usertime WHERE user_name = username) INTO userexists;
        -- If user exists, check password age
        IF (userexists) THEN
          SELECT lasttime INTO last_password_change
          FROM password_check.usertime
          WHERE user_name = username;
          -- Check if password is older than 90 days
          IF (CURRENT_TIMESTAMP - last_password_change) > INTERVAL '90 days' THEN
            RAISE EXCEPTION 'Password has expired. It must be changed every 90 days. Last change was on %', last_password_change;
          END IF;
          -- Update timestamp for new password change
          UPDATE password_check.usertime SET lasttime = CURRENT_TIMESTAMP WHERE user_name = username;
        ELSE
          -- First time user, add to tracking table
          INSERT INTO password_check.usertime VALUES(username, CURRENT_TIMESTAMP);
        END IF;
        -- Existing password length check
        IF length(password) < 8 THEN
          RAISE EXCEPTION 'Password must be at least 8 characters long.';
        END IF;
        -- Existing common password check
        IF password_type = 'PASSWORD_TYPE_MD5' THEN
          SELECT EXISTS(
            SELECT 1
            FROM password_check.bad_passwords bp
            WHERE ('md5' || md5(bp.plaintext || username)) = password
          ) INTO invalid;
          IF invalid THEN
            RAISE EXCEPTION 'Cannot use passwords from the common password dictionary';
          END IF;
        ELSIF password_type = 'PASSWORD_TYPE_PLAINTEXT' THEN
          SELECT EXISTS(
            SELECT 1
            FROM password_check.bad_passwords bp
            WHERE bp.plaintext = password
          ) INTO invalid;
          IF invalid THEN
            RAISE EXCEPTION 'Cannot use passwords from the common password dictionary';
          END IF;
        END IF;
        -- Existing complexity check
        IF NOT (password ~ '[A-Z]' AND password ~ '[a-z]' AND password ~ '[0-9]' AND password ~ '[^a-zA-Z0-9]') THEN
          RAISE EXCEPTION 'Password must contain uppercase letters, lowercase letters, numbers, and special characters';
        END IF;
      END
    $$ LANGUAGE plpgsql SECURITY DEFINER;
    GRANT EXECUTE ON FUNCTION password_check.passcheck_hook TO PUBLIC;
    SELECT pgtle.register_feature('password_check.passcheck_hook', 'passcheck');
  $_pgtle_$
);

注: 要件に合わせてコードを変更してください。例えば、bad_passwords テーブルのパスワードを追加したり、パスワードの長さの要件を変更したり、パスワードの複雑さを確認したりできます。 

パスワードが長さの要件を満たしているかどうかを確認するには、次のコマンドを実行します。

CREATE ROLE user1 PASSWORD '123456';

想定される出力:

ERROR: Password must be at least 8 characters long.
CONTEXT: PL/pgSQL function password_check.passcheck_hook(text,text,pgtle.password_types,timestamptz,bool)
SQL statement "SELECT password_check.passcheck_hook($1::pg_catalog.text, $2::pg_catalog.text, $3::pgtle.password_types, $4::pg_catalog.timestamptz, $5::pg_catalog.bool)"

注: 上記の例は、パスワードが 8 文字以上でなければならないため、エラーが発生したことを表しています。

パスワードが一般的すぎるかどうかを確認するには、次のコマンドを実行します。

CREATE ROLE user1 PASSWORD 'password';

想定される出力:

ERROR: Cannot use passwords from the common password dictionary
CONTEXT: PL/pgSQL function password_check.passcheck_hook(text,text,pgtle.password_types,timestamptz,bool)
SQL statement "SELECT password_check.passcheck_hook($1::pg_catalog.text, $2::pg_catalog.text, $3::pgtle.password_types, $4::pg_catalog.timestamptz, $5::pg_catalog.bool)"

注: 上記の例は、一般的なパスワード辞書のパスワードを使用できないため、エラーが発生したことを表しています。

パスワードの有効期限を確認するには、次の手順を実行します。

  1. 次のコマンドを実行して、ユーザーが最後にパスワードを変更した日時を確認します。

    SELECT * FROM password_check.usertime WHERE user_name = 'user1';

    想定される出力:

    user_name | lasttime
    ----------+----------------------------
    user1     | 2025-10-01 18:48:07.319908
    (1 row)
  2. 次のコマンドを実行して、最終更新日を 91 日前に設定します。

    UPDATE password_check.usertime
    SET lasttime = CURRENT_TIMESTAMP - INTERVAL '91 days'
    WHERE user_name = 'user1';

    想定される出力:

    UPDATE 1
  3. 次のコマンドを実行して、最終更新日が 91 日前であることを確認します。

    SELECT * FROM password_check.usertime WHERE user_name = 'user1';

    想定される出力:

    user_name | lasttime
    ----------+----------------------------
    user1     | 2025-07-02 18:55:36.248913
    (1 row)
  4. 次のコマンドを実行して、パスワードの有効期限機能をテストします。

    ALTER USER user1 WITH PASSWORD 'NewTestPass786!';

    想定される出力:

    NOTICE: Password has expired. It must be changed every 90 days. Last change was on 2025-07-02 18:55:36.248913
    ALTER ROLE
  5. 次のコマンドを実行して、ユーザーパスワードが変更され、最終更新日が更新されたことを確認します。

    SELECT * FROM password_check.usertime WHERE user_name = 'user1';

    想定される出力:

    user_name | lasttime
    ----------+----------------------------
    user1     | 2025-10-01 19:18:42.028533
    (1 row)

制限されたパスワード管理機能を有効にする

rds.restrict_password_commands パラメータを 1 に設定して、rds_password ロールを付与したデータベースユーザーのみがパスワードを管理できるようにします。

次の手順を実行します。

  1. DB クラスターパラメータグループを作成します
  2. DB クラスターパラメータグループを DB クラスターに関連付けます
  3. DB クラスターパラメータグループを変更して rds.restrict_password_commands1 に設定します。

注: 変更を適用するには、Aurora PostgreSQL 互換のプライマリ DB インスタンスを再起動する必要があります。

IAM データベース認証を使用する

IAM データベース認証では、パスワードではなく認証トークンを使用して DB クラスターに接続します。

IAM データベース認証を設定するには、次の手順を実行します。

  1. IAM データベース認証を有効にします
  2. IAM データベースアクセス用の IAM ポリシーを作成します
  3. プライマリユーザー、またはアクセス許可を作成および付与できるユーザーとして DB クラスターに接続します。
  4. ユーザーに rds_iam ロールを付与します
  5. IAM 認証を使用して DB クラスターに接続します

IAM ユーザー用のカスタムパスワードポリシーを作成する

詳細については、「IAM ユーザー用のアカウントパスワードポリシーを設定する」を参照してください。

AWS Secrets Manager を使用する

AWS Secrets Manager を使用してプライマリユーザーのパスワードを管理します。IAM 条件キーを使用して、Aurora によるプライマリユーザーのパスワードの管理を強制します。

詳細については、「Amazon Aurora および AWS Secrets Manager によるパスワード管理」を参照してください。

関連情報

PostgreSQL のロールとアクセス権限について

New – Trusted Language Extensions for PostgreSQL on Amazon Aurora and Amazon RDS (新規 — Amazon Aurora と Amazon RDS 上の PostgreSQL 用の信頼できる Trusted Language Extensions)

AWS公式更新しました 5ヶ月前
コメントはありません

関連するコンテンツ