テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

プログラム(機能)関連の開発の話題
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

かなり久しぶりの投稿です。よろしくお願いいたします。
どちらのカテゴリがよいか分からなかったのですが、日本語版特有の問題のような気がしたのでこちらに投稿します。

現在、CentOS4(php 4.3.9, mysql 4.1.22), UTF-8 の環境で 1.0.5J のサイトを運用しています。日本語の入力については全く問題がないのですが、テンプレート変数により用意したフォーム内に簡体字中国語を入力した場合、簡体字の部分だけ抜け落ちてしまう現象にぶつかりました。

例えば「日本东京都」と入力した場合、"東"にあたる"东"だけが抜け落ちます。IEだと文字が消えたように見えますが、FireFoxですと◆形に?の文字化けのように見えます。
違う環境で試してみたところ、

CentOS5(php 5.1.6, mysql 5.0.77), 1.0.5J-r1 入力不可
CentOS5(php 5.1.6, mysql 5.0.77), 1.0.4J-r4 入力可
CentOS4(php 4.3.9, mysql 4.1.22, 問題の1.0.5Jと同じサーバです), 0.9.6 入力可

という結果でした。
.htaccess内のmbstring関連の設定は一致させありますが変わらずです。直感的には 1.0.5J以降で問題が出始めたのではと考えています。

まだコードを追っている最中なのですが、 例えば、/manager/processors/save_content.processor.php 内の 498行以下のコードに次のような箇所があり、

コード: 全て選択

if (isset($tvIds[$tvId])) {
	$tvChanges[] = array(array('tmplvarid' => $tvId, 'contentid' => $id, 'value' => $modx->db->escape($tvVal)), array('id' => $tvIds[$tvId]));
} else {
	$tvAdded[] = array('tmplvarid' => $tvId, 'contentid' => $id, 'value' => $modx->db->escape($tvVal));
}
テンプレート変数の値を $modx->db->escape でエスケープしている箇所があります。このエスケープ処理の前後でテンプレート変数の内容が変化しているようで、先の例ならば「日本东京都」の"东"が落ちて「日本\\0京都」となっていました。
この $modx->db->escape 処理のどこかに問題があるのだと思いますが、1.0.5J-r3で既に改善されているということはあるでしょうか。modx_site_contentに格納されるintrotextなどの通常項目も同じエスケープ処理がされるはずですが、こちらは問題がなく、どうも理由が掴みきれない原因にもなっています。
1.0.5jのリリースノートにescape処理の変更が記載されていたので、こちらでも1.0.4j-r4と1.0.5jの該当部分を比較してみようかと思います。

何か情報があればお助け頂ければ幸いです。よろしくお願いいたします。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

いくつかのバージョンのコードを見てみました。

0.9.6, 1.0.4J-r4 では

コード: 全て選択

function escape($s) {
      if (function_exists('mysql_real_escape_string') && $this->conn) {
         $s = mysql_real_escape_string($s, $this->conn);
      } else {
         $s = mysql_escape_string($s);
      }
      return $s;
   }
1.0.5Jでは

コード: 全て選択

   function escape($s) {
      if (function_exists('mysql_set_charset') && $this->conn)
      {
         $s = mysql_real_escape_string($s, $this->conn);
      }
      elseif ($this->config['charset']=='utf8' && $this->conn)
      {
         $s = mb_convert_encoding($s, 'eucjp-win', 'utf-8');
         $s = mysql_real_escape_string($s, $this->conn);
         $s = mb_convert_encoding($s, 'utf-8', 'eucjp-win');
      }
      else
      {
         $s = mysql_escape_string($s);
      }
      return $s;
   }
となっていました。
1.0.5J以外ではmysql_real_escape_string 関数の有無をチェックして処理を分けていますが、PHP4.3.x以降では存在しているので mysql_real_escape_string 関数によるエスケープ処理になりますね。1.0.5Jは mysql_set_charset の有無を見ているので PHP5.2.3 未満では1.0.5Jから追加されたエスケープ処理に移動するようです。

コード: 全て選択

         $s = mb_convert_encoding($s, 'eucjp-win', 'utf-8');
         $s = mysql_real_escape_string($s, $this->conn);
         $s = mb_convert_encoding($s, 'utf-8', 'eucjp-win');
一旦、eucjp-winにしてからエスケープ処理をして、もう一度UTF-8に戻しているので、eucjp-winにした時点で中国語の部分が落ちているということですね。これだと中国語以外でも、eucjp-winで表現出来ないUTF-8中の文字は全て落ちてしまうと思います。
php5.2.3未満の mysql_real_escape_string と UTF-8 の処理に何か問題があるための処置でしょうか?1.0.4J-r5のリリースノートにある件と関係あるでしょうか。
こちらの環境ではmysql_real_escape_string のエンコード判定ミスはないようなので、とりあえずこの部分を元に戻すことで回避できそうです。
mysql_set_charsetが使用できない環境で、なおかつエンコード判定をミスする環境があるとすれば、やはり何らかの回避処理が必要そうですがUTF-8のことを考慮すると他の方法をとる必要があるように思えます(が、良い方法が思いつきません・・・)。今の処理だと、問題ない場合でもCentOS5以下他、幅広い環境で文字落ちの危険性があると思いますがいかがでしょうか。。。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

