[新機能] Amazon Cognito CSV インポートでパスワードハッシュを指定してみた
はじめに
Amazon Cognito のユーザーインポート機能がアップデートされ、CSV ファイルにパスワードハッシュを含めてインポートできるようになりました。従来の CSV インポートでは、インポート後のユーザー状態が RESET_REQUIRED となり、初回サインイン時にパスワードリセットが必須でした。パスワードハッシュを CSV に含めてインポートすると、ユーザー状態は CONFIRMED となり、既存パスワードのまま即座にサインインできます。
本記事では、対応する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_AUTH と ALLOW_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."
}
}
インポートされたユーザーの UserStatus は CONFIRMED でした。
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
インポートジョブ作成時は、PasswordHashingAlgorithm に PBKDF2_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
-
検証時点の 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 のバージョンによっては、正式なパラメータとして指定できる可能性があります。 -
CSV ヘッダーには
get-csv-headerで取得した全カラムが必要
必要な属性だけに絞った CSV を使用すると、ヘッダー不一致でインポートに失敗します。The header in the CSV file does not match the expected headers.get-csv-headerが返した全カラムをヘッダーに含め、不要な値は空欄にします。 -
username-attributes: emailのプールではcognito:usernameにメールアドレスを指定
このプール構成でメールアドレス以外をcognito:usernameに設定すると、インポートに失敗します。Username should be an email.cognito:usernameにはユーザープールのサインイン属性と整合するメールアドレスを設定します。 -
インポート直後は 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 実行レートの上限などの非機能要件も含めた評価をおすすめします。







