ホーム > java
javaのアーカイブ
UTF-16時代のエスケープ処理
えーと、変換後がUCS-2とかだとマズいかな? …(実験中)… 試作パッチ以前に、Connector/Jでcharacter_set_server=ucs2のサーバに繋がらないんですけど。 Connector/J側でcharacterEncoding=UTF-8などとしておけば繋がりますが、こんな仕様あったかな…。
そもそもucs2は現在クライアントキャラクタセットとしては使えないんじゃないでしょうか。
UCS-2 cannot be used as a client character set, which means that SET NAMES 'ucs2' does not work. (See Section 9.1.4, “Connection Character Sets and Collations”.)
ちなみにUCS-2とかUTF-16のようにASCIIな部分までマルチバイトなキャラクタセットが使えるとなると、Cのmysql_real_escape_stringも問題が発生しそうですね。マルチバイトはエスケープ対象外という処理でShift_JIS(やGBK)の5c問題を解決しているようなので。これは現在のサポート文字セットに依存した脆弱な実装ということになるんでしょうか。
そんな時代がくるのか分かりませんが、perl的にUTF-16時代の正しいescape処理を考えてみるとこんな感じ?
#!/usr/bin/perl use strict; use warnings; use utf8; use Encode; # from DBD::mysqlPP::quote my @quote_target = ( "\\" => '\\\\', "\0" => '\\0', "\n" => '\\n', "\r" => '\\r', "'" => q{\\'}, '"' => '\\"', "\x1a" => '\\Z', ); my $str = "I'm sorry\nok."; print quote($str, 'utf-16LE'); sub quote { my ($str, $charset) = @_; my %quote_target_for_charset = map { Encode::encode($charset, $_) } @quote_target; $str =~ s{ (.) }{ my $bytes = Encode::encode($charset, $1); $quote_target_for_charset{$bytes} // $bytes; }exmsg; return $str; }
Yen markのhtmlでのエスケープもなんかへんな感じになってるのでファイルごとup
1文字ずつencodeしてるのはなんかやーなかんじですね。試してないけどRubyだと$KCODEの切り替えを使うとencode的な事は1回で済むのかも。あとで試してみよう。
useServerPrepStmtsを使うのが根本解決だとはおもう。けど…?
UnicodeのU+00A5問題
JavaとMySQLの組み合わせでUnicodeのU+00A5を用いたSQLインジェクションの可能性
なるほど。この問題は初耳だったので驚いた。
しかも、PreparedStatementを使っても解決されないという。この部分に非常に驚いたのでどういうことなのか少し調べてみた。
PreparedStatementとは?
mysqlにおける話としてはPrepared Statement (訳)がとてもわかりやすい。
なかでも重要なのは以下の部分だ。
PerlとJava のユーザはかなり長い間prepared statementを使って きました。しかし、これらはクライアント側のprepared statementでした。 クライアント側のprepared statementは同じようなセキュリティの恩恵 をもたらしますが、性能向上には至りません。でも心配いりません。 MySQL Connector/J は3.1のリリースでサーバーサイドprepared statement をサポートします。
そして実は現在のConnector/Jの最新版(5.1.7)ではデフォルトでは「サーバーサイドprepared statement」を使っていないのである。そしてクライアントサイドのprepared statementは結局のところエスケープ関連のセキュリティ脆弱性を潜在的に抱えてしまう存在なのである。そして実際に今回の問題があった。
Connector/JでサーバーサイドのPreparedStatementを使う
jdbcのURLにuseServerPrepStmts=trueをつけるだけである。
Connection con = DriverManager.getConnection( "jdbc:mysql://localhost/tokumaru?user=xxx&password=xxxx&useUnicode=true&useServerPrepStmts=true&characterEncoding=" + charEncoding)
簡単ですね。こうすることにって、U+00A5問題は発生しません。エスケープ自体どの時点でもしないわけですから。めでだしめでたし。
しかし、useServerPrepStmtsのデフォルト変更問題が
useServerPrepStmtsのここの説明ではデフォルトがtrueになっているが、これは上述の通り嘘である。ちなみに英語マニュアルをみるときちんとfalseになっている。これにはuseServerPrepStmtsの登場時(Connector/J 3.1.0)にはデフォルトがtrueだったが、5.0.5と5.1.0においてfalseに変更されたという経緯があるためのようだ。そしてなぜfalseにされたかということの背景を察すると、trueにすることの弊害もありそうで、手放しでこれをtrueにすることを勧めることが少しはばかられる。これについて詳しい方がいたらぜひ説明をお願いしたいところである。
ホーム > java
- 検索
- フィード
- メタ情報