ほぼ推察のとおりです。この部分は苦し紛れのトリックで、中国語などが扱えなくなる代わりに、日本語に関しては正しくエスケープを行なえます。1.0.4以前でどうだったかというと、実は内部的にはエスケープ系の文字化けが発生していました。これが原因で、特定の環境でチャンクやスニペット名を日本語で管理できないという問題が起きていましたが、たぶん他にも細かい問題が発生していた可能性があります。それを修正したのが1.0.4J-r5・1.0.5Jあたりからです。完全な解決が無理なのを承知で、一般的な利用における合格点を目指したという感じです。utf-8には存在するがeuc-jpに存在しない文字を扱いたいケースでは、mysql_set_charset関数が使えないPHP5.1.6環境などでは、たしかに問題があると思います。
(※本音を言うと、エンコードにこだわる案件ではできればPHPのほうをアップデートしてほしいですが、、実際は難しいかもしれませんね)

いろいろ調べましたが、mysql_real_escape_string関数は、PHP5.2.3より前の環境では日本語を素直に扱うのが難しいと思います。教科書的には「必ずmysql_real_escape_stringを使うべき」ということになってますが、そこにこだわらないで、条件さえ満たしていればmysql_real_escape_stringを使わないというアプローチも可能だと思います。その条件の洗い出しが必要ですね。
UTF-8はエスケープ処理を失敗させるコードを含んでいますが、UTF-8であることをデータベース側に事前に伝えられていたら、ちゃんと問題なくエスケープされます。ご利用のPHP5.1.6では、事前に伝える手段がありません。
php5.2.3未満の mysql_real_escape_string と UTF-8 の処理に何か問題があるための処置でしょうか?1.0.4J-r5のリリースノートにある件と関係あるでしょうか。
こちらの環境ではmysql_real_escape_string のエンコード判定ミスはないようなので、とりあえずこの部分を元に戻すことで回避できそうです。
つまり、そういうことになります。前後の変換処理各1行を削除しても問題ない環境は、大丈夫ということになります。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

yamaさま、お返事ありがとうございます。

mysql_real_escape_string のエスケープ処理失敗の件は、UTF8で二重にバックスラッシュが入る場合がある件でしょうか?
それとも別の単純な文字化けが発生するためでしょうか。
今までこの問題にぶつかったことがないので詳しくはないのですが、お手すきの時で構いませんので問題が出る条件とその結果をご存じでしたら教えて頂けないでしょうか?

データベース側の設定が

コード: 全て選択

+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
この状態ならば、問題が出ず、laten1があると問題が出る件に関係しているでしょうか・・・。

「入力はユニコードですよー」とアナウンスしていると、中国語に限らず、ユーザは機種依存文字その他も気にせず何でも入力してしまう可能性があります。
できればこの問題を綺麗な形で解決出来ればと思うのですが・・・。エスケープ二重問題ならば何とかなるような気もしたのですが、mysql_set_charset が登場した経緯に関係する問題ならば難しいでしょうかね。。。
エレガントな解決が不可能ならば、インストール・アップデート時に不具合を判定して異なるコード、またはパッチを当てるような仕組みが理想でしょうか。
ユニコードとしては落ちてしまう文字範囲がかなり広いと思いますので、本件についてはリリースノートに注記があればいいかなぁとも思います。

色々と勝手な意見を言ってしまい申し訳ありません。お手伝いできることがあれば御協力するようにいたしますので、よろしくお願いいたします。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

shobu さんが書きました:mysql_real_escape_string のエスケープ処理失敗の件は、UTF8で二重にバックスラッシュが入る場合がある件でしょうか?
そうです、二重バックスラッシュです(数カ月前の改修なのではっきり覚えてないけど、たしかそうだったと思います)。文字化けという形で表面に出てこないことが多い問題なので、MODXユーザの間で話題になることもほとんどないですが、個人的にはかなりまずい問題と認識していました。

今回の改修を施したのが数カ月前なのでよく覚えてないですが、

コード: 全て選択

+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
これだけutf-8で揃っていれば、たぶん問題ないですね。これらの値の全てがスクリプト側で動的に変更できるわけではなく、それもアプローチの難しさの一因になっています。

WordPressでは、条件的に問題なければmysql_real_escape_string使わないでaddslashesで済ませちゃってるみたいです。Version2.8あたりからけっこう本気で取り組んでる感じの書き方なので、もしかすると参考になるかな?とアタリをつけてます。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

インストーラに手を入れて、mysql_set_charsetが使えない環境に対して注意書きを表示するとかは可能だと思います。考えてみます。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

ご対応の予定ありがとうございます。

私はCentOS4,5とMODX 1.0.xJの組み合わせで作業を進めることが多いのですが、昔々、フォーラムにも投稿しましたが、RHEL3環境とそこからDB移行した環境で似たようなことがあったぐらいで、今までに mysql_real_escape_string に原因がありそうな問題にぶつかったことがないのですが、よくある症状なのでしょうか?
問題が発生する環境の方が少ないようならば、1.0.5Jのhackは逆の問題を起こす可能性の方が大きいような気がします。。。

他の方の環境ではどうなのか気になっています。一般的なディストリビューションやレンタルサーバでの情報があると判断しやすいでしょうか。。。

思いつきのアイデアですが、インストーラ中でDB作成後、または既存DBの確認後、 CREATE TEMPORARY を使って一時テーブルを作成。そこに問題が出るパターンの文字列(これがよく分かっていないのですが、出るときは「あいうえお」でも1文字ずつエスケープ文字が入ってしまうんでしたっけ?)を mysql_real_escape_string でエンコードして投入。その結果と投入文字列が異なるかどうかで処理を分けて、パッチ宛や、インストールコードを変える、警告を表示する・・・なんてことはMODxのインストーラ中で可能でしょうか。。
今までインストーラのコードを見たことがないのです。見当違いor不可能ならばスミマセン。。。。

引き続きよろしくお願いいたします。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

