[新機能] Amazon Cognito CSV インポートでパスワードハッシュを指定してみた

[新機能] Amazon Cognito CSV インポートでパスワードハッシュを指定してみた

Amazon Cognito のユーザーインポート機能がアップデートされ、CSV ファイルにパスワードハッシュを含めてインポートできるようになりました。従来はインポート後にパスワードリセットが必須でしたが、既存パスワードのまま即座にサインインできます。bcrypt と PBKDF2_SHA256 の2種のアルゴリズムで実際にインポートし、認証が成功することを確認しました。
2026.07.16

はじめに

Amazon Cognito のユーザーインポート機能がアップデートされ、CSV ファイルにパスワードハッシュを含めてインポートできるようになりました。従来の CSV インポートでは、インポート後のユーザー状態が RESET_REQUIRED となり、初回サインイン時にパスワードリセットが必須でした。パスワードハッシュを CSV に含めてインポートすると、ユーザー状態は CONFIRMED となり、既存パスワードのまま即座にサインインできます。

https://aws.amazon.com/jp/about-aws/whats-new/2026/07/amazon-cognito-password-hash-import/

本記事では、対応する4種類のアルゴリズムのうち bcrypt と PBKDF2_SHA256 の2種類を取り上げ、パスワードハッシュを CSV へ設定してインポートし、既存パスワードで認証できることを確認しました。

検証内容

検証環境

項目
リージョン ap-northeast-1
ユーザープール username-attributes: email、メール検証あり
アプリクライアント シークレットなし、ALLOW_USER_PASSWORD_AUTH / ALLOW_ADMIN_USER_PASSWORD_AUTH を有効化
AWS CLI v2.35.24
boto3 / botocore 1.43.49
検証したアルゴリズム BCRYPT、PBKDF2_SHA256

事前準備

メールアドレスをサインイン属性とし、メール検証を有効にしたユーザープールを作成しました。

aws cognito-idp create-user-pool \
  --pool-name password-hash-import-test \
  --username-attributes email \
  --auto-verified-attributes email \
  --policies '{"PasswordPolicy":{"MinimumLength":8,"RequireUppercase":true,"RequireLowercase":true,"RequireNumbers":true,"RequireSymbols":true}}' \
  --region ap-northeast-1
{
    "UserPool": {
        "Id": "ap-northeast-1_XXXXXXXXX",
        "Name": "password-hash-import-test",
        "UsernameAttributes": ["email"],
        "AutoVerifiedAttributes": ["email"]
    }
}

インポート直後のユーザーは SRP 認証が使えないため、パスワードを直接送信する認証フローを有効にする必要があります。ALLOW_USER_PASSWORD_AUTHALLOW_ADMIN_USER_PASSWORD_AUTH を有効にしたアプリクライアントを作成しました。シークレットは発行しません。

aws cognito-idp create-user-pool-client \
  --user-pool-id ap-northeast-1_XXXXXXXXX \
  --client-name password-hash-import-client \
  --no-generate-secret \
  --explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_ADMIN_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
  --region ap-northeast-1
{
    "UserPoolClient": {
        "UserPoolId": "ap-northeast-1_XXXXXXXXX",
        "ClientId": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
        "ClientName": "password-hash-import-client",
        "ExplicitAuthFlows": [
            "ALLOW_USER_PASSWORD_AUTH",
            "ALLOW_ADMIN_USER_PASSWORD_AUTH",
            "ALLOW_REFRESH_TOKEN_AUTH"
        ]
    }
}

インポートジョブが CloudWatch Logs に書き込むための IAM ロールを作成しました。信頼ポリシーで cognito-idp.amazonaws.com からの引き受けを許可し、CloudWatch Logs への書き込み権限を付与します。

cat > trust-policy.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "cognito-idp.amazonaws.com"},
    "Action": "sts:AssumeRole"
  }]
}
EOF

aws iam create-role \
  --role-name cognito-import-cloudwatch-role \
  --assume-role-policy-document file://trust-policy.json

aws iam put-role-policy \
  --role-name cognito-import-cloudwatch-role \
  --policy-name cognito-import-logs \
  --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["logs:CreateLogGroup","logs:CreateLogStream","logs:DescribeLogStreams","logs:PutLogEvents"],"Resource":"arn:aws:logs:*:*:*"}]}'
{
    "Role": {
        "RoleName": "cognito-import-cloudwatch-role",
        "Arn": "arn:aws:iam::123456789012:role/cognito-import-cloudwatch-role"
    }
}

bcrypt ハッシュインポート

まず、bcrypt の cost を 10 として、テスト用パスワード TestPassword123! のハッシュを生成しました。Cognito が受け付ける cost の上限は 10 です。

