現在JavaScriptの設定が無効になっています。このページを利用するにはJavaScriptの設定を有効にしてください。記事の内容に関していくつか訂正させていただきました。ご指摘いただいた皆様ありがとうございました。誤字脱字を修正しました。ソースコードの間違いを修正しました。BOMの記述を分かりやすい表現に修正しました。合字に関する記載を追記いたしました。JAPANで通知プラットフォームの開発をおこなっています佐々木海(@Lewuathe)と申します。普段は全社向けのPush通知プラットフォームやメール配信プラットフォームの開発、保守をしています。通知というのはPush通知にしろ、メール配信にしろ基本的には「テキストデータ」を送ることになります。プラットフォーム内ではこれらのテキストに対してさまざまな処理をかけることになるのですが、さすが日本語といったところでしょうか、一筋縄ではいかない部分が出てきました。いう話をしたいと思います。恥ずかしながら今までUnicodeとUTF-8に違いがよくわかっていませんでした。どっちも符号化方式のことなんだろうと考えていたのですが、よくよく調べてみると違いました。そもそものレイヤーが違うんですね。ConsortiumがPromoteしている文字集合と文字コード体系のことです。ConsortiumUnicode以前に存在していたさまざまな文字コード体系は互換性がなく、またサポートしている文字集合自体もバラバラでそれぞれ十分ではありませんでした。Unicodeではこの文字集合に含まれる文字それぞれに対してUnicodeスカラ値という非負整数値を対応づけています。実際コンピューターで利用する際にはこれらの値をbyte列に直す必要があります。この変換方法を文字符号化方式といいます。UnicodeではUnicodeスカラ値を定義するだけでなく、この文字符号化方式をも標準化したため文字コードの選択による混乱を避けることに寄与しています。この文字符号化方式の一つがUTF-8です。UnicodeがUTF-8よりも上位の概念にあたるわけです。言ってみれば仕様と実装といったところでしょうか。見てみると(よくよく見てみなくても)全然違うものですね。符号化方式ここではUTF-8をあげましたが、もちろんこれ以外にもUnicodeを実現する文字符号化方式はあります。(UTF-16とかUTF-32など)大事なことはこれらが「世界中にあるすべての文字を一つの一貫した方法で表現したい」というモチベーションに基いて定義されたということです。なんとも壮大で夢のある話ですね。私はWeb屋さんなのでUTF-8をよく見ます。実際今回の自分の開発しているプラットフォームで扱わなければならなかった文字もUTF-8で定義された文字列でした。ここで少しUTF-8について見て行きたいと思います。UTF-8については以下のような特徴があります。Mark)の必要がない31bitまで表現できるのでサロゲートペアが必要ないこれらの特徴を実際のbitパターンを見ながら考えてみたいと思います。bitパターン)まず表1の見方です。左のカラムが先ほど説明しましたUnicodeスカラ値を表しています。大きなひとつ右のカラムがそれを表すUTF-8のbit列です。上のbit列から、下のbit列までの範囲を表します。一行目の文字集合は8bitで表現され00000000~01111111の範囲に収まります。赤文字はUTF-8の文字境界の識別に使われるbitで実際の文字表現には使われないので、これを除いたbitを有効bitとして3カラム目に格納してあります。先ほどの特徴をひとつずつ見てみましょう。可変長(1-4byte)を使って一つの文字を表現するUnicodeで定義されている文字集合は基本多言語面から第16面までなので、U+0000からU+10FFFFまであれば事足ります。よってUTF-8では1-4byteあれば一つの文字を表すことができることがわかります。BMP)面というのは符号空間を指す一部分のことで、Unicodeでは0から16までの面に分かれています(表2)。基本多言語面に含まれます。表の赤文字で表現されているbitを見てください。一行目を除いてすべてその文字を表すbyte数の分だけ1が連続していることがわかります。2byte目以降はすべて10で始まっていることがわかります。このことから文字境界を知りたければ0で始まっているか、1が2つ以上連続しているかをbyte毎に見てあげればいいことになります。これがUTF-8の文字境界が明確である理由です。一行目の有効bitが7bitである範囲を見てください。ASCIIコードは7bitで表現できますので、この一行目の範囲をASCIIと同じ文字コードにすることで互換性を保っています。Mark)の話を簡単にします。通常ネットワークや異なるプラットフォームにテキストを渡すとき、そのエンディアンを気にする必要があります。これはbyte順を入れ替えた際に異なる文字になってしまうような文字コードの場合は特に大事です。そのためBOMという仕組みがあります。BOMはテキストデータの先頭につける数バイトのデータでこれを元にコンピュータは符号化形式などを判断し適切に復号することができます。UTF-8に関してはどの2つのbyteを見てもその順番を調べればビッグエンディアンなのか、リトルエンディアンなのかの区別をつけることができます。これは表1の各文字の1byte目と2byte目の先頭が必ず異なるためです。オーバヘッドなども鑑みて通常はUTF-8の場合はBOMが付けられない場合が多いです。BOM)31bitまで表現できるのでサロゲートペアが必要ないまずサロゲートペアの話をしましょう。はじめに少し書きましたがUnicodeは世界中のすべての文字を2byte(16bit)で表そうとしたと書きました。最大65536種類の文字を集録することができます。ところが上記のUTF-8の表を見ておかしいと思いませんか。UTF-8でも結局4byte程使ってしまっているわけですね。いろいろな言語がUnicode文字に組みいれてほしいということを言ったために結局65536種類では足りなくなってしまいました。UTF-8の場合はそもそも可変長文字として定義されているので上記のように定義すればもっとたくさんの文字を表すことができるので問題ありません。ところがUTF-16など一部の符号化方式は固定長でした。そのためUnicodeに新たに取り込まれた文字たちを表すにはすでに容量が足りなくなっていたわけです。そこで考えられたのがサロゲートペアです。サロゲートペアは以下のような仕組みです。下位サロゲート」の4バイトで1つの文字を表すつまり今まで通り基本的には2byteで表し、一部の文字については4byteで表現するという方法この方法によりUnicodeでは新たに約104万(~1024*1024)の文字を扱えるようになったただ先程も書きましたが、UTF-8は可変長1~4byteを利用できる符号化方式ですのでめんどくさいサロゲートペアを使用する必要がないわけです。冗長なUTF-8ここまでUTF-8の特徴を見てきました。UTF-8は素晴らしい文字コードです。何より互換性の高さは文字コードとしては大事な特徴だと思います。そんな完全無欠に見える符号化方式であるUTF-8にも弱点があります。ここでは冗長なUTF-8と呼ばれる脆弱性を紹介したいと思います。通常、文字と文字コードは一対一に対応しなければなりませんが、UTF-8ではそうならない場合があるという話です。通常Unicodeの符号からUTF-8で文字コードを当てはめる場合は以下のように行います。ひらがな「あ」の場合はUnicodeスカラ値をbitにあてはめ必要なbyte数が3byteだとわかります。先程の表を思い出してください。3byteであれば先頭は1110となります。あとは2番目以降のbyteの先頭が10となるようにbitを割り振っていけばよいです。このようにUnicodeスカラ値から一意に文字コードが決められるはずなのですが、実はそうならない場合があります。7bitの範囲に収まるはずなので1byteでいいのですがここでは余分な0を加えて無理やり3byte文字に仕立てあげています。これ自体はUTF-8の変換方式にはしたがっていないのですが、このような解釈に基いて変換を行うことがあると文字コードが何種類もできてしまうという事態が起きてしまうわけです。この文字コードが何種類もできてしまう問題はこの他にも「合字」により生じるものでもあるらしいです。結局同じ文字として扱えるんだから別にいいじゃんと思うかもしれません。この脆弱性はディレクトリトラバーサルなどの攻撃に利用されることがあるのでやはり対策が必要です。基本的には余計な0をつけてしまったことに問題があるので、これを判断してあげればいいです。その文字に対して割り当てられている文字コードが最小のものになっているかの判断を加えることや0の区間が続いているだけのbyteがないかなどのチェックを入れるということです。Unicodeを斬るこのブログを書く元々のきっかけは冒頭にも書きましたがUTF-8で送られてきた文字列の境界を調べたかったからなんですね。脱線してしまいました。答えはこの記事に書いてあるのですが、最後に文字境界を判断するコードを書いてまとめとしたいと思います。今回のコードで処理される文字列はNFCで正規化されたものを入力として受け取ります。合成済みの符号が用意されていない文字に関しては今回は考慮していません。bufp++;}これでUTF-8の文字列の境界を定めつつ処理を行うことができました。今回はUnicodeをいろんな視点から斬ってみました。最後には実際に斬りました。文字コードって奥が深いなと感じていただければ幸いです。JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。JAPANのサービス開発を支える技術やデザイン、文化を発信しています。Reserved.このページの先頭へ。