◀ 16.

MySQLのCSV出力とAccess denied|認証・SELECT・FILE権限を切り分ける

▶

CSV出力中のAccess deniedは、まず「どの接続/SQL/ファイル操作で失敗したか」を確認します。1045だけでFILE権限不足やパスワード誤りの一つへ断定しません。ユーザー/host/接続方法/製品版と、実行した処理を分けて調べます。

MySQLの接続、SELECT、サーバー出力とクライアント側CSV変換を分ける

最初に切り分ける3段階

段階確認避けること
接続開始実際の接続先/ユーザー/認証/設定。using password: YESは送信した意味で、正しい証明ではない全host許可・認証方式の一律変更
接続後のSELECT選ばれたアカウントと表の参照権限GRANT ALLやroot常用
ファイル出力クライアント側かサーバー側か、パス/OS権限/形式CSVのために無条件のFILE権限付与

MySQL 8.4.13の隔離検体では、誤パスワードの接続が1045/28000、接続済みのINSERT拒否が1142、FILEなしのINTO OUTFILEが1227/42000でした。全版/全クライアントの番号を保証する一覧ではありません。サーバーログや接続設定を公開する時は資格情報を伏せ、接続先/経路を変えた再試行で診断条件を混ぜないでください。

読取りだけで接続と権限を確認する

次は接続済みの利用アカウントで行う読取りです。cwiki_export.notesは検体の表なので、実環境では権限のある対象に置き換えます。USER()は接続側の識別、CURRENT_USER()は認証に使われたアカウントの確認に使い、両者が常に同じとは考えません。アカウント一覧を得るために一般ユーザーへmysql.userの読取り権限を広げません。

-- Run only on an already authorized connection; this changes no privileges.
SELECT VERSION(), USER(), CURRENT_USER(), DATABASE();
SHOW GRANTS;
SELECT id, note FROM cwiki_export.notes ORDER BY id LIMIT 1000;

CREATE USER/GRANT/ALTER USERを行う必要が確認された場合も、管理者が対象のアカウントと操作を限定します。現行MySQLではCREATE USERとGRANTを分け、通常これらの後にFLUSH PRIVILEGESは不要です。権限表を直接更新する手順に置き換えません。hostの照合は具体的なエントリや名前解決等に依存し、localhost/127.0.0.1/%を無条件の一対一対応にしません。

サーバー側OUTFILEとクライアント側出力を区別する

SELECT ... INTO OUTFILEはサーバーのファイルへ書く操作で、FILEに加えてsecure_file_privやDBサーバーOSの権限、既存ファイルを上書きできない条件等を確認します。一般のCSV取得のためにFILEを与える必要はありません。クライアント側でSELECT結果を受け取ってファイルにするなら、対象表のSELECT権限とクライアント側の保存先/OS権限を確認します。MySQLのmysqldumpは通常SQLバックアップであり、名前だけでCSV出力とは扱いません。

小さな結果を正しくCSVにする例

接続済みアカウントで上のSELECTが通ることを確認後、mysqlの--xmlで同じ固定2列を取得する例です。以下の接続情報はローカル隔離検体だけのものです。実パスワードは-pの対話入力にし、-pPASSWORDのように引数へ直書きしません。既存設定が接続先等を上書きしないか別途確認し、実サーバーでこの検体名を作成しないでください。

mysql --default-character-set=utf8mb4 --xml -u cwiki_export -p --execute="SELECT id, note FROM cwiki_export.notes ORDER BY id LIMIT 1000" > new-export.xml
python export_csv.py < new-export.xml > new-export.csv

このコマンド表記はPOSIXシェル向けです。PowerShell/cmdの入力リダイレクト等は同じ構文ではありません。前のmysqlが正常終了した場合だけ変換します。リダイレクトは既存ファイルを先に切り詰め得るため、新しい出力先を使い、コマンドの終了状態と結果を確認してから成果物にします。XMLも個人情報等を含む中間ファイルなのでアクセス権/保管/廃棄は既存の運用ルールに従います。大量抽出・全件保証・同時更新の整合性を実装した例ではありません。

"""Convert one small mysql --xml result, not arbitrary SQL/XML or spreadsheet-safe output."""
import csv
import io
import sys
import xml.etree.ElementTree as ET

MAX_BYTES = 2 * 1024 * 1024
NIL = "{http://www.w3.org/2001/XMLSchema-instance}nil"


def convert(raw: bytes) -> str:
    if len(raw) > MAX_BYTES:
        raise ValueError("Unsupported XML input")
    text = raw.decode("utf-8")
    if "\x00" in text or "<!DOCTYPE" in text.upper() or "<!ENTITY" in text.upper():
        raise ValueError("Unsupported XML input")
    root = ET.fromstring(text)
    if root.tag != "resultset" or len(root) > 1000:
        raise ValueError("Expected one bounded result set")
    output = io.StringIO(newline="")
    writer = csv.writer(output)
    writer.writerow(["id", "note", "note_is_null"])
    for row in root:
        if row.tag != "row" or len(row) != 2:
            raise ValueError("Expected two fields")
        fields = list(row)
        if any(f.tag != "field" or len(f) for f in fields):
            raise ValueError("Unexpected field shape")
        if [f.get("name") for f in fields] != ["id", "note"]:
            raise ValueError("Unexpected field order")
        identifier = fields[0].text or ""
        if fields[0].get(NIL) is not None or not identifier.isascii() or not identifier.isdigit() or len(identifier) > 20:
            raise ValueError("Expected a decimal identifier")
        note = fields[1].text or ""
        nil = fields[1].get(NIL)
        if nil not in (None, "true") or (nil == "true" and note) or len(note) > 65536:
            raise ValueError("Invalid note")
        writer.writerow([identifier, note, "true" if nil == "true" else "false"])
    return output.getvalue()


if __name__ == "__main__":
    try:
        result = convert(sys.stdin.buffer.read(MAX_BYTES + 1))
    except (ValueError, ET.ParseError):
        sys.stderr.write("Invalid or unsupported export input\n")
        raise SystemExit(1)
    sys.stdout.buffer.write(result.encode("utf-8"))

Python標準ライブラリだけの変換で、入力は最大2MiB/1000行/固定idとnoteの2列/1セル65536文字までの局所的な受入条件です。任意のMySQL/XML型・制御文字・バイナリ・複数結果を完全対応するものではありません。NULLはnote_is_null列で区別し、空文字や文字列NULLと混同しません。カンマ/引用符/改行/タブ/日本語をcsv.writerで扱い、sedでタブをカンマへ置換しません。

CSVの引用符は表計算の数式実行を止める機能ではありません。このデータ交換例は=1+1等を元値のまま保持します。信頼できないCSVをExcel等で直接開かず、表計算向け出力が必要なら値を変える方針・取込み方法を別に設計してください。

認証方式と復旧操作の注意

mysql_native_passwordはMySQL 8.0.34で非推奨、8.4で既定無効、9.0で削除されています。default_authentication_pluginも8.4では削除済みです。古いクライアントを理由にサーバー全体を旧方式へ戻さず、対象クライアントの対応版・TLS/認証設定を確認します。MariaDBの認証仕様をMySQLと同一視しません。

下のroot復旧用の旧停止/認証スキップ/killallは実行しないでください。通常のCSV調査とは別の管理作業であり、複数インスタンスや既存接続/データへ影響します。正式な版別復旧手順、バックアップ、停止対象と作業承認が必要です。この記事の検証では既存DBの停止/パスワード再設定を行っていません。

以前の説明と例

現在版の表/コード/章を以下に保持し、コピーされやすい旧例には直前で個別に訂正します。さらに元v5のCSVコマンドとFILEの表も末尾で復元しますが、危険/不完全な旧手順としての保守記録です。

旧例の注意:旧要点のGRANTとIDENTIFIED BYを組み合わせた文は現行MySQLの手順ではありません。通常のGRANT/CREATE USER後にFLUSHは不要です。接続/SELECT/出力の3段階で原因を確認します。

この記事の要点
  • ERROR 1045 (28000): Access denied for user は MySQL 認証失敗エラー
  • 主な原因 6 つ: パスワード誤り / ホスト不一致 / ユーザー未作成 / 認証プラグイン差異 / 権限不足 / my.cnf の設定誤り
  • 対処: GRANT ALL ON db.* TO "user"@"%" IDENTIFIED BY "pw"; + FLUSH PRIVILEGES;
  • caching_sha2_password(MySQL 8 既定)と mysql_native_password(旧クライアント)の互換性問題に注意
  • 確認: SELECT user, host FROM mysql.user; で実在ユーザーとホスト範囲を確認

このエラーの概要

MySQL / MariaDB に接続しようとしたときに次のメッセージが返ります:

旧例の注意:エラーメッセージの記録です。using password: YESはパスワードが正しい意味ではありません。発生した段階と版も確認します。

ERROR 1045 (28000): Access denied for user 'username'@'localhost' (using password: YES)

接続開始時にこのエラーが出た場合は、認証やアカウント照合を確認します。CSV処理のどの接続/SQLで返ったかを特定し、SQLSTATEだけでファイル権限不足へ断定しません。

原因の切り分け

旧例の注意:旧原因表はYESだけで誤パスワードと断定し、%や旧認証pluginへ変更する対処を含みます。これらは推奨しません。アカウント照合/権限/版を調べ、無条件の全host許可や権限拡大をしません。

原因確認方法対処
パスワード誤りメッセージ末尾 (using password: YES)正しいパスワードで接続、リセット
ユーザー未作成SELECT user, host FROM mysql.user;CREATE USER で作成
ホスト不一致同上、host 列確認'user'@'%' で全ホスト許可、または特定 IP 指定
認証プラグイン違いSELECT user, plugin FROM mysql.user;ALTER USER ... IDENTIFIED WITH mysql_native_password
権限不足SHOW GRANTS FOR 'user'@'host';GRANT で追加
パスワードファイル誤りmy.cnf の [client] セクション正しいパスワードに書換え

対処1: ユーザー / ホストを確認

旧例の注意:旧コードはシェルとSQLが混在し、%がlocalhostを含まないという一律の説明も不正確です。接続方法と実際のCURRENT_USER()を調べ、エントリを無条件に二重作成しません。

-- root で接続して確認(root 自体が拒否される場合は対処5参照)
mysql -u root -p

-- 全ユーザー一覧
SELECT user, host, plugin FROM mysql.user ORDER BY user, host;

-- 'app_user'@'localhost' で接続できないが、'app_user'@'%' は存在する場合
-- → クライアント側で別ホストから接続しているのに 'localhost' エントリでログインしようとしている

-- ホスト '%' は「任意のホスト」だが localhost を含まない MySQL 8.x の挙動に注意

対処2: ユーザー作成 / 権限付与

旧例の注意:これは全hostやDB全体の広い権限を与える旧例で、CSV取得のために実行しません。対象表への必要な操作だけを管理者が設計し、公開パスワードを実運用に使いません。

-- ユーザー新規作成(localhost からのみ)
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'StrongP@ssw0rd!';

-- 任意ホストから接続可能なユーザー
CREATE USER 'app_user'@'%' IDENTIFIED BY 'StrongP@ssw0rd!';

-- 権限付与(特定 DB の全権限)
GRANT ALL PRIVILEGES ON mydb.* TO 'app_user'@'localhost';
GRANT ALL PRIVILEGES ON mydb.* TO 'app_user'@'%';

-- 反映
FLUSH PRIVILEGES;

-- 確認
SHOW GRANTS FOR 'app_user'@'localhost';

対処3: パスワードを変更(リセット)

旧例の注意:パスワード変更は既存接続/アプリへ影響します。旧PASSWORD()例は現行MySQLで使わず、通常のALTER USER後にFLUSHは不要です。この調査で実アカウントを変更しません。

-- MySQL 5.7+ / 8.x
ALTER USER 'app_user'@'localhost' IDENTIFIED BY 'NewP@ssw0rd!';

-- 同じ user を別ホストごとに変更
ALTER USER 'app_user'@'%' IDENTIFIED BY 'NewP@ssw0rd!';

