Project

General

Profile

Bug(バグ) #3134

4バイト文字を含んだ文字列を投稿すると、4バイト文字以降のデータが切れた状態で保存されてしまう

Added by Kiwa Sakai over 7 years ago. Updated over 2 years ago.

Status:
Won't fix(対応せず)
Priority:
Normal(通常)
Target version:
Start date:
2012-07-26
Due date:
% Done:

0%

3.6 で発生するか:
Yes (はい)
3.8 で発生するか:
Yes (はい)

Description

現象

MySQL の utf8 では4バイトの文字を扱うことができず、4バイト文字を含む文字列を保存する際に4バイト文字以降が切れてしまった不完全な状態で保存されてしまいます。

この影響で、以下の様な問題が発生します。

  • 必須項目が空の投稿がおこなえる
  • トピック・日記のタイトルに4バイト文字が含まれていると、通知のアクティビティデータが破損し、そのアクティビティを表示する箇所(マイホーム、アクティビティリストなど)が500エラーになる
    500 | Internal Server Error | Doctrine_Table_Exception
    Unserialization of template_param failed.

UTF-8 の4バイト文字は、JIS X 0213 第3・第4水準漢字の一部や、Unicode 6.0 の携帯電話の絵文字の一部が該当します。
Unicode 6.0 で定義された携帯電話の絵文字は iOS 5 の絵文字としても既に使われていますが、このうち U+10000 以降のものは UTF-8 では4バイト文字となります。

参考

UTF-8で4バイトになる文字
http://www.softel.co.jp/blogs/tech/archives/596

Unicode6.0の携帯電話の絵文字の一覧 - Wikipedia
http://ja.wikipedia.org/wiki/Unicode6.0%E3%81%AE%E6%90%BA%E5%B8%AF%E9%9B%BB%E8%A9%B1%E3%81%AE%E7%B5%B5%E6%96%87%E5%AD%97%E3%81%AE%E4%B8%80%E8%A6%A7

iOS Emoji
http://punchdrunker.github.com/iOSEmoji/table_html/

現象確認バージョン

OpenPNE-3.6.4

原因

修正内容


Related issues

Related to OpenPNE 3 - Backport(バックポート) #3179: 4バイト文字を含んだ文字列を投稿すると、4バイト文字以降のデータが切れた状態で保存されてしまう Fixed(完了) 2012-07-26
Related to OpenPNE 3 - Backport(バックポート) #3255: 4バイト文字を含んだ文字列を投稿すると、4バイト文字以降のデータが切れた状態で保存されてしまう Fixed(完了) 2012-11-08

Associated revisions

Revision 61b427a8 (diff)
Added by Kousuke Ebihara almost 7 years ago