shobu さんが書きました: 私はCentOS4,5とMODX 1.0.xJの組み合わせで作業を進めることが多いのですが、昔々、フォーラムにも投稿しましたが、RHEL3環境とそこからDB移行した環境で似たようなことがあったぐらいで、今までに mysql_real_escape_string に原因がありそうな問題にぶつかったことがないのですが、よくある症状なのでしょうか?
私の周囲では毎月といっていいくらい、よくありました。仕事で使ってる自分が困ってたので、とりあえず暫定でもいいと思って修正しちゃいました。(気にしない人のほうが多いとは思いますが・・)
今回のような報告はありがたいです。可能性としては分かっていることでも、誰からも報告がないとすぐに改善する必要を感じることができませんし。
shobu さんが書きました:思いつきのアイデアですが、インストーラ中でDB作成後、または既存DBの確認後、 CREATE TEMPORARY を使って一時テーブルを作成。そこに問題が出るパターンの文字列(これがよく分かっていないのですが、出るときは「あいうえお」でも1文字ずつエスケープ文字が入ってしまうんでしたっけ?)を mysql_real_escape_string でエンコードして投入。その結果と投入文字列が異なるかどうかで処理を分けて、パッチ宛や、インストールコードを変える、警告を表示する・・・なんてことはMODxのインストーラ中で可能でしょうか。。
投入文字列と結果の比較はやったことがありますが、手間的にもあまり難しくはないですね。違う時にどういうエスケープを行なうべきかという問題が難しいです。難しいというか、私が把握しきれなくてこんがらかってるだけという感じですが。
そもそも特定環境ではmysql_real_escape_stringにこだわる必要自体がないのか、あるいはset namesをうまく使えばいいのか、あたりまでは推測がついてます。このへんがすっきり情報の整理がつけば、着手できそうに思ってます。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

たびたびお返事ありがとうございます。

二重エスケープの発生条件は判明しているでしょうか?yamaさんがよく利用される環境を教えていただけると参考になります。
そしてテンプレート変数でのみ発生する理由も気になりますが、私の方ではわかっていません。

DBのテーブルの文字コード設定がUTF8になったりlatin1なったりはmy.cnfの設定にもよるので、作業環境や業務の内容によっては回避可能(先にあげたようにすべてUTF8状態)だったり、どうにもならなかったり、まちまちでしょうね。
先の例のようにデータベースの初期設定がキッチリUTF8になっていれば問題が出ないような気がしているのですが、どうなんでしょ。。。

さて、どうにもならない場合、そして問題が出る場合のみ対応を考える形がよいと思うのですが、1.0.4までで二重エスケープ発生の件の報告例は多かったのでしょうか。個人的なイメージだと問題が出ていない環境の方が多いのではないかと思っています。
1.0.5のhackはUTF8として失うものがかなり大きいと思うので、基本的にはhackなし。不具合が出てしまう環境ではやむなくhackありで保存できる範囲がEUCjp-WINの範囲になってしまうメッセージを出せればと思うのですが、これは私個人の希望が大きいでしょうかね。

先ほどは一時テーブルで・・・と書きましたが、よく考えるとこれはDBの文字コード設定次第で状況が変わってしまいますね。インストーラを使って0からDBを用意した場合、別の環境のデータをレストアした場合、手でmysqlに直接DBを作った場合などで結果がチェック通りにならない可能性もありそうです。
すでに準備されたテーブルでチェックした方が確実なのでイベントログ用のテーブルあたりでテストするのがいいのかなぁ。

他の方のご意見もお聞きしたいです。。。引き続きよろしくお願いいたします。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

shobu さんが書きました:二重エスケープの発生条件は判明しているでしょうか?yamaさんがよく利用される環境を教えていただけると参考になります。
PHP5.1.6以前(5.1.6含む)の環境ではよくありました。去年の時点でさくらインターネットでも化けていたので、5.2.8以上の環境でも問題はあったみたいです。さくらで問題があったのは、mysql_set_charsetを使ってなかったからですね。使える環境では使うべきだと思いますので、これはよいと思います。
shobu さんが書きました:さて、どうにもならない場合、そして問題が出る場合のみ対応を考える形がよいと思うのですが、1.0.4までで二重エスケープ発生の件の報告例は多かったのでしょうか。個人的なイメージだと問題が出ていない環境の方が多いのではないかと思っています。
今のところ一件も聞いてないですが、私自身が困っていたし、内部的な文字化けは今後いろいろ問題が発生すると思ったので修正しました。このへんは、すみません。開発はなかなか大変なので、ユーザの意見の数で決めるのはちょっとつらいというのが本音です。自分が便利と思うものが他の人にとっても便利と感じられるものであれば嬉しいです。と、やや脱線ですが、、(この話はあまり広げたくないので)
shobu さんが書きました:1.0.5のhackはUTF8として失うものがかなり大きいと思うので、基本的にはhackなし。不具合が出てしまう環境ではやむなくhackありで保存できる範囲がEUCjp-WINの範囲になってしまうメッセージを出せればと思うのですが、これは私個人の希望が大きいでしょうかね。
現時点ではそうなりますね。できれば精度を高めて解決したいですが、解決策を練っている間に、mysql_set_charset関数が使えない環境自体が着々と少なくなっていくことを考えると、当面は対症療法的な解決でもよいかな?と思ってます。エンコードの問題だけに限らず、JSONが使えなかったり、PHP5.1.6って微妙に扱いが面倒なんですよね。
shobu さんが書きました:先ほどは一時テーブルで・・・と書きましたが、よく考えるとこれはDBの文字コード設定次第で状況が変わってしまいますね。インストーラを使って0からDBを用意した場合、別の環境のデータをレストアした場合、手でmysqlに直接DBを作った場合などで結果がチェック通りにならない可能性もありそうです。
そうですね。照合自体はできるけど、想定できる原因が複数出てくると思います。しかしエスケープの方法自体はいくつもあるわけではないので、見極めができれば対応は難しくないと思います。ただ、一時テーブルを作って照合するテは、地味に工数が多い気がします。一時的に作ったものは消す必要がありますし、ちゃんと削除できたかどうか確認するルーチンも必要ですし。できればini_setで得られた値をもとに判定する程度で済ませることができればと考えてます。

