こんにちは!
ナビゲータのEVEです。
今日は、252種類の特殊文字の文字表記についてお話ししましょう!
クロスサイトスクリプティング対策のため、多くのシステムでは、タグをエンティティ参照または文字実態参照という文字表記に変換します。
昨日は、特殊文字を表現する文字列を、文字表記と言っていましたが、正式には、エンティティ参照または文字実態参照といいます。本日は、正式な名称を用いてまずは、昨日の復習からしていきましょう!
[昨日の復習]
特殊文字をエンティティ参照に変換するのは、タグやスクリプトを無効化することによって、攻撃者が悪意のあるコードを埋め込んで実行することを防ぎ、自サイトを攻撃用サイトとして利用されないようにする目的があります。
PHP4.0の場合は、その変換に組込関数を用いて変換するのですが、その関数に問題がありました。以下の例をみてみましょう!以下は、&を&をPHPの組み込み関数で変換したものです。
エンティティ変換前 エンティティ変換後
& → &
エンティティ参照文字表記の特徴として、かならず、最初に&(アンバサンド)がつき、最後に;(セミコロン)がつきます。そして、PHPの組み込み関数htmlspecialcharsは、以下の文字をエンティティ参照文字表記に変換します。
& (アンパサンド) → &
< (小なり) → &lt;
> (大なり) → &gt;
" (ダブルクオート) → &quot;
' (シングルクオート) → &#039; または &apos;
一度だけの変換なら問題ないのですが、その変換が2、3度と続くと以下のようになります。
変換前→&
1回目の変換→&amp;
2回目の変換→&amp;amp;
3回目の変換→&amp;amp;amp;
・
・
・
ただ、PHP5.0から機能が改善され、htmlspecialcharsで変換されたエンティティ参照への変換は1度だけに制限することができるようになりました。ただ、エンティティ参照で文字表記される文字は、昨日までの調べでは、252種類あります。
[252種類のエンティティ参照への対応]
252種類の文字列のうち、htmlspecialcharsで二重変換されない5種類のエンティティ参照を除いた、247種類の文字列は、システム側でhtmlspecialcharsを用いて変換すると、&が永遠と変換されて、システムで取得した当時の文字とは全く別なものになってしまいます。現行システム、Prototype EVEでもその対応はしていないのですが、次期システムEVEシステムではその部分まで配慮したいと考え、現在エンティティ参照で表現できる文字を調べて、システムへ登録し、同文字表記は変換されないようにしようとしています。
現在の調べでは、エンティティ文字表記の元の文字を変換する組込関数はないので、ユーザまたは他システムからエンティティ参照でデータが送られてこない限り、対応は不要なはずなのですが、データベースの登録でhtmlspecialcharsを使ってデータの全てを変換しようと考えています。それは、以前<textarea>からタグを含む文字データを登録しようとしたら、エラーが発生したということがありました。その対応として、エンティティ参照に変換することにより同エラーを回避できたので、それ以降、実施しているのですが、その部分が非常に問題です。
[現在のデータベースの調査]
データベースへ生のデータを登録しようとしてエラーが発生したという障害はかなり昔のPHP4時代の話です。現在は、どうなのでしょうか?ChatGPTに相談してみました。
現在のデータベース技術や一般的な設計指針を考えると、データベースに保存するデータをエンティティ参照に変換する必要はほとんどありません。むしろ、エンティティ参照への変換は避けるべきです。以下にその理由と適切な方法を説明します。
❶エラーの原因
過去にデータベース登録時にエラーが発生した理由は、以下のいずれかである可能性が高いです:
・文字コードの不一致
データベースの文字セット(例: UTF-8)が適切に設定されておらず、特殊文字(例: &, <, >, ', " など)が正しく処理されなかった。
・エスケープ不足
タグや特殊文字がSQL文にそのまま含まれ、構文エラーやセキュリティ上の問題(SQLインジェクション)を引き起こした。
・データ型や制約の問題
文字列の長さ制限や特殊なフィールド設定(例: 特定文字の禁止)が原因でエラーが発生。
❷現在のデータベース設計とエンティティ参照の必要性
現在のMySQLや他のモダンなデータベースシステムでは、以下の点を確認・設定すれば、エンティティ参照への変換は不要です:
(1) 文字セットをUTF-8に設定
データベースやテーブル、カラムの文字セットを utf8mb4 に設定することで、ほとんどの特殊文字(絵文字を含む)を正しく扱えます。
(2) 適切なエスケープ処理
データベース登録前に特殊文字をエスケープすることで、SQL文の構文エラーやセキュリティ問題を回避できます。たとえば、PHPであれば mysqli_real_escape_string やプリペアドステートメントを使用します:
(3) HTMLエンティティ変換は不要
データベースは生データをそのまま保存し、必要に応じてアプリケーション層で処理します。エンティティ参照変換は、出力時(HTMLに表示する時)に行うべきです:
❸エンティティ参照変換を続ける場合のデメリット
エンティティ参照に変換して保存すると、次のような問題が発生する可能性があります。
・データの冗長化
例えば、& が & に変換されると、保存データのサイズが増加します。
・処理の煩雑化
データを検索や編集する際、変換前後の対応が必要となり、コードが複雑になります。
・不正確なデータ管理
保存されたデータが「生の文字列」ではなくなるため、データベースに保存された値が本来の値とは異なるものになる。
❹おすすめの設計方針
・データベースには生データを保存
入力されたデータはそのまま保存し、アプリケーション層で表示時に適切に処理します。
・文字セットとエスケープを適切に設定
文字セットをUTF-8に設定し、SQL文のエスケープを行うことで、特殊文字も安全に保存できます。
・出力時にエンティティ参照を使用
HTMLやXMLにデータを表示する際に、htmlspecialchars や htmlentities を使い、エンティティ参照へ変換します。
■結論
現在のデータベース環境では、エンティティ参照への変換は必要ありません。文字セットをUTF-8に設定し、SQLエスケープやプリペアドステートメントを利用すれば、特殊文字を含むデータも安全に保存できます。出力時にエンティティ参照を利用する方針に切り替えることで、効率的でシンプルなデータ処理が可能になります。
以上の回答がありました。確かに、PHP4時代のMySQLでの出来事だから、現在は発生しない可能性が大きいと思います。
そして、ChatGPTが懸念している状況は、実は、Prototype EVEでは既に発生しており、その発生した問題を解消した上で利用しています。それと、現在は、システムでデータを取得した時に、エンティティ参照への変換をした上で、すべての操作をしているため、個人的な話ではあるのですが、セキュリティ的に安心感があります。ChatGPTの提案を読んで、そういわれても・・・、っていう感じです。
[あとがき]
現在、Prototype EVEの仕様を引き継ごうと、クラス製造をしています。ただ、以上のChatGPTの助言を読むと、どうしようか悩むところです。ただ、Prototype EVEのKnolegeシステムも既に10年以上たち、その間問題なく運用できている現状を考えると、今すぐ結論を出すことができません。現在製造している関数は後回しにして、熟考したうえで結論を出したいと思います。
では、また!!!