yamicha.com's Blog - Presented by yamicha.com
Blog yamicha.com's Blog - 2018/07 の記事
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] [26] [27] [28] [29] [30] [31]

yamicha.com's Blog
 諸事情により、現在更新休止中。ご了承ください。もし今後ブログを再開することがあるとすれば、その際にはこのブログスクリプトではなく、新しく開発したものによるかもしれません。
 当ブログ管理者についてはこちらをご参照。
開発魔法(737)
社会問題(733)
お知らせ(11)
質問・バトン回答(15)
ゲスト出演(8)
経済・知的財産(150)
ゲーム開発(182)
[Ada] 勝手に補足
- Note
- 金配りの次の一手


- Endless Ultimate Diary
- 銃世界

漢字バトン
- うるる雑記帳
- 漢字接力棒

ツキアイゲノムバトン
- ブログ@うにうに画像倉庫
- あぶ内閣

縺イ縺セ縺、縺カ縺励ヰ繝医Φ
- 月夜のボヤキ
- 騎士サーラバトン
パスワードを使う
名無し (2012/02/27)


開発者解放裁判
yamicha.com (2010/03/14)
Winnyに関しては、私も「純白」とまでは考えておりませんし、使用し..

開発者解放裁判
通りすがり (2010/03/08)
winnyに関しては「ダウンロードソフト板」なんてところを拠点に開発..

新型インフルエンザの恐怖
いげ太 (2009/11/03)
> C#などの「int Some(int , int)」は、F#では「(int * int) ->..