キャッシュ生成のタイミングをずらすことができれば、文字化けした状態でも正しくデータを扱える可能性はありますが、試しに途中までやってみましたがかなり工数が大きくなります。データベースだけ見れば、入れることができれば逆の処理で出すこともできるので、文字化けは特に意識することもないんですが(※厳密にはセキュリティ的には問題があると思います)。MODXはコンテンツだけでなくconfigも含めたほとんど全てをキャッシュに格納するので、他のCMSで問題化しないことがMODXで問題になる理由があるとしたら、ひとつはこれですね。

とりあえず話を戻すと、再現条件の洗い出しと、有効なエスケープ方法を見つけること。かなと思います。mysql_real_escape_stringを使わないというテはあると思います。
まず、mysql_set_charsetが使えない特定の環境(PHP5.1.6など)が今回のターゲットですね。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

こんばんは。
少しずつ思い出してきました。以前にも似たような件で投稿した覚えがあると書きましたが、やはり関係していますね。
エスケープとは別の文字化けだった気はしますが、本質は同じようです。

http://blog.cheki.net/archives/349
こちらの記事にある内容を見て思い出しました。当時も同じページがヒントになったはずです(ページ主さんどうもです)。

念のためですが、ご使用の環境では magic_quote による二重エスケープの件は関係ないですよね?

私の場合、今回のような問題を回避しやすいとか、自動バックアップの仕組みを用意したいとか、他のシステムや深いところまで絡んだコードが必要だったりなど、様々な理由で、どうしてもこのサーバで・・・という場合でなければ、システム丸ごとroot権限毎用意できる環境を使用します。
よってmy.cnfの設定から行うのですが、前述の記事にあるように
default-character-set = utf8
skip-character-set-client-handshake
などの設定も加えています。DBの準備もインストーラ任せでなく、mysqlから行っています。
結果的には先の投稿のようにすべてutf-8になった状態のDBを用意することになりますし、skip-character-set-client-handshake によって接続時にもUTF8が保証されて問題を回避できているのだと思います。

さくらのレンタルサーバというのはマネージドサーバでしょうか。VPSは複数台利用しているのですが、こちらは使用したことがありません。
mysqlはさくらが設定したものを使用するわけですよね?それがlatin1問題を抱えているということでしょうか。
webarena suitexの環境にはアクセスできるので明日、どのような状態かチェックしてみたいと思います。。。

さて、確かにphp5.3シリーズが使えればそれで解決?ですし、CentOS5でも使用できるようになったので、mysql_set_charsetで回避できる環境も増えるとは思いますが、以前より運用しているサイト、サーバだとRHEL4, CentOS4などの環境もまだまだあるでしょうし、RHEL5相当でも運用中のものをphp5.3シリーズに置き換えるのはかなり勇気がいると思います(kagoyaのマネージドはphpをがんがん入れ替えてくれますが、freetypeやGDの挙動の違いに始まり、細かいバージョン間の際に振り回されることがあります・・・いきなり5シリーズ、5.3シリーズになったときは苦労しました)。
なので、多くのユーザがphp5.3で解決となるまでにはまだまだ時間がかかるように思います。

私は使わせてもらっているだけの立場なので、勝手を言うのはどうかとも思うのですが、個人的な意見を表明させていただくと、、、

・今回問題の変更はできれば元に戻してほしい。UTF8環境なのに実質EUCjp-WINの状態になり、5.3未満がすべて該当するという判定方法故、この状態になる環境がかなり出てしまうと思われる(多くのユーザは気づいていないだけでは・・・)。
・ユーザはunicode環境だと思いこんでいると思うので、この問題に気づかないで使用する可能性がある。
・まずは簡単な対応として、元のコード(本家と同じということですね)に戻し、インストール時の注意表示やリリースノートにこの問題の解説と共に置き換え用のファイルを同梱、状況に合わせて自分で置き換えるようにする(元コードと置き換えファイルの内容は逆でもよいとは思いますが、不具合発生に気づきにくいと思うので)。
・可能ならばシステムの状況を確認した上で、save_content.processor.php の置き換えをし、対応の説明を表示する。
yama さんが書きました:そうですね。照合自体はできるけど、想定できる原因が複数出てくると思います。しかしエスケープの方法自体はいくつもあるわけではないので、見極めができれば対応は難しくないと思います。ただ、一時テーブルを作って照合するテは、地味に工数が多い気がします。一時的に作ったものは消す必要がありますし、ちゃんと削除できたかどうか確認するルーチンも必要ですし。できればini_setで得られた値をもとに判定する程度で済ませることができればと考えてます。
イベントログのテーブルに書きっぱなしというのは駄目でしょうかね。「インストール時のDBの状況確認を行いました」などとイベントログとして書き込んでしまい、その投入状況を比較確認して処理判断を行い、消しはしないという方法です。問題があればEUCjp-win処理を含むファイルで置き換えをし、その旨を表示できればベストですよね。
・不具合なしバージョンでも、mysql_set_charsetが使える場合は使用するようにすることで確実を期す。
・不具合ありの場合は現コードを使用すれば、mysql_set_charset が使えれば問題回避(だと思う)。だめならばEUCjp-winで我慢。

というところでどうでしょうか。私の方でもインストーラでどうにかできないか調査してみようと思います。二重エスケープが発生してしまう環境を作り出すのがハードルですが・・・。仮想サーバにlatin1になる環境を用意してみたいと思います。