import bcrypt

password = b'TestPassword123!'
salt = bcrypt.gensalt(rounds=10)
hashed = bcrypt.hashpw(password, salt)
print(hashed.decode())
# 出力例: $2b$10$6utAl7aqnqX9VP4tyUsY2Ol9eciwln7Wx71GFPBy1I1P8xpKgfUu6

CSV のヘッダーは、対象ユーザープールの get-csv-header で取得したものをそのまま使う必要があります。

get-csv-header の実行結果
aws cognito-idp get-csv-header \
  --user-pool-id ap-northeast-1_XXXXXXXXX \
  --region ap-northeast-1
{
    "UserPoolId": "ap-northeast-1_XXXXXXXXX",
    "CSVHeader": [
        "profile", "address", "birthdate", "gender", "preferred_username",
        "updated_at", "website", "picture", "phone_number", "phone_number_verified",
        "zoneinfo", "locale", "email", "email_verified", "given_name", "family_name",
        "middle_name", "name", "nickname", "cognito:mfa_enabled", "cognito:username",
        "password_hash"
    ]
}

末尾に password_hash カラムが含まれています。取得した全カラムをヘッダーに含め、password_hash に生成したハッシュを設定しました。username-attributes: email のプールでは、cognito:username にもメールアドレスを設定します。CSV のフォーマットの詳細は公式ドキュメントを参照してください。

profile,address,birthdate,gender,preferred_username,updated_at,website,picture,phone_number,phone_number_verified,zoneinfo,locale,email,email_verified,given_name,family_name,middle_name,name,nickname,cognito:mfa_enabled,cognito:username,password_hash
,,,,,,,,,,,,bcrypt-user-01@example.com,TRUE,,,,,,,bcrypt-user-01@example.com,$2b$10$6utAl7aqnqX9VP4tyUsY2Ol9eciwln7Wx71GFPBy1I1P8xpKgfUu6
,,,,,,,,,,,,bcrypt-user-02@example.com,TRUE,,,,,,,bcrypt-user-02@example.com,$2b$10$6utAl7aqnqX9VP4tyUsY2Ol9eciwln7Wx71GFPBy1I1P8xpKgfUu6

CreateUserImportJob では、新しい PasswordHashingAlgorithm パラメータに BCRYPT を指定します。検証時点の AWS CLI v2.35.24 と boto3 / botocore 1.43.49 ではこのパラメータに未対応だったため、botocore の SigV4 署名を使って HTTP API を直接呼び出しました。以下は実行したコード全文です。

import json
import urllib.request
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
from botocore.session import Session

region = 'ap-northeast-1'
service = 'cognito-idp'
endpoint = f'https://cognito-idp.{region}.amazonaws.com/'

session = Session()
credentials = session.get_credentials().get_frozen_credentials()

payload = json.dumps({
    'JobName': 'bcrypt-import-test-3',
    'UserPoolId': 'ap-northeast-1_XXXXXXXXX',
    'CloudWatchLogsRoleArn': 'arn:aws:iam::123456789012:role/cognito-import-cloudwatch-role',
    'PasswordHashingAlgorithm': 'BCRYPT',
})

headers = {
    'Content-Type': 'application/x-amz-json-1.1',
    'X-Amz-Target': 'AWSCognitoIdentityProviderService.CreateUserImportJob',
}

request = AWSRequest(method='POST', url=endpoint, data=payload, headers=headers)
SigV4Auth(credentials, service, region).add_auth(request)

req = urllib.request.Request(
    endpoint,
    data=payload.encode('utf-8'),
    headers=dict(request.headers),
    method='POST',
)
with urllib.request.urlopen(req) as response:
    print(response.read().decode())

レスポンスには、CSV のアップロード先となる署名付き URL と PasswordHashingAlgorithm が含まれていました。指定した BCRYPT が返ることを確認できました。

CreateUserImportJob のレスポンス
{
    "UserImportJob": {
        "JobName": "bcrypt-import-test-3",
        "JobId": "import-XXXXXXXXXX",
        "UserPoolId": "ap-northeast-1_XXXXXXXXX",
        "PreSignedUrl": "https://cognito-idp-user-import-pdx.s3.us-west-2.amazonaws.com/<masked>",
        "Status": "Created",
        "CloudWatchLogsRoleArn": "arn:aws:iam::123456789012:role/cognito-import-cloudwatch-role",
        "ImportedUsers": 0,
        "SkippedUsers": 0,
        "FailedUsers": 0,
        "PasswordHashingAlgorithm": "BCRYPT"
    }
}