-- 旧式
SET PASSWORD FOR 'app_user'@'localhost' = PASSWORD('NewP@ssw0rd!');  -- MySQL 5.6 まで

FLUSH PRIVILEGES;

対処4: 認証プラグインの違い(MySQL 8 でハマる)

対象クライアントの認証対応とサーバーの版を照合します。未対応時のエラーが常に1045になるとは限りません。旧pluginへ変更する操作を先に実行しません。

旧例の注意:旧認証方式への変更は既定の対処にしません。mysql_native_passwordは8.4で既定無効、9.0では削除です。対応クライアントと認証/TLS設定を確認します。

-- 現在のプラグイン確認
SELECT user, host, plugin FROM mysql.user WHERE user = 'app_user';
-- user      | host       | plugin
-- app_user  | localhost  | caching_sha2_password   ← これだと旧クライアントから繋がらない

-- 旧式プラグインに変更
ALTER USER 'app_user'@'localhost'
  IDENTIFIED WITH mysql_native_password BY 'StrongP@ssw0rd!';

FLUSH PRIVILEGES;

次のサーバー既定変更は旧記録で、現在の対処として使いません。8.4以降へ設定を追加しないでください。

旧例の注意:default_authentication_pluginはMySQL 8.4では削除されています。下の旧設定を現在のmy.cnfへ追加しないでください。

# /etc/mysql/my.cnf または /etc/my.cnf
[mysqld]
default_authentication_plugin = mysql_native_password

対処5: root パスワード忘れ / root 自体がはじかれる

旧例の注意:実行禁止の旧復旧記録です。停止/認証スキップ/killallは通常のCSV調査とは別で、既存インスタンス/データへ影響します。正式な版別復旧計画/承認/バックアップなしに実行しません。

# MySQL を停止
sudo systemctl stop mysql

# セーフモード(認証スキップ)で起動
sudo mysqld_safe --skip-grant-tables --skip-networking &

# パスワード無しで root 接続
mysql -u root

# パスワード再設定
mysql> FLUSH PRIVILEGES;
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewRootPass!';
mysql> EXIT;

# 通常起動に戻す
sudo killall mysqld
sudo systemctl start mysql

# 新パスワードで接続
mysql -u root -p

対処6: クライアント設定 (my.cnf / .my.cnf) を確認

実際に使うクライアントの版/オプションファイル探索とグループを確認します。mysqldumpの通常出力はSQLであり、MySQL 8.4で削除されたmysqlpumpを現在のCSVツールとして案内しません。

旧例の注意:資格情報の平文配置例を保持したものです。実値へ置換して公開せず、所有者/ACL/保管方針を確認します。SQLバックアップ用ツールとCSV変換も区別します。

# ~/.my.cnf  ← 個人用
[client]
user     = app_user
password = StrongP@ssw0rd!
host     = localhost

[mysqldump]
user     = backup_user
password = BackupP@ss!

旧例の注意:既存ファイルへのchmod/上書きを無条件に実行しません。600はPOSIX上の典型で全環境共通の必須値ではありません。--defaults-fileの値中の~はシェルで展開されない場合があるため確認した絶対パス等を使います。--no-defaultsでもlogin-pathの読込みが残る条件を確認します。

# パーミッション 600 が必須
chmod 600 ~/.my.cnf

# 設定ファイルを明示指定
mysqldump --defaults-file=~/.my.cnf mydb > backup.sql

# 設定ファイル無視
mysqldump --no-defaults -u app_user -p mydb > backup.sql

typical な接続例

旧例の注意:接続先/資格情報は検体の例です。--ssl-mode=REQUIREDは暗号化の要求で、接続先ホストの識別検証の保証ではありません。実接続では確認済みCA/ホストを検証し、rootでのCSV出力を常用しません。

# 基本
mysql -u app_user -p mydb
# Enter password: ********

# ホスト・ポート指定
mysql -h 192.168.1.10 -P 3306 -u app_user -p mydb

# SSL 接続
mysql --ssl-mode=REQUIRED -u app_user -p -h db.example.com

