先日、あるWebサービスのテストをしていたところ、UTF-8でなければいけないパラメータの文字列のなかに、 ASCIIの ESC(0x1b) が混入していて、アプリがクラッシュするという問題に遭遇しました。 原因はパラメータにISO-2022-JPのキャラクタセットの文字列を渡していたためでした。

いくら入力データが不正でもアプリがクラッシュしてしまうのはまずいので、 入力データの検査を追加することにしました。アプリはPython3で書かれています。

文字列のなかに ESC が混入しているかどうか、どうやってチェックするべきか考えていたところ、 Pythonにはisprintable()という便利な関数があることを発見しました。

isprintqable()の仕様

試してみると、ちゃんと ESC を検出してくれます。めでたしめでたしということでテストコードを書いて プルリクエストを作成しレビューに回します。

新規にバリデーションを追加するわけなので、不正データは弾いてくれるのはいいのですが、 いままで使えていた正常データに影響が出ては困ります。念の為、プロダクションのデータベースに残っている 過去データを吸い上げて、正常データが不正と判定されないかどうか調べることにしました。 しかしそこで驚きの結果が出ます。正しいデータなのに不正と判定されるものが頻発したのです。 原因は2バイトの空白文字 \u3000 でした。isprintable()はこの文字が含まれると False を返します。 ASCIIの空白文字 0x20 はもちろん printable (True) と判定されます。 2バイトスペースが印字不可とは驚きの挙動ですが、仕様書的には正しい動作のようです。 ESCの存在を直に調べるコードに書き換えて、プルリクエストを再提出。

プロダクションリリース前に発見できて良かったです。知らずにリリースしていたら、 お客さまから多くの苦情を頂いていた事でしょう。くわばらくわばら。

今回の教訓:生データでの検証は大切!

野中 哲

野中 哲

エンジニア、開発担当

2016年3月に入社。NECで衛星通信の制御用ソフト開発、アップルでMacOSのローカリゼーション、AppleShareファイルサーバの開発等に従事。プライベートではRuby,Haskellなどのプログラミングとラグビー観戦を好む。最近の興味はSwiftでiOSアプリを開発すること。FAA自家用パイロットライセンス所有。