時効に関する思考
yamicha.com (2009/08/31)
>いげ太さんコメントありがとうございます。手元にドキュメントが少..
Homepage
Blog Top
Normal View
List View
Search
Calendar
Comment List
Trackback List
Blog RSS
Send Trackback
Phone Mode
Administrator
yamicha.com
Blog
るううるる。
Source
法令データ提供システム
FindLaw
Development
Java2 Platform SE 6
Java EE 6 API
MySQL Developer Zone
PHP Reference
MSDN Library
Ada Reference Manual
Objective Caml
Python Documentation
Erlang
Prolog Documents
真に言論の自由の侵害
2007/07/26(Thu)23:45:07
 ジャンルにもよりますが、ブログは後から読み返すことで当時の世相やトレンドを理解し、当時起こった事件のあらましを知ることができます。これは時事系ブログに限ったことではなく、さらにはブログに限ったことでもなく、社会系サイト・ブログなら事件や事故、経済系なら証券取引法改正や産業の上下、ゲームサイトなら話題の作品といった具合に、色々と内容が変わってきます。「身近な話題」のブログでも、昔に比べれば携帯電話やら何やらが登場することが増えているでしょう。
 しかし、最近はいくつかの新聞社が「ブログのキーワードで参院選を分析」などといったことをやっていますが、その行為に意味はあるのでしょうか。ブログに開設者名簿などありませんから、基準となるブログを「無作為抽出」することはできません。しかもブログ自体「ブログと名乗ればブログ」であり、明確な基準はありません。トラックバックが基準の1つとなりそうですが、スパムなどを理由に制限するブログも少なくありません。会員同士でのみトラックバックできるブログもありますが、あれは厳密にはブログに当てはまらないでしょう。
 すなわち、ブログの分析は正確なものとはなり得ません。「ある大手のブログサービス業者に協力を求めて調査」なら可能でしょうが、業者によってブログの内容に何らかの傾向が出る可能性があります。また、これでは自前のブログを分析することができません。自前のブログを開設する場合、ある程度のPC知識が必要ですから、レンタルを使う人にPCに精通した人はいても、サーバー領域提供業者が簡単ブログ開設サービスでもしていない限り、自前の人にPCの素人はいません。
 大した違いでないと言うなかれ、これだけでも考え方は分かれます。少なくとも政府のIT(イット)とやらの政策に敏感であることが考えられますし、その中の少なからぬ人が政府の同政策に批判的です。それに関連し、個人情報保護や住基ネットなどにも明るい人が多いようです。作者の知性に関しては、ほぼ誤差の範囲でしょうが、「レンタル」より「自前」の方が明らかに難易度が高いため、「底辺のレベルが数段高い」可能性があります。
 このような具合ですから、正直なところ「ブログで見る選挙動向調査」は信用なりません。まじめなブログと意味不明な他者中傷ブログが同等の位置づけなのも奇妙ですし、調査手法に疑問も残ります。
 選挙前にはブログに「選挙記事」が増えるのは事実のようですが、こうした記事は「賞味期限」の維持が難しいため、積極的に取り上げるかは悩みどころです。それというのも、ブログで時事問題を書くのは後からでも資料として使えるものの、選挙がらみの話題は余程気をつけておかなければ「その場限り」になってしまうことが多いのですが、たまにはそれも一興ということで。
 さて、参院選に敗北したらどうするか。初期のうちから明言していた小沢氏に対し、退陣しない意向を示す安倍氏。「参院選は政権選択ではない」というのがその理由です。今から予防線を張っておくとは潔くありませんし、実際に自民党が負けると決まってもいないため何ともいえませんが、別に退陣されてもされなくても結構です。ご自身で判断なさると良いでしょう。
 なぜなら、今回の参院選で民主党が勝つべき理由は安倍内閣(もし退陣したら後継内閣)のレームダック化を図ることであるためです。自民党・安倍内閣の横暴をこれ以上許すと大変なことになります。かといって小沢氏に政治を任せることはできません。したがって、ここは両者ともに身動きが取れない状態になっていただき、安倍・小沢の旧世代のリーダーに何もさせることなく、双方に新しいリーダーを迎えた方が良いのです(一応明記しておきますと、安倍氏は比較的若いのではありますが、いくら若くとも所詮は現在のリーダーです。つまり、新しいリーダーが誕生した時からすれば旧世代のリーダーです)。
 衆院に2/3の議席があれば原理的には参院を無視できますが、実際にそれをするのはなかなか難しいようです。また、今の自民党では何を間違っても2/3の議席を取ることはできず、仮にどれほど優秀な指導者が現れたとしても、低IQ選挙以外で2/3を取るのはそもそも困難です。すなわち、参院選で民主党が勝てば仮のレームダック化、そして参院選直後〜2年以内に衆院選が行われれば、その後は完全なレームダック化が起こります。参院には解散がありませんので、レームダックは安定して3年間続きます。
 後はその間に双方の旧世代のリーダーが消え、新しいリーダーに代われば良いのです。3年後の参院選、できればその前の衆院選から新しいリーダーに取り替えれば最も良いのですが、そもそも衆院選は時期が不定ですし、安倍内閣が「私の内閣の信任選挙」などと称して自己都合が良く絶対に自分が勝てる時期にぶつけてくる可能性もありますので、こればかりは何とも言えません。新しいリーダーがまともな人間かは分かりませんが、使えない旧リーダーを取り替えた結果ですからマシにはなるでしょうし、今回の参院選で自民党が痛い目を見れば「選挙ではどちらが勝つか分からない」ことが証明されますから、どちらの党も必死でやることが期待できます。それでもダメならリーダーを変えるのみです。それが二大政党制のそもそもの思想です。
 今回の参院選で目指すべきは「レームダック化による新しいリーダーの登場」であり、したがってこれの結果で安倍氏がどうしようが知ったことではありません。潔く退陣して新しいリーダーに代わっていただけば、それはそれで良いのではありますが、「ポスト安倍」が麻生氏では大した違いがあるようには見えませんし、かといって小沢氏に政権を担わせるのも避けたいところですから、どのみち「レームダック」を目指すのみです。結局のところ、次の参院選、できればその前の衆院選までに新しいリーダーと交代していただけば良いのであり、それまでの安倍氏の進退などどうでも構いません。
 ひとまずレームダック化さえさせておけば、安倍内閣が日本をこれ以上悪化させないうちに新しいリーダーに引き継がせることができます。最近は「参院不要論」なども飛び出していますが、その是非はここでは問わないとして、もし参院選に使い道があるとすれば、やはり「レームダック化を起こして新しいリーダーを生じさせる」ことを置いて他にありません。これが衆議院だけでは、二大政党の両方のリーダーが信用ならなくても、必ずどちらかが選出されることは避けられませんが、参議院があればそれは回避できます。現在の両党のリーダーが信用ならなければ衆院選と参院選でそれぞれ違う政党が選ばれ、信用されれば両方で同じ政党が選ばれることになれば、参議院にも意味は出てくるでしょう。
 それにしても気に入らないのが森氏。「自民党が大敗すれば北朝鮮が喜ぶ」とのことですが、北朝鮮問題をダシにして国民を脅し、自民党への票を貢がせようなどとは、どこまで拉致被害者をバカにしますか。唯一の判断材料は「安倍内閣に代わってから、北朝鮮問題はほとんど進展がなく、むしろ悪化が目立った」ことだけであり、これだけが信頼できる情報です。口では大阪の城も建ちますし、拉致被害者を全員取り戻すこともできるのです。
 安倍内閣が北朝鮮問題を解決できないのは結果論と言うなかれ、私は総裁選の時点でそれを予想していました。単に起こるべくして起きたことに過ぎません。それにもかかわらず、北朝鮮問題を利用して票を稼ごうなどとは、拉致被害者及びその救助に尽力される家族や関係者の方々への重大な侮辱であり、これだけは絶対に許すことができません。せめて、あの失言の森が自分勝手に言い出したことであり、安部氏が同じ考えでないことを望みます。しかし、森氏も1度は拉致被害者家族の講演でも聞いてはいかがでしょう。あの訴えを実際に聞けば、それを票に利用しようとしている自分の愚かさに気づけるはずなのですが。もし彼らの訴えを聞いた上でそれを言っているのなら、もう手の施しようがありません。

 総務省がいよいよ本腰を入れ始めました。インターネット情報規制社会の到来です。「有害情報の規制」と銘打ってはいますが、圧力や懐柔である程度は意のままに操れるマスコミとは違い、インターネットは政府にとってかなりの脅威です。国内のアダルトや残虐サイトを規制するというならともかく、これをブログによる情報発信にも適用するそうです。私個人の考えを述べるなら、どうなろうと総務省ごときに屈服する気はありませんが、これが恐ろしい傾向であることは否定できません
 確かに、インターネット上にも取り締まらなければならないものがあるのは否定しません。児童ポルノサイトであったり、振り込め詐欺やその他の犯罪希望者を集める「裏」求人サイトは存在すべきではありませんし、ネット上の中傷に対しては非常に厳しい姿勢で臨まなければなりません。例えばイラク人質事件の際は、被害者宅に中傷電話をかけた連中を1人残らず摘発し、刑事罰に処すべきでした。掲示板やサイトによるいわれのない中傷に民事のみで対処するのは限界ですので、こちらにも刑事罰が必要です。以前に民主党へのいわれなき中傷が問題になったことがあり、「裏に何か団体が絡んでいて、その団体が中傷をネット上で広めようとしているのでは」との推測も出ていましたが、こうしたものも野放しにはできません。いくら損害賠償請求を受けても逃げ回るような人間には、他者との公平性を確立する上でも、しっかり刑事罰を与えた上で国が損害賠償分を取り立てて配分するようなことも必要です。
 しかし、そうでないデータについて総務省が介入することは避けるべきです。何でも、発信するデータに対して「青少年の保護」や「人間の尊厳の尊重」を求めるそうですが、何をすれば青少年を保護し、人間の尊厳を尊重できるのでしょう。政権に批判的なことを書いて「青少年をたぶらかした」などと言われなければ良いですが。ちなみに、今のところ罰則はないようですが、各自治体がこれを条例によってオーバーライドし、それに罰則をつけることは認めるようです。
 確かに「クラブきっず」(交通事故の犠牲者の児童を勝手に転載していたサイト)のようなサイトが問題なのは当然ですが、これは実際に既存の法律で対処できた上、それに欠陥があるというなら既存の法律を拡張する形にすれば済みます。特に「自殺サイト」を制限しようという意図が分かりません。自殺サイトによる自殺はやたら大きく報じられますが、年間3万人が自殺している現状を考えれば、マスコミが恣意的に問題を大きくしていることは明らかです。そして、自殺サイトによって命を救われた人の話はめったに報道されません。以前に練炭自殺問題が繰り返し報道されましたが、それによる死者が数十人としても、自殺サイトで思いとどまって命拾いした人はその何倍にも上るでしょう。
 さらにこっけいなのが、「業界と協力し、サイトに有害情報が含まれるかを事前に判別できるシステムを開発する」という点です。何かの利権がらみでしょうか。そういえば安倍氏が「情報処理推進機構」の議長をやっていたりしましたが。社会保険庁のシステム開発でも、裏で天下りが行われていたというではありませんか。
 ここで面白い小話を2つほど。まず、米国のフィルタリングで大統領に関する政府のサイトが弾かれたことがあったそうです。日本で言えば、首相官邸のサイトが弾かれるようなものです。利用者もさぞ驚くことでしょう。理由は「カップル」という文字が含まれていたからとか。どこまで本当かは知りませんが、現状のフィルタリング技術などこの程度です。
 もう1つ、これはキッズgooについて指摘したサイトに記載されている有名な話なのですが、キッズgooでは有害情報のフィルタリングサービスを提供しており、有害とされたサイトにはアクセスできません。ところが、そこまで当たり障りのなさそうなサイトが規制される反面、gooサイト内の「過激な濡れ場にも大胆にアタック」などといった真っ先に制限されそうな文言が素通り。わざとなのか、フィルタリングが使い物にならないのかは知りませんが、あまりにもひどいフィルタリングもあったものです。
 総務省は本気でこのようなものを作る気なのでしょうか。フィルタリングには「文言によるオートフィルタ」「ホワイトリスト」「ブラックリスト」のおおむね3種類がありますが、文言フィルタで政府サイトを弾くようではお笑い種ですし、ブラックリストでは抜けが大きすぎてフィルタとしては使い物にならず、ホワイトリストは逆に安全と認められたサイトしか使えませんので、ほとんど言論規制と同等になります。
 また、インターネットに「PG12」やら「R15」と同等のものを導入することも検討しているようですが、一体どのような技術を用いてそれをするのでしょう。まさか「METAタグにレートを埋め込め」やら「robots.txtに書け」とは言わないでしょうが。CEROレートでもあるまいし、1つ1つサイトを見ていってレート分けなどできるわけがありませんし、意味不明です。
 結局、これらは「言論の自由の侵害」か、あるいは「実現できるわけがない誇大妄想」のどちらかです。さすがはIT(イット)大国の総務省、私のような凡才とは考えることが違うようです。このようなことを書くと、あとあと総務省様に有害サイト指定されてしまうのでしょうか。政府に無害サイトとみなされるような内容を書くよりは、その方が良いですが。
 しかし、マスコミは被害者遺族らを散々苦しめたり、場合によっては犯人をでっち上げたりしているというのに、なぜこちらはロクな規制が行われず、インターネット規制については先走りするのでしょう。インターネットは「人間の尊厳の尊重」を遵守しなくてはならず、マスコミはしなくて良いというのはおかしいにもほどがあります。

 もし日本に参院がなく、安倍と小沢を選べと言われたら。究極の選択です。JavaとPerlのどちらかを選べと言われたら。これもまた究極の選択です。Perlでも5と6では全然違いますが、どちらの正規表現が良いかと言われたら。これもまた選べたものではありません。
 Perlといえば正規表現、正規表現といえばPerl。黒魔術と評されようが、「Write once, Decode anywhere」であろうが、これなくしてPerlは語れません。結果、Perl使いはSQLのREGEXP文もPHPのpregもjava.util.regexも使える(文字列に関しては)万能開発者となるのです。きっと。pregにせよ、java.util.regexにせよ、おおむねPerl互換になっています。
 しかし、せっかくこうして歩調をあわせてくれているのに、Perl 6と来た日には、完璧に正規表現を変えてくださっています。一応「m:perl5」指定で以前の正規表現も使えるようにはなっているのですが、Perl 6を語るなら新しい正規表現は避けて通れません。
 ところが、なぜかPugsでは正規表現が動作しないではありませんか。Googleで調べたところ、どうやら「C:\Perl6\site\lib\auto\pugs\perl5\lib」ディレクトリを作り、以下にPugsのzip内の「perl5\Pugs-Compiler-Rule\lib\Pugs」を展開しなければならないとのこと。なぜCドライブ決め打ちなのでしょう。もうCドライブは容量オーバー寸前というのに。理不尽さにあきれつつ、仕方がないのでやってみたところ、それでもなお動作しません
 色々試行錯誤をしてみた挙句、これで無理なら正規表現をあきらめてクラスでも習得するとして、CPANからRegexのライブラリ(Pugs-Compiler-Rule 0.25)を落としてきました。これを上書きしたところ、見事に動作してくれました。
 それでは早速試してみましょう。Perl 5では「=~」で正規表現を使っていましたが、Perl 6では「~~」になっています。ちなみにPerl 6では「~=」で文字列の追加です。普通の代入演算子は「+=」のようにイコール記号が後なのですが、Perl 5までの正規表現はイコールが先です。覚えておくと迷わずに済みます。