changed opDoctrineRecord::_set() to replace 4 bytes UTF-8 characters to U+FFFD in MySQL non binary string column (refs #3134)

History

#1 Updated by Kiwa Sakai over 7 years ago

  • Description updated (diff)

#2 Updated by Kiwa Sakai over 7 years ago

  • Description updated (diff)

#3 Updated by Kiwa Sakai over 7 years ago

  • Description updated (diff)

#4 Updated by Kousuke Ebihara about 7 years ago

  • 3.8 で発生するか set to Yes (はい)

#5 Updated by Yuma Sakata about 7 years ago

  • Target version set to OpenPNE 3.8.2

#6 Updated by Yuma Sakata about 7 years ago

  • Target version changed from OpenPNE 3.8.2 to OpenPNE 3.9.0-old

#7 Updated by Yuya Watanabe about 7 years ago

  • Assignee set to Yuya Watanabe

#8 Updated by Yuya Watanabe about 7 years ago

メモ

対策としては 2通り考えられるが,先に結論を述べておくと 後者を選択する

  1. 4byte 文字をサポートできる仕様にする
  2. 4byte 文字を受け付けない仕様にする

前者の場合,データベース自体(この場合 MySQL)が 4byte 文字に対応していないということになり,この MySQL のバージョンアップなりの対応で対応できるが,すでに動いている SNS などを考慮すると MySQL のバージョンを上げることが難しい場合もあるため今回はこの方法を取らない.次期 OpenPNE バージョンなどで対応できるように検討することが適切であるとおもわれる.

基本的に本チケット(本バージョン)では後者の場合を取りうるが,この場合での検討事項は どのタイミングで 4byte 文字を切り落とすかとなる.
この問題はおそらく MySQL のバージョンや使用する文字コードなどが影響しているため Doctrine 以下で行いたいところが,データ格納時などに切り落とす処理を行うと影響範囲が大きく,Symfony で提供されているバリデーションがうまく働かなくなる可能性なども十分にありえるためあまり得策ではないといえる.

OpenPNE では似たような問題として ヌルバイト文字にも対応していて,これはリクエスト時にヌルバイトを切り落とすかどうかを指定して,切り落とした文字列を用いている.本チケットの問題も同様の対処により解決できるのではとかんがえられる.

つまりリクエストから文字列を取得してくる際に 4byte 文字を削除するような対策を取る.
この対策を施した OpenPNE では 4byte 文字を扱えないという仕様になり, 4byte 文字を削除した文字列を入力値として扱う.
こうすることで,空文字列が許容されないフォームに対しても 4byte 文字のみで構成された場合に空文字列として扱わせることで Symfony の Validator をそのまま用いることができる.

#9 Updated by Mutsumi Imamura about 7 years ago

  • Description updated (diff)

#10 Updated by Yuya Watanabe about 7 years ago

4byte 文字がすでに存在する場合があるかどうかを調査する必要がある.

#11 Updated by Yuya Watanabe about 7 years ago

すでに投稿された 4byte 文字に関係するものはDBに入っていないため考慮する必要はないが,アクティビティ通知のテンプレート部分が不正な場合のアクティビティのを削除するなどの対処を行う必要がある可能性がある.

+----+-----------+-------------------------+----------+-------------------+-------------+-------+-----------+--------+------------+---------------+------------+----------+-------------------------+---------------------+---------------------+
| id | member_id | in_reply_to_activity_id | body     | uri               | public_flag | is_pc | is_mobile | source | source_uri | foreign_table | foreign_id | template | template_param          | created_at          | updated_at          |
+----+-----------+-------------------------+----------+-------------------+-------------+-------+-----------+--------+------------+---------------+------------+----------+-------------------------+---------------------+---------------------+
|  3 |         1 |                    NULL | [Diary]  | @diary_show?id=45 |           1 |     1 |         1 | Diary  | NULL       | NULL          |       NULL | diary    | a:1:{s:3:"%1%";s:1514:" | 2012-09-12 13:24:11 | 2012-09-12 13:24:11 | 
+----+-----------+-------------------------+----------+-------------------+-------------+-------+-----------+--------+------------+---------------+------------+----------+-------------------------+---------------------+---------------------+

正しそうなデータは以下のとおり.

+----+-----------+-------------------------+----------------------+-------------------+-------------+-------+-----------+--------+------------+---------------+------------+----------+--------------------------------------+---------------------+---------------------+
| id | member_id | in_reply_to_activity_id | body                 | uri               | public_flag | is_pc | is_mobile | source | source_uri | foreign_table | foreign_id | template | template_param                       | created_at          | updated_at          |
+----+-----------+-------------------------+----------------------+-------------------+-------------+-------+-----------+--------+------------+---------------+------------+----------+--------------------------------------+---------------------+---------------------+
|  4 |         1 |                    NULL | [Diary] タイトル     | @diary_show?id=46 |           1 |     1 |         1 | Diary  | NULL       | NULL          |       NULL | diary    | a:1:{s:3:"%1%";s:12:"タイトル";}     | 2012-09-12 13:28:29 | 2012-09-12 13:28:29 | 
+----+-----------+-------------------------+----------------------+-------------------+-------------+-------+-----------+--------+------------+---------------+------------+----------+--------------------------------------+---------------------+---------------------+

#12 Updated by Yuya Watanabe about 7 years ago

  • Web サーバの外側から入ってくる任意の文字列で対応する必要がある
  • OpenPNE の PHP では UTF-8 を用いており,4byte UTF8 についての考慮はなされていない
  • 携帯電話のメールは Shift-JIS であり,4byte UTF8 について(おそらく)考慮する必要はない

外からの入力が必ず通る Request あたりで OpenPNE で変更出来る部分として opWebRequest のパラメータ取得を修正しようとした.
pc_frontend アプリケーションの場合ではうまくいくが api アプリケーションではうまくいかない.リクエストのオブジェクトとして opWebRequest を用いていないためである.
sfWebRequest であるならば request.filter_parameters のようなイベントを用いることができるが,これは初期化の時点で発行されるためにこのイベント発行時点での Request の中身を書き換えてしまう必要がある.

正しそうな修正としては api アプリケーションでも opWebRequest を用いることが考えられるが,本チケットの修正を対象とする修正としては少々大きいため,他の動作に影響がないか調査する必要がある.

#13 Updated by Mutsumi Imamura almost 7 years ago

  • Assignee changed from Yuya Watanabe to Kousuke Ebihara

#14 Updated by Kousuke Ebihara almost 7 years ago

  • Status changed from New(新規) to Pending Review(レビュー待ち)
  • % Done changed from 0 to 50

#15 Updated by Kousuke Ebihara almost 7 years ago

opDoctrineRecord のセッター経由で値を設定する際、カラムが非バイナリ文字列をストアするものである場合で、 DBMS として MySQL を使用している場合は、文字列中に含まれる 4 バイト UTF-8 の文字を U+FFFD に置換するように変更しました。

ただし、 MySQL を使用している場合でも 4 バイト UTF-8 に対応可能な環境についてこの挙動を回避できるよう、 Doctrine の接続設定として ATTR_4BYTES_UTF8_READY (999) を受け入れられるようにしました。以下のように設定することで U+FFFD の置換がおこなわれなくなります。

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn: 'mysql:dbname=example;host=localhost'
      username: root
      encoding: utf8
      attributes: { 164: true, 999: true } # 999: true を追加

#16 Updated by Rimpei Ogawa almost 7 years ago

  • Status changed from Pending Review(レビュー待ち) to Pending Testing(テスト待ち)
  • % Done changed from 50 to 70

#18 Updated by isao sano over 2 years ago

  • Status changed from Pending Testing(テスト待ち) to Won't fix(対応せず)
  • % Done changed from 70 to 0

OpenPNE 3.8.4 にて対応済みであったため、対応せずとします。

Also available in: Atom PDF