返された署名付き URL に CSV をアップロードしました。

curl -sS -o /dev/null -w '%{http_code}\n' \
  --request PUT \
  --upload-file bcrypt-users.csv \
  'https://cognito-idp-user-import-pdx.s3.us-west-2.amazonaws.com/<masked>'
200

アップロード後、AWS CLI でジョブを開始しました。

aws cognito-idp start-user-import-job \
  --user-pool-id ap-northeast-1_XXXXXXXXX \
  --job-id import-XXXXXXXXXX \
  --region ap-northeast-1
{
    "UserImportJob": {
        "JobId": "import-XXXXXXXXXX",
        "Status": "Pending",
        "PasswordHashingAlgorithm": "BCRYPT"
    }
}

完了後のジョブを確認すると、2ユーザーが正常にインポートされていました。

aws cognito-idp describe-user-import-job \
  --user-pool-id ap-northeast-1_XXXXXXXXX \
  --job-id import-XXXXXXXXXX \
  --region ap-northeast-1
{
    "UserImportJob": {
        "JobName": "bcrypt-import-test-3",
        "JobId": "import-XXXXXXXXXX",
        "Status": "Succeeded",
        "ImportedUsers": 2,
        "SkippedUsers": 0,
        "FailedUsers": 0,
        "PasswordHashingAlgorithm": "BCRYPT",
        "CompletionMessage": "Import Job Completed Successfully."
    }
}

インポートされたユーザーの UserStatusCONFIRMED でした。

aws cognito-idp admin-get-user \
  --user-pool-id ap-northeast-1_XXXXXXXXX \
  --username bcrypt-user-01@example.com \
  --region ap-northeast-1
{
    "Username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "UserStatus": "CONFIRMED",
    "Enabled": true,
    "UserAttributes": [
        {"Name": "email", "Value": "bcrypt-user-01@example.com"},
        {"Name": "email_verified", "Value": "true"}
    ]
}

インポート前から使用していたパスワードで ADMIN_USER_PASSWORD_AUTH を実行すると、トークンを取得できました。

aws cognito-idp admin-initiate-auth \
  --user-pool-id ap-northeast-1_XXXXXXXXX \
  --client-id xxxxxxxxxxxxxxxxxxxxxxxxxx \
  --auth-flow ADMIN_USER_PASSWORD_AUTH \
  --auth-parameters USERNAME=bcrypt-user-01@example.com,PASSWORD=TestPassword123! \
  --region ap-northeast-1
{
    "AuthenticationResult": {
        "AccessToken": "<masked>",
        "ExpiresIn": 3600,
        "TokenType": "Bearer",
        "RefreshToken": "<masked>",
        "IdToken": "<masked>"
    },
    "ChallengeParameters": {}
}

誤ったパスワードでは NotAuthorizedException が返りました。

aws cognito-idp admin-initiate-auth \
  --user-pool-id ap-northeast-1_XXXXXXXXX \
  --client-id xxxxxxxxxxxxxxxxxxxxxxxxxx \
  --auth-flow ADMIN_USER_PASSWORD_AUTH \
  --auth-parameters USERNAME=bcrypt-user-01@example.com,PASSWORD=WrongPassword456! \
  --region ap-northeast-1
An error occurred (NotAuthorizedException) when calling the AdminInitiateAuth operation: Incorrect username or password.

PBKDF2_SHA256 ハッシュインポート

PBKDF2_SHA256 でも同じ流れを検証しました。イテレーション回数は上限値の 600000 とし、以下の形式でハッシュを生成しました。Cognito が受け付けるフォーマットは $pbkdf2-sha256$イテレーション$base64ソルト$base64ハッシュ です。本検証では base64 のパディング(=)を除去した形式でインポートに成功しました。

import hashlib, base64, os

password = b'TestPassword123!'
salt = os.urandom(16)
iterations = 600000
dk = hashlib.pbkdf2_hmac('sha256', password, salt, iterations)

salt_b64 = base64.b64encode(salt).rstrip(b'=').decode()
hash_b64 = base64.b64encode(dk).rstrip(b'=').decode()
hash_str = f"$pbkdf2-sha256${iterations}${salt_b64}${hash_b64}"
print(hash_str)
# 出力例: $pbkdf2-sha256$600000$dFmpDGYWnTMuSn2wC3qo7Q$+1ITLXTq5iiV2SbQQ6loZ0DHz21ZktaFJMNemioCfcE

インポートジョブ作成時は、PasswordHashingAlgorithmPBKDF2_SHA256 を指定します。