それではよろしくお願いいたします。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

5.3未満がすべて該当するという判定方法
mysql_set_charsetが使えない一部の環境だけだと思っていますが、違うみたいですか?このへんが怪しいようであればもう少し調べてみます。
私の方でもインストーラでどうにかできないか調査してみようと思います。二重エスケープが発生してしまう環境を作り出すのがハードルですが・・・。
二重エスケープが発生してしまう環境というより、まず、mysql_set_charsetを使えない環境を用意することが必要だと思います。手っとり早いのは5.1.6環境を作ることですね。少し前のCentOSだと、たいていそうなってたと思います。これはVirtualPCのディスクイメージでも配布されていて、私もダウンロードして手軽に試してみたことがあります。
mysql_set_charsetが使えなくても、文字化けなく問題なく使えることはあると思います。ここからの検証がちょっと大変かもしれません。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

yama さんが書きました:
5.3未満がすべて該当するという判定方法
mysql_set_charsetが使えない一部の環境だけだと思っていますが、違うみたいですか?このへんが怪しいようであればもう少し調べてみます。
公式マニュアルによると mysql_set_charset が実装されたのは 5.2.3 以降なので、5.3未満すべてというのは言い過ぎでした。5.2.3未満すべてですね。
RHEL5は最近、php53というパッケージが出てきて「使おうと思えば」5.3以降が使用できるようにはなったのですが、標準は5.1.6なので、最近新規インストールした環境でなければ通常は5.1.6以下で運用されているケースが多いと思います。
そうなるとCentOS4,5含め、RHEL5以下の互換ディストリビューションなどの多くも該当してしまうので、「mysql_set_charsetが使えない一部の環境だけ」の範囲が、ユーザの数を考えるとかなり広いのではないかと思っています。現状のVPSはCentOS5あたりで提供されるケースが多く、RHEL6相当以降は自分でOS入替が必要な状況です。

root権限がないサーバだと各アプリケーションを独自ビルドのケースも多く、最新版のソースからビルドというケースが多いのでしょうか(先のkagoyaがそんな感じですが)。
二重エスケープが発生してしまう環境というより、まず、mysql_set_charsetを使えない環境を用意することが必要だと思います。手っとり早いのは5.1.6環境を作ることですね。少し前のCentOSだと、たいていそうなってたと思います。これはVirtualPCのディスクイメージでも配布されていて、私もダウンロードして手軽に試してみたことがあります。
mysql_set_charsetが使えなくても、文字化けなく問題なく使えることはあると思います。ここからの検証がちょっと大変かもしれません。
ご指摘の通りCentOS4,5あたりを普通にインストールすれば「mysql_set_charsetを使えない環境」が普通に用意はできるのです。RHEL6.1、CentOS6(こちらの存続が怪しい状況ですが)等々、php5.3を使用できるシリーズも登場してきていますが、業務使用だとまだ少し様子見かなぁ、、、という状況だと思います。
前述の通り、多くのVPSサービスもCentOS5止まりがほとんどです。が、my.cnfの設定を抜かりなくしてあれば、この環境でも問題は起こらないと思われます。
既に不具合が出てしまっている環境でも、mysqlの設定変更ができるのならば対応も可能だと思います。

現状、私の手元にある環境で不具合が出るケースがないので、仮想環境を用意してlatin1問題がおきる環境を準備してから検証を進めようと考えています、、というわけです。そもそも二重エスケープ発生がlatin1の件に関係しているのか、そこから確認が必要ですね。。。

さて、問題回避が難しいのはmysqlの設定をさわれないマネージド系レンタルサーバ環境でしょうか。古くに立ち上げられたサービスだとlatin1系の問題を引きずったままのサーバも多いのかもしれませんね。
ちなみにWebArena SuiteXもチェックしてみましたが、MODx1.0.4J-r2にて、標準付属のmysql環境では問題はおきませんでした。設定はみられませんが、phpは5.1.6、mysqlは5.1.36-community-logとなっていますので、「mysql_set_charsetを使えない環境」のはずです。

以上ご参考になれば幸いです。進展があればまたご報告いたします。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

国内で5.1.6環境が多いのは把握してます。たとえばRevolutionが5.1.6で動かないのですが、それで困っているという話はよく聞きます。海外ではあまり聞かないので、日本独特の事情なのかもしれませんね。

対応はしたほうがいいと思いますが、日本語版が特定の環境(PHP5.1.6以下)で中国語や機種依存文字が使えないのはいわゆる「事故」に相当するほどの問題ではないと認識しています。エスケープまわりは失敗すると大事なデータが消えて大変なことになるので、今は安定しているので逃げておきたいなというのもちょっと本音としてはあるのですが・・解決できれば解決したいので、もしよければお願いします。このへんの解決は本家開発チームからも期待されています。

とりあえず作為的にイレギュラーな環境を作るところからですね。少し古いCentOSだと、日本語環境というとeuc-jp(ujis)基準でデフォルト値が提供されていたりしないでしょうか。FreeBSDなんかはそうだったみたいですが。日本の普通の月額数百円~2000円程度のレンタルサーバで問題が起きているので、たぶんそういうことじゃないかなあという気がしています。化けたデータはデータベースを通して見ても正常に見えるので、データベースに入力する前にファイルなどに出力して、これを検査してテストを進めます。(というか、キャッシュをテキストエディタで開けば確認できます)

環境によってはmysql_real_escape_stringを使わない・set namesを併用するという方法で、意外とあっさり解決できるんじゃないかとは思ってます。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