"str" ~~ /[a-z]/
 結果、早速false。いきなりやる気を98%ほどそがれましたが、くじけてはいけません。割合ダメージで死ぬことはありません(どういう意味なのやら)。
 しかし、まさか文字クラス指定がいきなり使えなくなっているとは。たかがこの程度のコードでも、やはり下調べが必要なようです。そこで出てきたのが「[...]はPerl 5の(?:...)と同じである」という驚愕の事実。(...)でくくった部分は、正規表現処理後に特殊変数「$1」「$2」としてカッコの順番に参照できるのですが、Perl 5では(?:...)とすると参照を行わないようにすることができました。Perl 6では代わりに[...]を使うとのこと。つまり、参照が行われない(...)と同じなのです。扱いは(...)と同じですから、[a|b|c]などと書くことができます。特殊変数にはもう1つ重要な変更が加えられており、これまでは「$0」でマッチ部分全体、「$1」で最初のカッコとなっていたのが、Perl 6では最初のカッコが「$0」に対応します
 では文字クラスはどのように使えば良いのでしょう。何でも<>で囲むそうです。これで囲んだ部分の先頭が「[」、あるいは「+」で始まっていれば、文字クラスを使うことができるといいます。すなわち、上記のコードを書き直すと、
"str" ~~ /<[a-z]>/
"str" ~~ /<+[a-z]>/
 上記の好きな方を使うことになります。マッチ回数指定(量指定の+,*,?及び最短マッチの?)は<>の後につけます。また、これにより<及び>は正規表現で予約され、使う時にはエスケープが必要になっています。
 その他、Perl 5からの主要な変更としては、DOTALLモードが常にONになっています。Perl 5の「/.../s」と同じです。結果、「.」は改行も含むあらゆる文字にマッチします。COMMENTSモードもONになっており、こちらも「/.../x」と同じになっているようです。空白は無視され、コメントが有効になります。空白が無視されるため、以下の記述はマッチしません。
"This is the Perl" ~~ /This is the Perl/
 次のように書き直せば動作します。
"This is the Perl" ~~ /This<ws>is<ws>the<ws>Perl/
"This is the Perl" ~~ /'This is the Perl'/
"This is the Perl" ~~ /This' 'is' 'the' 'Perl/
 見ての通りですが、「'...'」でくくった部分はリテラルとして処理できます。それに伴って\Qと\Eは廃止されています。しかし、リテラル内に'を含む場合はどうすれば良いのでしょう。エスケープもできませんでした。これではお手上げです。ドキュメントにはエスケープできるらしいことが書いてあるのですが。
 Perl 5の^及び$は、これまでは行頭と行末を表すことがありましたが(MULTILINEモード)、今回からは文字列の先頭と末尾に固定されています。これについてはそこまで重い変更ではなさそうです。その代わり、行頭と行末を表すものとして「^^」「$$」があります。ただし、これらは純粋に行頭と行末を検出するだけであるため(確かPerl 5の行頭及び行末でもそうですが)、これ自体が改行を表すものではありません。したがって、「"\n" ~~ /($$)/; $0.say」では$0は空っぽであり、何も表示されません。
 <>で指定する文字列は演算できます。java.util.regexの条件指定と似たようなことが可能です。
"1048576" ~~ /^<[0-9]-[4-5]>+$/	# false
"16777216" ~~ /^<[0-9]-[4-5]>+$/	# true
 前方参照や後方参照も可能ですが、やり方が変わっています。
<before ...>	# = (?=...)
<!before ...>	# = (?!...)
<after ...>	# = (?<=...)
<!after ...>	# = (?<!...)
 ただし、afterはドキュメントでは動作するように書いてあるのに、Pugsでは動きません。バックトラックなしの読み込みもできます。とはいっても、Perl 5では(?>...)のスタイルを使っていたのに比べると、Javaの「強欲な指定子」に近いです。
<[...]>+:	# = (?>[...]+)
 正規表現には「クロージャ」機能を使ってコードを埋め込むことができます。
"perl" ~~ /{"pe" =~ "rl"}/
 内部で変数を使用したり、特殊変数を使用することもできます。また、マッチを失敗させるfailを返すこともできます。と、ドキュメントにはそのようなことが書いてあるのですが、実際にはできません。Pugs + Compiler-Rule 0.25ではこの辺りが限界ですか。
 今回、回数指定マッチ([a-z]{3,5}など)はなくなっており、代わりに**及びクロージャを使った指定(<[a-z]>**{3..5})をするようになっているのですが、クロージャに対応してくれない限り、これは使えません。非常に痛いですが、使えないものは仕方ありません。afterといい、対応が待たれます。
 Perl 6の正規表現の目玉の1つは「ルール」です。これを使うと正規表現を使いまわすことができます。
rule thisis {'This is the '.*};

"This is the Perl" ~~ /<thisis>/	# true
"That is the Perl" ~~ /<thisis>/	# false
 これは結構便利そうです。「rule a{[あ|い|う|え|お]}」など。ただ、コマンドプロンプト上では文字コードでエラーになるらしく、試してみることはできませんでした。
 ルールは最初からいくつか定義されており、「<rule>」の構文で使うことができます。使えるものとしては、alpha(アルファベット)、lower/lowercase(小文字)、upper/uppercase(大文字)、digit(整数)、xdigit(16進)、wp(空白 == ' ')、sp(空白文字 == \s)などの他、何とhiraganaやkatakana(読んで字の如く)まであります。ただし上記と同じ理由につき、ひらがなやカタカナは試していません。Perl 6は国際語のようで。
 後の細かな(しかし重要な)変更点としては、これまでの$0は$/になっています。上記でもいくつか述べましたが、スイッチのx,s,m,e(コメント有効及び空白無効,DOTALL,MULTILINE,関数展開)は全部廃止です。iやgは健在ですが、これらは「m:i:g/.../」のように指定します。置き換える場合は「s:i:g/.../」のようになります。また、perl5なるスイッチもあり、これはPerl 5の正規表現を使います。
 マッチ回数も色々指定できるようになっています。「1回か繰り返しか」だけでなく、「3回マッチ」や「5回目から置き換える」といった指定が可能です。が、例によって動作しません。使えれば便利そうなのですが。

まとめ
機能Perl 5Perl 6
スイッチ-x及びsが最初から入っている
文字クラス[a-z]<[a-z]> or <+[a-z]>
先読みマッチ(?=...) , (?!...)<before ...> , <!before ...>
後読みマッチ(?<...) , (?!<...)<after ...> , <!after ...>
バックトラックなし(?>...)...*:,...+:,...?:
回数指定マッチ[a-z]{3,5}<[a-z]>**{3..5}
特殊変数への格納なし(?:...)[...]

 これだけ覚えておけば、とりあえず打てるでしょうか。

 Perl 6も面白いのではありますが、現役といえばやはりPerl 5。言語はそれぞれ長所と短所がありますから、Java 1.4と5のようなアップグレードに関してはともかく、特にPerl 5とPerl 6のような「明らかに別物化している」ような言語については、どちらが有用かを選択することは難しいです。
 選択といえば、私にとって少々印象深いエピソードを。「おサイフケータイ」が出始めたころ、毎日新聞のサイトでこれに関する質問を出していました。それは別に良いのですが、用意された答えが「使ってみたい」「使ってみたいが少し不安」「おサイフケータイって何?」の3つだけというすさまじさ。私は当然、「おサイフケータイ」に関しては知っていましたが、何の魅力も感じませんでした。一体どれを選べというのでしょうか。「知っているが利用価値を見出せない」が私の答えです。
 さて、おサイフケータイより利用価値が高いPerl 5を。最近、掲示板などへの非常に悪質なスパム書き込みが横行しているようです。英文の書き込みならasciiのみの文章を弾けば問題ありませんが、日本語を使われる場合はそうはいきません。幸い、私は日本語の書き込みの被害にあったことはありませんが。
 このような書き込みをする悪質な人間や団体は、当然ながらサイトを逐一見て書き込むようなことはしていません。仮に1回の書き込みに30秒を要するとして、1000件書けば30000秒もかかりますが、やはり各サイトの管理者が同様に30秒でそれを消すとすれば、1000人が30秒ずつの手間で済みます。これでは業者が根負けしますので、書き込みに必要なフォームの引数をあらかじめ調べておき、ロボットで一気に書き込んでいるものと考えられます。
 これをやられると、サイトの管理者としてはたまったものではありません。当サイトでさえ(英文ながら)これに迷惑を被ったというのに、大手のサイトともなるとどれほど手を焼いていることでしょうか。想像もできません。そこで最近、画像で暗号キーを表示し、それをユーザーに入力させて人間であることを確認する手法が多く取られるようになりました。
 しかし、実際のところ画像解析技術がどこまで進んでいるかは知りませんが、単に画像で文字を表示するだけでは、それをロボットに解析されて読み取られる恐れが無きにしも非ず、です。OCR技術なども存在することですし。そこで、この手法を導入している多くのサイトでは、画像をあえて読み取りにくいように加工し、人間のみが解読できるようにしています。
 どうやら業者連中は人気のないサイトでもお構いなしの模様ですので、当サイトもいつ被害にあうとも知れません。また、実装もさほど難しくなさそうですので、実際に作成してみることにしました。このコードを被害者の皆様にささげます。流用は少々難しいかもしれませんが、当然ながら使用も改変も自由とさせていただきます。ソースは開発者の共有財産です。
#!/usr/bin/perl

# CREATE TABLE auth_check_img(number INT AUTO_INCREMENT , 
# data VARCHAR(32) , dated DATETIME , PRIMARY KEY(number));

use DBI;
use Encode;
use Encode::Guess ("utf8" , "shiftjis" , "euc-jp" , "7bit-jis");
use Image::Magick;
use Digest::SHA1;

$auth_time = 60*60*8;

&param(\%set);

if($set{'mode'} eq ""){
	&head; &form; &end;
}
if($set{'mode'} eq "auth"){
	&head; &auth; &end;
}
if($set{'mode'} eq "img"){
	&view_image($set{'id'});
}

sub form{
	my $auth = &create_auth;

	my $id = ®ist_image($auth);
	if(!defined($id)){
		&errmsg("データベースの不具合でご利用になれません。");
	}

	my $sha1_auth = &sha1($auth);
	my $time = time;
	my $sha1_time = &sha1($auth . $time);

	print <<"EOF";
<b>入力フォーム</b>

<form method="POST">
<input type="hidden" name="mode" value="auth">
<input type="hidden" name="auth_id" value="$sha1_auth">
<input type="hidden" name="auth_time" value="$sha1_time">
<input type="hidden" name="time" value="$time">
<img src="?mode=img&id=$id"><br>
<br>

上記画像の文字列を入力してください。<br>
<input type="text" name="auth" size="15"><br>
<input type="submit" value="送信">
</form>
EOF
}

sub auth{
	my $auth = $set{'auth'};

	# 正当性を確認
	my $deny = 0;

	if($set{'time'} + $auth_time < time){
		# 有効期限切れ
		$deny = 1;
	}elsif(&sha1($auth) ne $set{'auth_id'}){
		# 認証 ID が不正
		$deny = 1;
	}elsif(&sha1($auth . $set{'time'}) ne $set{'auth_time'}){
		# 時間加味後の ID が不正
		$deny = 1;
	}

	if(!$deny){
		print "<b>認証成功</b><br>";
		print "正しく認証されました。";
	}else{
		print "<b>認証失敗</b><br>";
		print "入力IDは不正です。";
	}
}

sub create_auth{
	my @texts = ();
	# 紛らわしい文字は使わない
	push(@texts , a..z);
	push(@texts , 1..9);

	my $input = "";
	for(my $i = 0; $i < 8; $i++){
		$input .= $texts[rand(@texts)];
	}

	return $input;
}

sub connect{
	my $db_host = "www.localyamicha.com";
	my $db_user = "yamicha";
	my $db_password = "password";
	my $db_database = "yamicha";

	$connect = DBI->connect(
		"DBI:mysql:$db_database:$db_host:mysql_server_prepare=1" , 
		$db_user , $db_password);

	$connect->do("SET NAMES sjis");

	# 有効期限切れのデータは削除
	my $ps = $connect->prepare(
		"DELETE FROM auth_check_img WHERE " . 
		"UNIX_TIMESTAMP() - UNIX_TIMESTAMP(dated) > ?");
	$ps->execute($auth_time);
	$ps->finish;

	return $connect;
}

sub regist_image{
	my $input = $_[0];

	my $connect = &connect;

	if(!$connect){
		return undef;
	}

	# データを登録
	$connect->do("BEGIN");

	my $ps = $connect->prepare(
		"INSERT INTO auth_check_img VALUES(NULL , ? , NOW())");
	$ps->execute($input);

	# ID を得る
	my $id = "";
	$ps = $connect->prepare("SELECT LAST_INSERT_ID()");
	$ps->execute();
	$ps->bind_col(1 , \$id);
	$ps->fetch();

	$ps->finish;

	$connect->do("COMMIT");

	$connect->disconnect;

	return $id;
}

sub view_image{
	my $id = $_[0];

	print "Content-type: image/gif\n\n";

	my $connect = &connect;

	if(!$connect){
		exit;
	}

	my $data = "";

	# データを取得
	$connect->do("BEGIN");

	my $ps = $connect->prepare(
		"SELECT data FROM auth_check_img WHERE number = ?");
	$ps->execute($id);
	$ps->bind_col(1 , \$data);
	$ps->fetch();

	# 取得したデータを削除
	$ps = $connect->prepare(
		"DELETE FROM auth_check_img WHERE number = ?");
	$ps->execute($id);
	$ps->finish;

	$connect->do("COMMIT");
	$connect->disconnect;

	# 画像を表示
	binmode(STDOUT);
	print &image_create($data);
}

sub sha1{
	$sha1 = Digest::SHA1->new;
	$sha1->add($_[0]);
	return $sha1->hexdigest();
}

sub image_create{
	my @texts = ();
	push(@texts , a..z);
	push(@texts , A..Z);
	push(@texts , 0..9);

	my $input = $_[0];

	my $img = Image::Magick->new(size => "250x100");
	$img->Read("xc:#FFFFFF");
	$img->Read("xc:#FFFFFF");

	for(my $y = 5; $y < 250; $y += 20){
		for(my $x = 5; $x < 250; $x += 20){
			my $str = $texts[rand(@texts)];
			my $color = sprintf("#%X02%X02%X02" , 
				rand(64) + 128 , 
				rand(64) + 128 , rand(64) + 128);
			$img->[0]->Annotate(text => $str , 
				pointsize => 18 , 
				x => $x , y => $y , fill => $color);
		}
	}

	$img->[1]->Transparent(color => "#FFFFFF");

	my $loop = 0;
	for(my $i = 0; $i < 8; $i++){
		my $str = substr($input , $i , 1);
		my $color = sprintf("#%X02%X02%X02" , rand(64) + 64 , 
			rand(64) + 64 , rand(64) + 64);

		$img->[1]->Annotate(text => $str , 
			pointsize => 42 , 
			x => $loop * 30 + 8 , y => rand(35) + 40 , 
			fill => $color);

		$loop ++;
	}

	$img->[1]->Wave(amplitude => 6 , wavelength => 64);

	$img->[0]->Composite(image => $img->[1]);

	my @blobs = $img->[0]->ImageToBlob(magick => "gif");
	return $blobs[0];
}

sub head{
	print <<"EOF";
Content-type: text/html

<html>
<head>
<title>Check</title>
</head>
<body>
EOF
	$head_impl = 1;
}

sub end{
	print "</body></html>";
	exit;
}

sub errmsg{
	if(!$head_impl){
		&head;
	}
	print "<b>Error</b><br>";
	print $_[0];
	&end;
}

sub param_parse{
	my $buffer = "";

	if($ENV{'REQUEST_METHOD'} eq "POST"){
		read(STDIN , $buffer , $ENV{'CONTENT_LENGTH'});
	}else{
		$buffer = $ENV{'QUERY_STRING'};
	}

	&param_form($buffer , $_[0]);
}

sub param_form{
	my @pairs = split(/&/ , $_[0]);
	my $set = $_[1];
	foreach(@pairs){
		my($name , $value) = split(/=/ , $_);
		$value =~ tr/+/ /;
		$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C" , hex($1))/eg;
		if($set->{$name} eq ""){
			$set->{$name} = $value;
		}else{
			$set->{$name} .= "," . $value;
		}
	}
}

sub param{
	&param_parse(\%set);

	while(my($name , $value) = each(%set)){
		$set{$name} = &param_encode($value);
	}
}

sub param_encode{
	my $value = $_[0];

	my $guess = Encode::Guess::guess_encoding($value);
	if(ref $guess){
		$guess = $guess->name;
	}else{
		$guess =~ s/^(.*?) or//;
		$guess = $1;
	}

	if($guess ne ""){
		Encode::from_to($value , $guess , "shiftjis");
	}
	return $value;
}
 データベース(MySQL 5.0)を使っていますが、別に使わなくても実装は可能です。ただし、Image::Magickは必須です。
 プログラムの流れとしては、まずPerl側でアルファベット(a-z及び1-9。紛らわしいので大文字と0は使っていません)8文字を組み合わせてランダムな文字列を生成しています。その文字列をデータベースに登録し、画像の呼び出しに備えます。もしデータベースを使うことができなければ、ファイルに保存しても構いません。ただ、何らかのトラブルによって生成されっぱなしで削除されなかったデータが残ってしまった場合、それを削除するのは有効期限をWHERE節に指定したDELETE文一発で済むデータベースの方が簡単です。ファイルを使用する場合には、削除処理を手動で実装しなければなりません。
 本当は「文字列をデータベースに保存し、imgタグでCGIを呼び出した際にそれを取得して表示」というプロセスを踏まず、imgタグによってCGIを呼び出し、そこで生成と表示を同時に行ってしまいたいところなのですが、CGIに画像を生成させるには文字列情報が必要です(その文字列を含む画像を作るのですから当然です)。データベースやファイルを使わない場合、引数によってデータを渡すしかありません。しかし、引数にそのまま生成する文字列を渡してしまうと、ソースを見ることで入力すべき文字列がバレてしまいます。かといって可逆暗号方式で渡したところで、当然ながら解読される恐れがあります。無論、不可逆方式で渡しても意味がありません。そこで、データを一旦データベースに登録し、そのレコードのPRIMARY KEYを引数としてimgタグで呼び出すCGIに渡し、そのCGIが画像を生成して返している、というわけです。これならタグを見ても文字列がバレることはありません。今回の実装では、1度画像を取得するとデータが消えてしまうため、同じIDによって画像を2度表示することはできないのですが(実際、複数回表示する必要はまずないでしょう)、時間経過によっても削除されるようになっているため、画像取得時にデータの削除をするかしないかはご自由にどうぞ。表示によって削除を行わない場合は、「view_image」サブルーチン内の当該部分を削除してください。
 さて、これでパスワード文字列をソース内に記述することなく画像を表示することができるようになりましたが、まだ課題が残っています。まず、フォーム送信先でパスワード文字列と入力された文字列を照合するためには、パスワード文字列が必要になります。これもデータベースに登録し、PRIMARY KEYのIDを送るようにしてしまっても良いのですが、もしこの機構をブログのコメント欄などに設置した場合、誰かがコメントを書かずに記事を読むだけで、データベースにデータが登録されてしまいます(画像生成の場合は、画像が表示されるたびに元のデータが削除されるため、いくらかマシです。画像を読み込む前に移動されてしまったり、ブラウザが画像を表示しない設定になっていたりすると、それでもデータは残留しますが)。これではあまりにも無駄が多すぎます。本当はこれが最も安全なのですが。
 そこで用いるのが不可逆暗号化です。SHA1(別にMD5でも何でも良いですが)でデータを暗号化してしまえば、それをそのままソース内に表示したとしても、この文字列から元のデータを復元することはできないのです。照合する際には、ユーザーが入力した元の文字列データをSHA1に変換し、SHA1にした文字列同士を比べれば良いのです。
 しかし、これでももう1つ問題が残ります。人間が1回だけ画像を読んで文字列を理解し、その文字列とSHA1の引数を送信するようにすれば、後はロボットが書き込みできてしまいます。そこで使うのが時刻データです。エポック秒と「文字列にエポック秒を何らかの方法で加算し、SHA1化したデータ」を使い、エポック秒のねつ造を予防した上で、一定時間内の送信(サンプルでは8時間)のみを有効とします。これで仮に人間が画像を読んで文字列を解読したとしても、8時間後には書き込めなくなってしまいます。
 しかし、実はこのコードにも欠陥があります。でっち上げた文字列とエポック秒を使い、それらを自前でSHA1変換して変換後文字列を生成、それらによってチェックをクリアし、書き込まれてしまう恐れがあるのです。これはもう、フォームの受け渡しの際にもデータベースを使うしか回避方法はありません。ですが、ソースさえ公開しなければ、変換後文字列を作るのは至難の業です。今回は単純にSHA1変換(秒数に関しては「文字列とエポック秒の単純な連結」)を行っていますが、これを「SHA1の後でMD5」であったり「SHA1を3回」、他には「何らかの係数を混ぜた後にSHA1変換」などでデータをさらに暗号化するように修正します。SHA1は元のデータを1ビット変えるだけ、適当な値に1を足すだけで、生成される値がまるっきり変わりますから、これで暗号化後のデータをねつ造することはほとんど不可能になります。もし万が一解読されたとしても、単に生成方法を少し手直しすれば、相手は再び莫大な時間を費やしてそれを見つけなければならず、割に合わないのは明らかです。
 具体的には、sha1サブルーチンを以下のように書き換えるだけで、推測することはほとんど不可能になります("word"部分には適当な文字を入れてください。好きな言葉でも構いませんし、普段使っているパスワードでも構いません。また、それらをSHA1化したものでさえ構いません。文字の長さにも基本的に制限はありません)。
$sha1 = Digest::SHA1->new;
$sha1->add($_[0] . "word");
return $sha1->hexdigest();
 ソースを見られても大丈夫なコードを書きたければ、データベースなりファイルなりを使う以外にないでしょうが、そもそもソースを見られれば、データベースのパスワードまでバレてしまうはずです。つまり、相手がスパム書き込み程度であれば、上記のコードで実用上十分な安全性を確保できます
 なお、このプログラムでは以下のような画像が生成されます。参考までに。実は画像作成部分が今回のプログラムの要であるため、ここまでで述べた「ソースを見られても安全なプログラム」に書き換えることも、実は比較的簡単にできたりします(サーバー負荷が増加し、無駄なレコードが生じることは覚悟しなければなりませんが)。



 さあ、何と書いてあるでしょうか。当ててみましょう。文字列は無作為生成であり、意味はありません。
カテゴリ [開発魔法][社会問題] [トラックバック 0][コメント 0]
<- 前の記事を参照 次の記事を参照 ->

- Blog by yamicha.com -