payload = json.dumps({
    'JobName': 'pbkdf2-import-test',
    'UserPoolId': 'ap-northeast-1_XXXXXXXXX',
    'CloudWatchLogsRoleArn': 'arn:aws:iam::123456789012:role/cognito-import-cloudwatch-role',
    'PasswordHashingAlgorithm': 'PBKDF2_SHA256',
})

CSV アップロードから認証テストまでは bcrypt と同様の手順で実行しました。

検証項目 bcrypt PBKDF2_SHA256
インポートジョブ Status Succeeded Succeeded
ImportedUsers 2 2
FailedUsers 0 0
ユーザー UserStatus CONFIRMED CONFIRMED
正しいパスワードでの認証 SUCCESS(トークン取得) SUCCESS(トークン取得)
誤パスワードでの認証 NotAuthorizedException NotAuthorizedException
レスポンスの PasswordHashingAlgorithm BCRYPT PBKDF2_SHA256

注意点・Tips

  1. 検証時点の AWS CLI / boto3 は PasswordHashingAlgorithm に未対応
    検証時点(2026-07-16)で使用した AWS CLI v2.35.24 および boto3 / botocore 1.43.49 には PasswordHashingAlgorithm の入力モデルが含まれていませんでした。指定すると以下のエラーになります。

    ParamValidationError: Parameter validation failed:
    Unknown parameter in input: "PasswordHashingAlgorithm", must be one of: JobName, UserPoolId, CloudWatchLogsRoleArn
    

    本検証では botocore の SigV4Auth を使い HTTP API を直接呼び出しました。利用する CLI / SDK のバージョンによっては、正式なパラメータとして指定できる可能性があります。

  2. CSV ヘッダーには get-csv-header で取得した全カラムが必要
    必要な属性だけに絞った CSV を使用すると、ヘッダー不一致でインポートに失敗します。

    The header in the CSV file does not match the expected headers.
    

    get-csv-header が返した全カラムをヘッダーに含め、不要な値は空欄にします。

  3. username-attributes: email のプールでは cognito:username にメールアドレスを指定
    このプール構成でメールアドレス以外を cognito:username に設定すると、インポートに失敗します。

    Username should be an email.
    

    cognito:username にはユーザープールのサインイン属性と整合するメールアドレスを設定します。

  4. インポート直後は SRP 認証が使えない
    初回サインイン前に USER_SRP_AUTH を使うと、認証に失敗します。

    An error occurred (NotAuthorizedException) when calling the InitiateAuth operation: Incorrect username or password.
    

    初回サインインには USER_PASSWORD_AUTH または ADMIN_USER_PASSWORD_AUTH を使用します。初回サインイン後は SRP 認証も利用可能になります。

対応アルゴリズムとパラメータ上限

実機で検証した BCRYPT と PBKDF2_SHA256 以外にも、SCRYPT と ARGON2ID に対応しています。フォーマットとパラメータ上限は公式ドキュメントに記載されています。

アルゴリズム フォーマット パラメータ上限
BCRYPT $2<a/b/x/y>$[cost]$[22-char salt][31-char hash] cost ≤ 10
SCRYPT N$r$p$hexSalt$hexHash N ≤ 65536, r ≤ 8, p ≤ 1
ARGON2ID $argon2id$v=N$m=M,t=T,p=P$salt$hash m ≤ 19456, t ≤ 2, p ≤ 1
PBKDF2_SHA256 $pbkdf2-sha256$iterations$salt$hash iterations ≤ 600000

移行元システムのハッシュが対応アルゴリズム・対応フォーマットに一致しない場合や、これらの上限を超えるパラメータで生成されている場合はインポートできないため、事前に確認が必要です。

まとめ

Amazon Cognito の CSV インポートで、パスワードハッシュを指定できるようになりました。bcrypt と PBKDF2_SHA256 の2種類で検証したところ、どちらもユーザーを CONFIRMED としてインポートでき、インポート前のパスワードによる認証に成功しました。

対応アルゴリズム・対応フォーマット・パラメータ上限を満たすハッシュであれば、ユーザーに一斉のパスワードリセットを求めることなく、既存システムから Cognito へ移行できます。

ただし、この機能のスコープは「他システムから Cognito への移行改善」です。Cognito からパスワードハッシュをエクスポートする手段は引き続き提供されていません。そのため、移行後に別サービスへ再度移行したり、パスワードハッシュを含むバックアップ/リストアを行ったりといったポータビリティが担保されるわけではありません。

Cognito をユーザー認証基盤として採用する際は、インポート機能だけでなく、API 実行レートの上限などの非機能要件も含めた評価をおすすめします。

https://dev.classmethod.jp/articles/cognito-provisioned-limit-service-quota-increase-load-test/

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事