# SOCKET 経由(localhost のみ)
mysql -S /var/run/mysqld/mysqld.sock -u root -p

FAQ

Q: 'user'@'%' を作ったのに localhost から繋がらない
A: アカウントはuserとhostの組で区別されますが、経路と照合対象を確認し、両方の作成やTCPへ変更するだけで直るとは考えません。接続できた場合はCURRENT_USER()を確認し、失敗時は管理者が候補エントリ/名前解決等を限定して調べます。

Q: パスワード正しいのに 1045 が出続ける
A: 1) パスワード末尾のスペース、2) 認証プラグインの違い、3) ホスト名解決の失敗(DNS)、4) MySQL の skip-name-resolve 設定、を順に確認。

Q: phpMyAdmin で 1045
A: config.inc.php の $cfg['Servers'][$i]['host']、['user']、['password'] を確認。host = 'localhost' と '127.0.0.1' で挙動が変わるので注意。

関連エラー

  • ERROR 1044 (42000): Access denied for user ... to database — DB 単位の権限不足、GRANT で対処
  • ERROR 2002 (HY000): Can't connect to local MySQL server through socket — 停止だけでなくsocketのパス/権限/接続先の違い等も確認
  • ERROR 2003 (HY000): Can't connect to MySQL server — ホスト到達不可、ファイアウォール

元v5のCSVとFILEの記録

元例の注意:元のエラーメッセージです。この番号だけでファイル書込み権限不足とは断定しません。

ERROR 1045 (28000): Access denied for user 'username'@'localhost' (using password: YES)

元例の注意:実行しない旧コマンドです。-pへの秘密値直書き、port変数名の誤記poty、sedでは引用符/改行/NULLを正しくCSV化できない点、出力上書きに注意します。上の限定XML/CSV例を参照してください。

mysql -h ${host_name} --port ${poty} -u ${user_name} -p${password} ${databasename} -e "${SQL}" | sed -e 's/^/"/g' | sed -e 's/$/"/g' | sed -e 's/\t/","/g' > /path/to/hoge.csv

元例の注意:FILEを無条件に付与する旧例です。サーバー側ファイル操作の強い権限で、一般のクライアント側CSV取得には不要です。実行しません。

grant file on *.* to hoge@localhost

確認範囲と関連情報

2026年10月26日確認。ローカルMySQL 8.4.13の隔離検体で認証/SELECT/FILEなしの出力と合成データのXML→CSVを検査しています。MariaDB/旧MySQL/全クライアント/実TLS・GUI・全CSV型や大規模抽出は未検証。本番DBのユーザー/権限/認証/OS設定を変更していません。

親のエラー一覧から子ページを選択できます。MySQL & MariaDBの親カテゴリも参照してください。

公式資料:接続問題、INTO OUTFILE、mysqlクライアントのXML等、権限変更の反映、旧認証plugin、8.4の削除項目。

子ページ

子ページはありません

同階層のページ
  1. 1071 Specified key was too long; max key length is 767 bytes
  2. ERROR 1063 (42000): Incorrect column specifier for column '~'
  3. mysqld: Can't change dir to '...\MySQL\MySQL Server X.X\data\' (OS errno 2 - No such file or directory)
  4. Install/Remove of the Service Denied!
  5. Datetime 型が NULL に見える
  6. Warning: World-writable config file '/etc/mysql/my.cnf' is ignored
  7. ERROR 1698 (28000): Access denied for user 'root'@'localhost'
  8. Exception: Wrong MySQL configuration
  9. [Warning] TIMESTAMP with implicit DEFAULT value is deprecated.
  10. ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
  11. ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
  12. Incorrect column specifier for column 'カラム名'
  13. BLOB/TEXT column 'description' used in key specification without a key length
  14. ERROR: /bin/sh: mysql_config: コマンドが見つかりません
  15. Host '...' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
  16. CSVエクスポート時に「ERROR 1045 (28000): Access denied for user 'username'@'localhost'」エラーが表示される
  17. Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT
  18. SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: ~

最近更新/作成されたページ