個々の Java 実行環境にはデフォルトの文字エンコーディングが定められている。使用可能な文字エンコーディングはJava SEのドキュメント「サポートされているエンコーディング」に列挙されている[Encodings 2006]。文字列とバイト列との間で変換を行う場合、変換内容を指示するために、文字エンコーディングを指定する必要がある。明示的に文字エンコーディングを指定しない場合は、デフォルトの文字エンコーディングが指定されているものとみなされる。文字列がなんらかのチャネルを通じて送られるとき、送信側では文字列データをバイト列に変換して送信し、受信側では受け取ったバイト列を再び文字列に変換する。このとき、送信側と受信側では、同一の文字エンコーディングを指定する必要がある。文字エンコーディングが一致しないと、データの破壊を招く。
Java API [API 2006] の String クラスの説明には以下のように記述されている:
新しい String の長さは文字セットによって変化するため、部分配列長と一致しないことがある。指定された文字セットで指定されたバイトが無効な場合、このコンストラクタの動作は指定されない。
有効な文字列に変換できることが分かっているバイナリデータについては、そのまま読み込んで文字列データに変換してもよい(「FIO11-J. バイナリデータを文字データとして読み込もうとしない」の例外 FIO11-EX0 により)。
違反コード
以下のコードはバイト列を読み込み、デフォルトの文字エンコーディングで String に変換している。デフォルトの文字エンコーディングがバイト列を生成したときの文字エンコーディングと異なると、正しい String オブジェクトは得られないだろう。入力のなかに、デフォルトのエンコーディングでは適切に表現できない値が含まれていると、未定義の動作を引き起こす。
try { FileInputStream fis = new FileInputStream("SomeFile"); DataInputStream dis = new DataInputStream(fis); byte[] data = new byte[1024]; dis.readFully(data); String result = new String(data); } catch (IOException x) { // エラー処理 }
適合コード
以下のコードでは、String コンストラクタの第2引数に、意図した文字エンコーディングを指定している。
try { FileInputStream fis = new FileInputStream("SomeFile"); DataInputStream dis = new DataInputStream(fis); byte[] data = new byte[1024]; dis.readFully(data); String encoding = "SomeEncoding"; // たとえば "UTF-16LE" String result = new String(data, encoding); } catch (IOException x) { // handle error }
例外
IDS13-EX0: プラットフォームもデフォルトの文字エンコーディングも受信側と同じものを使用する Java アプリケーションでデータが生成され、かつ、そのデータがセキュアな通信チャネルを通じて送信されたのであれば、受信側における文字エンコーディングの指定は省略できる(「MSC00-J. セキュアなデータ交換には Socket クラスではなく SSLSocket クラスを使用する」を参照)。
リスク評価
ファイル入出力やネットワーク入出力を行う際に文字エンコーディングを指定しないと、データが破壊される可能性がある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
IDS13-J | 低 | 低 | 中 | P2 | L3 |
自動検出
このガイドラインへの違反を適切に検出するのは難しい。
参考文献
[Encodings 2006] |
翻訳元
これは以下のページを翻訳したものです。
IDS13-J. Use compatible encodings on both sides of file or network IO (revision 62)