======================================================================== Shift_JIS 版から UTF-8 版への変更点 元にしたのは ame.x0.com の amezo.cgi(2007-11-11 / Wayback より回収) ======================================================================== 文字コードを変換しただけでは動きません。バイト数を前提にした処理が 何箇所かあるためです。以下が加えた変更の全てです。 ■ amezo.cgi [1] jcode.pl 依存の廃止 - require 'jcode.pl'; + use Encode (); 投稿の文字コード変換に jcode.pl を使っていましたが、コアモジュールの Encode に置き換えました。jcode.pl 2.10 は 641/648 行で defined %hash を 使っており Perl 5.22 以降では文法エラーになるため、ここを外せたのは 副次的に大きい効果があります。 あわせて jcode'convert という古い package 区切り(')も消えました。 この記法は Perl 5.38 で廃止されています。 [2] 投稿の文字コード正規化 - &jcode'convert(*value,'sjis'); + $value = &to_utf8($value); 受け取ったバイト列が妥当な UTF-8 ならそのまま、そうでなければ CP932(Shift_JIS) とみなして UTF-8 に変換します。古いブックマークや ボットが Shift_JIS で投げてきても化けません。 [3] 題名の長さ判定を「バイト数」から「文字数」へ - (@a + 0 > 20 || length($subj) > 40) && &error("コメントが長すぎます"); + (@a + 0 > 20 || &clen($subj) > 20) && &error("コメントが長すぎます"); 元は 40 バイト=全角20文字のつもりでした。UTF-8 だと全角1文字が 3バイトなので、そのままだと13文字で弾かれてしまいます。 [4] 投稿者名の切り詰めを文字単位へ - $name = substr($name, 0, 60); + $name = &ccut($name, 30); バイト単位の substr だと多バイト文字が途中で切れて、不正なバイト列が そのまま HTML に出力されます。文字単位で30文字に切るようにしました。 [5] 投稿サイズの上限を 1.5 倍に - length($buffer) > 5000 && &error('投稿内容が大きすぎます'); + length($buffer) > 7500 && &error('投稿内容が大きすぎます'); UTF-8 は Shift_JIS の 1.5 倍のバイト数になるため、書ける量を 元と同じくらいに保つための調整です。 [6] 削除機能の日付パース - if /(\d\d\d\d)..(\d\d)..(\d\d)..\(..\)(\d\d)..(\d\d)..(\d\d)/; + if /(\d\d\d\d).{3}(\d\d).{3}(\d\d).{3}\(.{3}\)(\d\d).{3}(\d\d).{3}(\d\d)/; 「年」「月」「日」などの区切り文字が Shift_JIS では2バイト、 UTF-8 では3バイトです。ここを直さないと、レスを削除したときに スレッドのタイムスタンプが復元されず、板の並び順が壊れます。 [7] 生成する HTML に charset と viewport を追加 + + charset は必須です。viewport はスマートフォンで読めるようにするため のもので、不要なら消して構いません。 [8] HTTP ヘッダの charset ソース表示(GET)・エラー画面・レス抜き出し表示(sub res)の Content-type に charset=UTF-8 を付けました。 エラー画面には
と meta も足しています。 [9] global.mem のパーミッションを落とす + chmod 0600, "global.mem"; このファイルには直前にスレッドを立てた人の IP が入ります。 web から読める状態だと誰でも見られるため、書き出した直後に 600 にします(作者が 2004 年に案内していた対処と同じ内容です)。 [10] スクリプト名のフォールバック + $cgi = 'amezo.cgi' unless $cgi; $ENV{'SCRIPT_FILENAME'} が設定されない環境で、投稿フォームの action が空になるのを防ぎます。 [11] 追加したサブルーチン decode_bytes() 指定の文字コードでデコードを試す to_utf8() バイト列を UTF-8 に揃える clen() 文字数を返す ccut() 文字単位で切り詰める ※ Encode::decode に CHECK を渡すと第2引数の文字列が「消費」されて 空になります。decode_bytes() が必ずコピーを渡しているのはそのためで、 ここを直接呼ぶと ASCII のフィールド(res など)が消えます。 [12] 削除機能の合言葉を「別ファイル+SHA-512 crypt」に - crypt($name, 'am') eq 'amsqKfBaSfYYU' && &del; + $delcryptfile = 'delpass.txt'; (設定変数を先頭に追加) + $delcrypt = &load_delcrypt; + $delcrypt ne '' && crypt($name, $delcrypt) eq $delcrypt && &del; + sub load_delcrypt { ... } (末尾に追加) 元は作者の合言葉の crypt 値(DES)がスクリプト本体に直書きされていて、 差し替えを忘れると作者の合言葉で削除できてしまう状態でした。 3つ直しています。 (a) そのハッシュを削除。 (b) ハッシュを本体から追い出し、別ファイル delpass.txt から読むように した。amezo.cgi は GET でソースが見える公開仕様のままだが、 合言葉ハッシュは別ファイルにあり、それを .htaccess で web から 遮断しているので外からは見えない。ファイルが無い/空なら削除機能は 無効(初期状態)。合言葉の変更も delpass.txt を書き換えるだけで、 本体を触らずに済む。 (c) ハッシュ方式を DES から SHA-512 crypt ($6$) に。 DES は先頭8バイトしか見ず総当たりにも弱いため。XServer(Perl 5.16) で $6$ が使えることは確認済み。合言葉の長さ制限も実質なくなった。 元のハッシュ 'amsqKfBaSfYYU' は DES crypt(先頭8バイトのみ有効・salt='am')。 ありがちな英単語では復元できなかったので、おそらく Shift_JIS の日本語 (実質4文字ぶん)が合言葉だったと思われます。復元は不要なため値ごと削除。 ■ dai.cgi Content-type に charset=UTF-8 を追加し、出力の先頭に charset と viewport の meta を出すようにしました。それだけです。 ■ blist.txt / header.html / ng.txt / delpass.txt blist.txt … 3板ぶんの雛形に差し替え(原本は ame.x0.com の27板) header.html … 素の雛形を新規に用意。原本は当時の@あめぞうの ヘッダそのもので、リンク先が全て消滅しているため ng.txt … 空 delpass.txt … 新規。削除機能の合言葉ハッシュを入れる(初期は空) [13] XSS 対策の強化(あめぞう型が繰り返し狙われた弱点を多層で塞ぐ) 原本も入力時に & " < > を HTML エスケープしており素朴な