JPCERT コーディネーションセンター

IDS12-J. 異なる文字コードへの文字列データの変換はデータが欠損しないように行う

Stringオブジェクトを異なる文字コードに変換すると、データが欠損するかもしれない。

Java API 仕様[API 2006]の String.getBytes(Charset)メソッドのドキュメントには以下のように記されている。

このメソッドは、不正入力シーケンスやマップ不可文字シーケンスを、この文字セットのデフォルトの置換バイト配列で置き換える。

たとえば、ファイルに書き込むためにStringをバイト列に変換しなくてはならず、かつ文字列にマップ不可文字が含まれている場合、文字列を適切にエンコードしなければならない。

違反コード

以下の違反コード例 [Hornig 2007] では、指定した charset で表現できない文字が string に含まれている場合、データが破壊されてしまう。

// エラー発生時にデータが破壊される
public static byte[] toCodePage_bad(String charset, String string) 
  throws UnsupportedEncodingException {
  return string.getBytes(charset);
}

// データの破壊を検知できない
public static String fromCodePage_bad(String charset, byte[] bytes)
  throws UnsupportedEncodingException {
  return new String(bytes, charset);
}
適合コード

java.nio.charset.CharsetEncoderクラスは、16ビットのUnicode文字をcharsetが指定するバイトシーケンスに変換することができる。java.nio.charset.CharacterDecoderクラスはその逆の変換を行うことができる[API 2006]。本ルールに関連する「FIO11-J. バイナリデータを文字データとして読み込もうとしない」も参照。

以下の適合コード [Hornig 2007] では、CharsetEncoderクラスとCharsetDecoderクラスを使用してエンコーディングの変換を行っている。

public static byte[] toCodePage_good(String charset, String string)
  throws IOException {
  
  Charset cs = Charset.forName(charset);
  CharsetEncoder coder = cs.newEncoder();
  ByteBuffer bytebuf = coder.encode(CharBuffer.wrap(string));
  byte[] bytes = new byte[bytebuf.limit()];
  bytebuf.get(bytes);
  return bytes;
}

public static String fromCodePage_good(String charset,byte[] bytes) 
  throws CharacterCodingException {
  
  Charset cs = Charset.forName(charset);
  CharsetDecoder coder = cs.newDecoder();
  CharBuffer charbuf = coder.decode(ByteBuffer.wrap(bytes));
  return charbuf.toString();
}		
違反コード

以下の違反コード [Hornig 2007] では、文字列をテキストファイルに、指定したエンコーディングで追記しようとしている。String には表現不可能な文字が含まれているかもしれないため、これは間違ったやり方である。

// エラー発生時にデータを破壊する
public static void toFile_bad(String charset, String filename, String string)
  throws IOException {
  
  FileOutputStream stream = new FileOutputStream(filename, true);
  OutputStreamWriter writer = new OutputStreamWriter(stream, charset);
  writer.write(string, 0, string.length());
  writer.close();
}
適合コード

以下の適合コード [Hornig 2007] では CharsetEncoder クラスを用いて適切なエンコーディング変換を行っている。

public static void toFile_good(String filename, String string, String charset)
  throws IOException {
  
  Charset cs = Charset.forName(charset);
  CharsetEncoder coder = cs.newEncoder();
  FileOutputStream stream = new FileOutputStream(filename, true);
  OutputStreamWriter writer = new OutputStreamWriter(stream, coder);
  writer.write(string, 0, string.length());
  writer.close();
}

ファイルからデータを読み出す際には、FileInputStream オブジェクトおよびInputStreamReader オブジェクトを用いること。後者は CharsetDecoder 引数を取るが、この引数はファイルへの書込み時に指定した引数と一致するものでなくてはならない。

リスク評価

標準的でない方法で文字セットの変換を行うと、データの欠損が生じる可能性がある。

ルール 深刻度 可能性 修正コスト 優先度 レベル
IDS12-J P4 L3
関連ガイドライン
MITRE CWECWE-838, "Inappropriate Encoding for Output Context"
CWE-116, "Improper Encoding or Escaping of Output"
参考文献
[API 2006]Class String
[Hornig 2007]Global Problem Areas: Character Encodings
翻訳元

これは以下のページを翻訳したものです。

IDS12-J. Perform lossless conversion of String data between differing character encodings (revision 61)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter