April 24, 2008

MySQL 4.1での日本語文字化けのメカニズム(?)

v4.0までの文字コードの扱い

  • サーバだけが設定された文字セットで動作する。
  • クライアントはサーバの文字セットに合わせて動作する。

v4.1からの文字セットの扱い
  • サーバとクライアントがそれぞれ別に設定された文字セットで動作する。
  • サーバと異なる文字セットのクライアントが接続してきた場合、サーバはsjisに変換してからクライアントにデータを送る。
    このとき変換できない文字は’?’になったりおかしな文字になる。

  • ありがちなケースとしては、サーバはきちんとujisに設定したがクライアントがlatin1というケース。
  • 一般に配布されているPHPやMySQLやlibmysqlは明記されてない限りはlatin1が標準になっている可能性が高い(MySQL ABで配布しているバイナリもlatin1)。

  • クライアントの文字セットを合わせるにはmy.cnfで適切な設定を行うか、mysqlクライアントの起動時にオプションで明示的に指定する必要がある。
  • しかしlibmysqlの場合はmy.cnfを読んでくれないし、オプションを指定することもできない。
    ※PHP5.2.3以降であればmysql_set_charset()で指定可能。
  • この場合はデフォルトの文字セットを希望のものに指定してコンパイルし直すか、SET NAMES文を発行してクライアントの文字セットを指定する。
  • ただし、SET NAMES文をプログラム中から行う場合はEncoding-Based SQL Injectionの危険性が伴う。
  • SET NAMESでの指定はmysql_real_escape_string()に影響を及ぼさない。
    というかmysql_client_encoding()の値が変化していない。
  • つまり適切なエスケープが行えないためインジェクションを引き起こす可能性がある。
    参考:http://goungoun.dip.jp/app/fswiki/wiki.cgi/devnotebook?page=MySQL%A1%A2%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8%B8%C0%B8%EC%A4%AB%A4%E9+SET+NAMES+%A4%CF%A5%BB%A5%AD%A5%E5%A5%EA%A5%C6%A5%A3%A4%CB%CC%E4%C2%EA%A4%A2%A4%EA

mysqldumpの文字化け
  • v4.1からはコンパイル時の設定にかかわらずutf8でダンプを行うようになった。
  • my.cnfか実行時に明示的にbinaryを指定するのが無難。
  • またBLOBを含む場合には—hex-blobオプションを指定する。