キャッシュの件は/manager/processors/cache_sync.class.processor.phpのbuildCache関数あたりが見直しの必要があると思います。こっちを解決すればよいというわけではないんですが、そもそもの話の発端はここの処理の問題からです。

'$c[\''.$modx->db->escape($tmp1['name']).'\']'
たとえばこれですね。意味なくない?と思うのですが・・
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

http://www.klab.jp/media/mysql/index6.html
この辺に綺麗に情報が集まっていますね。
今までよく検証したことがなかったのですが、 set names ではクライアント側となるphpの認識が変わらないので本件では意味が無いのかもしれません(要確認)。

my.cnf を触れる&システム全体で文字コードを統一して良い環境ならば、my.cnf に

コード: 全て選択

[client]
default-character-set=utf8
で問題を回避できる(要確認)。

コード: 全て選択

[mysqld]
default-character-set=utf8
skip-character-set-client-handshake
は今では推奨されないようですが、問題になるのはutf8以外の環境のみでしょうかね。。。今まではこれで回避していました。

my.cnfを触れず、上記の設定もされていない場合、
php5.2.3以降ならばmysql_set_charset を指定する(現1.0.5Jのように関数が存在すればconnect時に指定する形で良さそうですね)。逆にmysql_set_charset が使えればmy.cnf側の設定がいらない訳ですが。
mysql_set_charset を使用できない場合はどうすべきか。。。

mysql_set_charset を使用できない場合でも、my.cnfが触れるならば問題を回避できるので、eucjp-win変換はして欲しくない。
この判定をどうするか。。。どのようにして対処するか。
という流れでしょうか。

(connect時にmysql_set_charset を使用する部分を加えるだけの前提で)
  • ・インストール時にまずはmysql_set_charset が通るならばオリジナルのままにする。
    ・通らない場合は、状況の確認を行う(テーブルへの書き込み&確認)
    ・問題ないようならばオリジナルのままにする。
    ・問題がある場合、置き換えファイルで置き換える。置き換えのためには事前にパーミッションの確認も必要ですね。。
置き換えの場合は、本家からも期待されているとなると日本語だけの対応では駄目なので、今の方法と別の解決方法を考える必要があるでしょうね。
または、言語別に対応を用意するのか(この場合、ここに落ちると各言語範囲しか使用できないということになりそうですが)・・・
php公式マニュアルの mysql_real_escape_string のコメントにある

コード: 全て選択

function mysql_escape_mimic($inp) { 
    if(is_array($inp)) 
        return array_map(__METHOD__, $inp); 

    if(!empty($inp) && is_string($inp)) { 
        return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
     } 

    return $inp; 
} 
上記のような代用コードを使用したファイルに置き換える(上記コードの妥当性は確認していません)。でも、これをデフォルトとはせず、基本は素直に mysql_real_escape_string を使用して、可能ならば mysql_set_charset を使用するコードをデフォルトとするのが良いと思います。

----------------------------
1.0.4-r5以降からの新規インストールならば「日本語しか入らないんだ。」という認識もあり得ると思うのですが、どうにも気になるのは、アップグレードだった場合、今まで問題がなかった環境においても、ページの再保存時などに触っていないはずの入力済みのデータが抜け落ちる可能性が出てきた点です。
対応はしたほうがいいと思いますが、日本語版が特定の環境(PHP5.1.6以下)で中国語や機種依存文字が使えないのはいわゆる「事故」に相当するほどの問題ではないと認識しています。エスケープまわりは失敗すると大事なデータが消えて大変なことになるので、今は安定しているので逃げておきたいなというのもちょっと本音としてはあるのですが・・解決できれば解決したいので、もしよければお願いします。このへんの解決は本家開発チームからも期待されています。
とあったのですが、現状は前述のようなケースで逆に大事なデータが消えて大変なことになる可能性の方が大きい気がしています(特に1.0.4-r5以前からのユーザ)。
よく問題になる「~」の件とかは大丈夫なようですが(sambaの件が参考になりますかね http://www2d.biglobe.ne.jp/~msyk/charco ... JP-ms.html )、日本語版が(環境にもよるわけですが)「本当に日本語しか使用できない」バージョンだと考えているユーザは少数で、「いざとなれば中国語はもとより、タイ語でもベトナム語でも入力できますよ」と思っているユーザの方が多いのではないでしょうか。そうなると今は良くても後々問題になるケースもあり得ますし、何より現状は問題が発覚しづらいのも気になります。
二重エスケープが発生すれば直ぐに気づきそうですが、偶々入力したeucjp-win範囲に無い文字が落ちるなどは見た目気づきづらいのではないでしょうか?
少し古いCentOSだと、日本語環境というとeuc-jp(ujis)基準でデフォルト値が提供されていたりしないでしょうか。
mysqlやphpの関係で、MODxをRHEL系列で使おうと思えば、基本的にはRHEL4相当以降になると思いますが、RHEL4以降はOSとしてはutf8が基本だと思います。付属のlibmysqlclientがlatin1でコンパイルされているのがネックになるわけですが。
逆に今でもeuc-jp(ujis)が基本のサーバは最近見ない、というかMODx含めUTF8が基本になってきたCMSなどを使う場合、このようなサーバは出来るだけ避けているから、かもしれませんね。。。そういうユーザの方が多いと思っていましたが。。。他のユーザのご意見もないと何とも言えませんね。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

yama さんが書きました:キャッシュの件は/manager/processors/cache_sync.class.processor.phpのbuildCache関数あたりが見直しの必要があると思います。こっちを解決すればよいというわけではないんですが、そもそもの話の発端はここの処理の問題からです。

'$c[\''.$modx->db->escape($tmp1['name']).'\']'
たとえばこれですね。意味なくない?と思うのですが・・
何度もスミマセン。未だキャッシュの方の問題が理解できていないのですが、modx->db->escape内の問題が解決出来れば、こちらも解決、ということではないのでしょうか?
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

shobu さんが書きました:今までよく検証したことがなかったのですが、 set names ではクライアント側となるphpの認識が変わらないので本件では意味が無いのかもしれません(要確認)。
なんとなくそんな気もしてます。理屈上ではset namesが使えそうに思ってますが、実際は一度もテストで手応えを得られたことがないです。
shobu さんが書きました:mysql_set_charset を使用できない場合でも、my.cnfが触れるならば問題を回避できるので、eucjp-win変換はして欲しくない。
この判定をどうするか。。。どのようにして対処するか。
という流れでしょうか。
ここんとこですが、my.cnfをさわるよりは該当部分のコードを2行削ってしまうほうが安全だし、分かりやすくないでしょうか?あくまでも対症療法ですが。

シンプルに考えると、まずは自分が目的とする使い方で問題が起きるかどうかを気付くことができればよさそうに思います。それで解決ってわけではないですが、エンコードまわりの解決はけっこう気が遠い話な上に影響力が大きいことになるので、目先の対策は必要ですよね。インストーラに、「お使いの環境では中国語や一部の機種依存文字の入力ができません。必要な場合はxxxを書き換えてください」みたいなガイドを出す、って感じでどうでしょう?
shobu さんが書きました:(connect時にmysql_set_charset を使用する部分を加えるだけの前提で)
  • ・インストール時にまずはmysql_set_charset が通るならばオリジナルのままにする。
    ・通らない場合は、状況の確認を行う(テーブルへの書き込み&確認)
    ・問題ないようならばオリジナルのままにする。
    ・問題がある場合、置き換えファイルで置き換える。置き換えのためには事前にパーミッションの確認も必要ですね。。
「問題ないようならば」ってとこがポイントになりますね。

コード: 全て選択

function mysql_escape_mimic($inp) { 
    if(is_array($inp)) 
        return array_map(__METHOD__, $inp); 

    if(!empty($inp) && is_string($inp)) { 
        return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
     } 

    return $inp; 
} 
上記のような感じで、代替えアプローチを用意するのがよいですね。(これが有効かどうかは私もよく見てないので分かりませんが、興味あります)
shobu さんが書きました:二重エスケープが発生すれば直ぐに気づきそうですが、偶々入力したeucjp-win範囲に無い文字が落ちるなどは見た目気づきづらいのではないでしょうか?
二重エスケープの問題は気づきにくいと思います。イレギュラーなエスケープで入力したものを、同じ手順を逆にたどって取り出すので、結果的には元の状態に戻りますので。実際、二重エスケープが発生して困っているという話は聞いたことがありません。システムの挙動が不安定なことがあるので、原因を探ってみたらここに問題があることが分かった次第です。見えにくい問題を抱えたまま開発を続けるわけにはいかないので(実際大変です)、この部分を元の状態に戻せないというのはそういう理由です。

「mysql_set_charsetは使えないけどエンコードずれの問題が発生しない環境」が今回のターゲットですよね。だったら、2行削除も有効ですが、プラグインを作るなどしてmysql_set_charset関数を空定義してしまうという手も有効かも。エンコードずれの問題が発生しない条件を特定できるなら、インストーラ側で工夫してconfig.inc.phpに埋め込んでしまうという手も使えると思います。
アバター
yama
管理人
記事: 3236
登録日時: 2009年7月29日(水) 02:50

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by yama »

shobu さんが書きました:未だキャッシュの方の問題が理解できていないのですが、modx->db->escape内の問題が解決出来れば、こちらも解決、ということではないのでしょうか?
ここで$modx->db->escape()を使う必要自体、目的を考えるとどうなのかな?と思います。データベース関係ないはずですし。必要なエスケープを行なわないために問題が起きる可能性はありますが、今のところ実例は確認できてないので検証を後回しにしています。
以前のMODXは、この部分の情報が化けたまま動いてました。「チ¥ャ¥ン¥ク」みたいな状態になってても、基本的に化けた情報をベースにやりとりしてるから結果オーライで動いてることが多かったみたいですね。チ¥ャ¥ン¥クという名前のチャンクを呼ぶ時点で、チ¥ャ¥ン¥クとして呼んでました。
そもそも脆弱性対策のためのmysql_real_escape_stringなので、せっかくエスケープしてるのに全然意味がないことになります。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: テンプレート変数へ中国語を入力できない($modx->db->escapeの問題?)

投稿記事 by shobu »

yama さんが書きました:
shobu さんが書きました:mysql_set_charset を使用できない場合でも、my.cnfが触れるならば問題を回避できるので、eucjp-win変換はして欲しくない。
この判定をどうするか。。。どのようにして対処するか。
という流れでしょうか。
ここんとこですが、my.cnfをさわるよりは該当部分のコードを2行削ってしまうほうが安全だし、分かりやすくないでしょうか?あくまでも対症療法ですが。
yama さんが書きました: シンプルに考えると、まずは自分が目的とする使い方で問題が起きるかどうかを気付くことができればよさそうに思います。それで解決ってわけではないですが、エンコードまわりの解決はけっこう気が遠い話な上に影響力が大きいことになるので、目先の対策は必要ですよね。インストーラに、「お使いの環境では中国語や一部の機種依存文字の入力ができません。必要な場合はxxxを書き換えてください」みたいなガイドを出す、って感じでどうでしょう?
何が気になっているかというと、一番の問題は1.0.4-r4以降で急に変更になったということなのです。アップグレードしているユーザということです。
元々問題が出ていないユーザについては、my.cnfで回避できることを知っているか、元々そういう環境で使用している場合が多いと思います。今までがそういう認識だったのに、あるバージョン以降は今まで問題ない文字が入力できなくなったり、既に入力済みの内容が削れたりということが起きてしまうのではないかと危惧しました。
警告文などに気づいて、現状のコードを修正するというのでも良いとは思うのですが、折角インストーラがあるのでここで上手く処理できればなーと。
yama さんが書きました:
shobu さんが書きました:(connect時にmysql_set_charset を使用する部分を加えるだけの前提で)
  • ・インストール時にまずはmysql_set_charset が通るならばオリジナルのままにする。
    ・通らない場合は、状況の確認を行う(テーブルへの書き込み&確認)
    ・問題ないようならばオリジナルのままにする。
    ・問題がある場合、置き換えファイルで置き換える。置き換えのためには事前にパーミッションの確認も必要ですね。。
何度か書きましたが、オリジナルに戻すってのは本当にナシです。内部でやりとりする情報が化けるので、システム的におかしくなるんですよ。汗
これはどういうことでしょうか?問題の処理を入れるようになってからは、読み出し時にも何か別の処理を入れたところがあるということでしょうか?
そういうことではなく、私の環境のように全てutf8でやりとりできていれば「内部でやりとりする情報」も問題なく、そうではないと問題が出る(それもエスケープ周り?)ということでしょうか。
使用している立場というか、環境というか、その辺の違いで状況が180度逆の状態で、認識も逆なのかもしれませんね。。。
yama さんが書きました:

コード: 全て選択

function mysql_escape_mimic($inp) { 
if(is_array($inp)) 
return array_map(__METHOD__, $inp); 

if(!empty($inp) && is_string($inp)) { 
return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $inp);
} 

return $inp; 
} 
上記のような感じで、代替えアプローチを用意するのがよいですね。(これが有効かどうかは私もよく見てないので分かりませんが、興味あります)
mysql_real_escape_string は connect しないと使えないので、代わりになる方法については色々とアプローチがあるようです。
mysql_real_escape_string() は、MySQL のライブラリ関数 mysql_real_escape_string をコールしています。 これは以下の文字について先頭にバックスラッシュを付加します。 \x00, \n, \r, \, ', " そして \x1a.
とあるので、上記のコードで問題なさそうに見えますが・・・。
yama さんが書きました:
shobu さんが書きました:二重エスケープが発生すれば直ぐに気づきそうですが、偶々入力したeucjp-win範囲に無い文字が落ちるなどは見た目気づきづらいのではないでしょうか?
二重エスケープの問題は気づきにくいと思います。イレギュラーなエスケープで入力したものを、同じ手順を逆にたどって取り出すので、結果的には元の状態に戻りますので。実際、二重エスケープが発生して困っているという話は聞いたことがありません。システムの挙動が不安定なことがあるので、原因を探ってみたらここに問題があることが分かった次第です。見えにくい問題を抱えたまま開発を続けるわけにはいかないので(実際大変です)、この部分を元の状態に戻せないというのはそういう理由です。
前述の件と同じですが、この状況をこちらで再現出来ていないのでどのような問題が起きているのか、とても気になります。この問題から派生して、また別の事象が起きるということですよね。どんなかんじなんでしょうか?
私の場合は初回構築時や日々の運用更新時もmysqlへの直接操作を行うので「\あ\い\う\え\お」なんてデータが入っていたら直ぐに気づきますし、この場合はサーバ側に問題があるので設定を確認、修正・・・というアプローチが多く、アップデート時のこともあるので、できるだけ提供されたコード本体に手を入れないようにするようにしているのです。この辺の間隔の違いなんでしょうね。。。
yama さんが書きました: 「mysql_set_charsetは使えないけどエンコードずれの問題が発生しない環境」が今回のターゲットですよね。だったら、2行削除も有効ですが、プラグインを作るなどしてmysql_set_charset関数を空定義してしまうという手も有効かも。エンコードずれの問題が発生しない条件を特定できるなら、インストーラ側で工夫してconfig.inc.phpに埋め込んでしまうという手も使えると思います。
あー、なるほど・・・。でも、mysql_set_charsetは公式の関数なので、全然関係ない他のコードでも空定義でない前提で呼び出されてしまう可能性もあり得ますよね(別のスニペットやプラグインとか、自前の何かとか)。

mysql_set_charsetの有無、不具合問題の有無に基づいて config.inc.php 内で mysql_real_escape_string の代替コードを別名定義。
dbapi.mysql.class.inc.php では最初に代替コードの定義を確認して、存在するならば(つまり問題が出る環境)代替コードによるエスケープ。
それ以外は通常通り、但し、使用できれば connec関数内で mysql_set_charset を使用するようにしておく、というのはどうでしょう。

全ての処理を代替コードに置き換えてしまうのは先々のことを考えると怖いので、基本はmysql_real_escape_string を素直に使用するというのが理想かなと思います(本家からの置き換え需要もあり得ますし)。
これならばconfig.inc.phpは元々書き込み状態にする必要があるので、インストーラで素直に書き換え対応も出来そうですし、過去の環境からも状況を変えずに対応出来そうですよね。特定の言語環境にも依存しないし、本家での既存環境も変えないと思うので、採用もして貰いやすいかもしれませんね。

そうなると、あとは・・・
  • 前述の代替コードの妥当性
    インストール時点での不具合環境のチェック方法
の確認ですね。まだ、実験できていませんが、こちらで見てみたいと思います。

この件、商用の環境などで以前から使用しているユーザにとってはかなり大きな問題だと思ったのですが、日本のユーザは本家版を使用されている方の方が多いのでしょうか。私は日本語版が出てからはそちらを使用させて頂いています(感謝しております)。
返信する