WordPressのCaptchaを変更してみる(Advanced noCaptcha & invisible Captcha)

というわけで、Captchaが大分バグっている、というかとある事情で不具合が起こっていたようなので更新しました。その更新について少し書いておきたいと思います。一応コメントの書き込みテストはしているのでキャッシュ処理に関する問題はないはずですが…。

 

大量のスパムコメントがあった理由は…

4月までの数年間WordPressの更新は「緊急性の高いものを除きとりあえずセキュリティアップデートだけ」という状態だったので、一部の事態に鈍感になっていたことが大きかったです。で、本題の大量のスパムコメントですが、とりあえずコメントのフィルタシステムでスパム扱いされていたので半分無視していたのですが、妙に気になったのでCaptchaに関して調べてみると・・・。

SI Captcha Anti-SpamCaptchaは問題あり

という内容の記事があり、調べてみると開発者が変わって更新が止まっているとかバックドアが仕込まれているとかいろいろと情報がありました。ちなみに私が二年前の段階で導入していたのがSI Captcha Anti-Spamの方で、どうもこれがスパムコメントの原因だったようです。

 

別の種類のCaptchaを導入してみることに

さすがにCaptchaが一つもない、というのは微妙ということで調べてみていくつか導入候補を考えたのですが、それが

  • Google Captcha (reCAPTCHA)
  • Login No Captcha reCAPTCHA
  • Advanced noCaptcha & invisible Captcha

になりました。ところがどっこい。基本的にこれらはすべてGoogleのCaptchaシステムを借りて行うものなので変に所有者が変わらない限りはCaptchaとしての動きはほぼ変わりません。後は信頼度と設定がどこまでできるか?に依存するため、今回はAdvanced noCaptcha & invisible Captchaを使ってみることにしました。

 

導入の手順はおそらくこの3つともほとんど変わらない(と思う)

というのも、結局はGoogleのreCAPTCHAを借りることになるため、

  1. WordPressの管理画面からプラグインを検索してインストールする
  2. GoogleのreCAPTCHAの管理画面にGoogleアカウントでログインしてAPIキーとSecretキーを取得する
  3. 設定画面でAPIキーとSecretキーを登録して、ほか必要な設定を行う

となります。GoogleからAPIキーを取得するためのアドレスは大抵プラグインの設定画面にリンクが張られていると思うのでそれを参照すればよいと思います。ちょっと注意が必要なのが、reCAPTCHAのキーを取得するときに認証のバージョンなどを設定することができますが、そのバージョンにプラグイン側の設定も合わせる必要がある、ということです。v2(noCaptchaもしくはinvisible Captcha)で設定する場合とv3で設定する場合でそれぞれ方法が異なりますので注意しましょう。

あとはどの場所にCaptchaを設置するかどうかです。Advanced noCaptcha & invisible Captchaではいくつかの場所にチェックボックスだけで設定できるので、コメント投稿およびログイン画面に設定することにしました。これで安全性がかなり高まるかと思います。ちなみにAdvanced noCaptcha & invisible Captchaの場合はbbPressやBuddyPress、WooCommerceとも連動させてCaptchaをかけることができるので、これらのプラグインでサイトを作っている人には簡単に設定できるようになる分おすすめかもしれません。

 

Captchaのプラグインを変えるだけでスパムコメントが一切なくなった…

というわけで、Captchaのプラグインを変えて数日間たったわけですが、スパムコメントが一切なくなりました。まあ、このサイトこの2年間くらい記事更新がほとんどなかったこともありコメントらしいコメントもないので正しいコメントが受け付けられているかどうかはテストしたこと以上のことは不明なのですが、それでも管理の手間が大分減っているのでちょっと感動です。スパムコメントのフィルタリングに関してはIPフィルタ以上の効果ですね。

firewalldを使って特定の国からのアクセスを遮断する方法

Twitterにも書きましたが、ちょっととある国からのアクセスが非常にウザくなってきたのでIPフィルタを用いてアクセスを遮断することにしました。ちなみに、4月にサーバシステムを新しくしていますが、前のサーバにはiptablesに直接フィルタ情報を書き込むタイプのスクリプトを用意して処理していたのですが、サーバが新しくなったこともあり、IPフィルタ処理がfirewalldを通して行うことが通常となってしまいましたので、そちらの方法に合わせています。ほかのサイトでも紹介されていますが、半分は自分のメモ書きのようなものです。

 

というわけで作業手順

それほど難しいものではないですが、手順としては

  1. 対象の国に属するIPのリストを取得する
  2. firewalldにIPリストを登録する
  3. firewalldの処理を有効にする

これだけです。まあ、最後にcronなどで処理を行うためのスクリプトを用意しますのでそちらをそのまま使えばOKかもしれません。

 

1.対象の国に属するIPのリストを取得する

本当ならばftpなどでリストをとってくる方法が正しいと思うのですが、この処理を組み込むために検索したときにIPリストを提示してくれるサイトとしてこちら(https://ipv4.fetus.jp/)がありましたので、私の例でもこちらを使うことにします。なお、この部分にIPリストを提示する別のサイトを使っても問題はないと思います。

このサイトではそれぞれの国に割り当てられたIPのリストについて国名(もしくは国コード)をクリックするとこのサイトからダウンロードできるアドレスが取得できます。形式もhtaccess形式やiptables形式、postfix形式など主要なサービスに対してかけられるようになっていて便利です。

ただし、firewalldでは普通にIPがずらっと書いてあるだけの形式を使うので「プレインテキスト」形式で問題ありません。対象の国のプレインテキスト形式をダウンロードできるURLを確認しておきましょう。

 

2.firewalldにIPリストを登録する

正しくはfirewalldではなくipsetという別のものを使うのですが…。ない場合はパッケージをインストールしておきましょう。私の場合はCentOS系なので

$ sudo yum install ipset

でインストールしておきましょう。

これがfirewalldに複数のIPを一括で処理させるもので、これをインストールすればこんな感じのことができます。よくスパムが来るのはこのサイトの場合はロシアやウクライナなので、ウクライナを対象にした場合はこんな感じでかけます。すでにIPリストを/tmp/ua.txtに保存してあるとすると…

$ sudo firewall-cmd --permanent --new-ipset=uk --type=hash:net
$ sudo firewall-cmd --permanent --ipset=uk --add-entries-from-file=/tmp/uk.txt

とすれば登録が完了します。

 

3.firewalldの処理を有効にする

登録が完了すれば、firewall-cmdでチェックが可能になり、

$ sudo firewall-cmd --zone=drop --list-all

というコマンドで確認すると

drop (active)
  target: DROP
  …
  sources: ipset:uk
  …

のように登録が確認できると思います。あとは

$ sudo firewall-cmd --reload

で再読込をすれば完了です。

 

で、定期的に更新するスクリプトは…

IPリストは不定期に変わりますので適当な間隔で更新していかないと思った国のIPをはじけなくなってしまいます。今のところ私はこんなスクリプトを作成しています。

#!/bin/sh

# コマンドの場所の設定
CURL = /usr/bin/curl
FIREWALL_CMD = /bin/firewall-cmd

# IPリストをダウンロード
$CURL "https://ipv4.fetus.jp/ua.txt" > /tmp/ua.txt

# firewalldに状態を設定する
$FIREWALL_CMD --permanent --delete-ipset=ua
$FIREWALL_CMD --permanent --new-ipset=ua --type=hash:net
$FIREWALL_CMD --permanent --ipset=ua --add-entries-from-file=/tmp/ua.txt
$FIREWALL_CMD --reload

# 使ったファイルを片付ける
rm /tmp/ua.txt

curlやfirewall-cmdの場所は環境によって異なると思いますので環境に合わせて変更すればOKですし、国が異なる場合はreloadの前に指定したい国の数だけ処理を追加すればOKです。最後にこれをcronで定期的に動くように設定すれば完了となります。

 

個人的には特定の国からのアクセスをはじく、とかはやりたくないが…

ちょっと目に余るアクセスがありましたのでそれに対応したものです。スパムというのも面倒なものですし、対応も面倒だな~と思いながらやっています。

WindowsのDPIスケーリング処理について一考察

WindowModePatchで実装するのに大分困っていたDPIスケーリング処理(DPIAwareness)についてメモ書き代わりに処理について書いておきます。ちなみに、英語の読み間違いを防ぐためにいっておくと、日本語的には「DPIによるスケーリング(解像度変更)処理」というべきなのでしょうが、英語では「DPIAwareness」となっており、Awarenessの意味は「気がつく」「知る」なので、直訳すると「DPIについてプログラム側が気がついているかどうかを処理する」という意味になってしまいます。これが私が処理について誤解した原因なのですが…。

 

DPIスケーリングを指定する3つのモード

はい、まずは私が翻訳をミスったポイントです。簡単に調べてみるとほかの日本語サイトではちょっと解説してあったのでなんともいえないのですが。基本的にはプログラム側が指定できるDPIスケーリングのモードは3つあります。それが

  1. DPI処理について関知しない(Unaware)
  2. DPI処理をアプリケーション初期化時にのみ行い、後はシステムに任せる(System aware)
  3. DPI処理を完全にアプリケーション側で行う。途中で(ウィンドウの位置移動によって表示を行うモニタが変更されるといった原因で)変更される場合にはメッセージを送るので、そのメッセージを確認してアプリケーション側が処理を行う(Per Monitor DPI aware)

となります。ちょっと注意してほしいのが1.のUnawareパターンですが、これはつまり「アプリケーション側は何もやらなくてもシステム側(すなわちOS側)が勝手にやるので気にしなくてもいいよ」という意味です。プログラム側からこれを設定すると表示上は勝手にスケーリングしてしまうのでWindowModePatchでは非常に都合が悪いモードになっています。通常のアプリケーションでは面倒であれば1.のパターンを使うのもありだと思います。まあ、今時のゲームであれば解像度調整機能は必須なので1.のUnawareを使うことはまずないと思います。

 

DPIスケーリング処理は通常はマニフェスト側で設定する

Microsoft側としてはこちらを想定しているようです。というのも、この後愚痴のように出てきますがアプリケーションが起動してからだとスケーリングモードの変更がプロセス単位でできないことが多いのです。(これについては推察になりますが、おそらくWindowModePatchと同じように座標を設定/取得する関数の処理をスケーリングに合わせて変更する、というものが割り込むことが「想定されるのか否か」によって初期化の方法が変わるのために難しいのではないか?というものです。)まあ、通常のアプリケーションならマニフェスト指定が簡単でしょうね。

やり方としては

  1. VisualStudioなどのビルドツールにおいてマニフェストの設定で「DPIの認識」を設定する
  2. マニフェスト内のwindowSettingsセクション内にdpiAware値およびdpiAwareness値を設定する

というものです。前者の方が勝手に作ってくれる分簡単ですね。なお、マニフェストにDPIに関する設定がないときはシステム側が勝手にスケーリングをするDPIUnaware状態となりますので注意してください。

 

アプリケーション側からの設定はOSごとに方法が異なる

まず紹介されている方法として自身のプロセス全体にDPIに関する設定をかける方法。MSDNでは

  • SetProcessDPIAware関数(Vista~Win8.0)
  • SetProcessDPIAwareness関数(Win8.1~Win10 1607以前)
  • SetProcessDpiAwarenessContext関数(Win10 1607以降)

によりできる、と書いてあるので、該当するフラグをつかって設定すれば良さそうなのですが、私がデバッグした限りにおいてこれらがうまく設定できた試しがない、というのが悲しいところでした。書いてある注意点としては「ウィンドウが作成される前にこの関数を使って設定するように」と書いてあるはずなのですが、やってみてもすべて「Access Denied」(すでにDPIのモードが設定されているため設定不可能)ではねられて終わりでした。なぜなんだよ~と思いながら書いています。

というわけで仕方がないので…。Win8.1以前では対応策がこれ以上ないようですが、Win10以降であればスレッド単位、もしくはウィンドウ単位でDPIAwarenessが設定できるのでそちらを使います。スレッド単位の場合はCreateWindowEx関数などウィンドウ作成系の関数の前に呼び出せばモード変更が可能なようです。使う関数は

  • GetThreadDpiAwarenessContext関数
  • SetThreadDpiAwarenessContext関数

になります。一応Get側で取得して予定しているモード値と異なっているときSet側で設定する、という動きになります。Win10以外のOSにも対応する場合はuser32.dllにインポートされているのでたいていの場合GetModuleHandle関数からGetProcAddress関数で取得することになります。

面倒なのはこれらで使うDPI_AWARENESS_CONTEXTという値です。

というのは、WindowsSDKのバージョンの関係で自分で定義する場合が多いこととこれら2つの関数が返すDPI_AWARENESS_CONTEXTの値はMSDNで定義されている各値と同じものを返さない場合がある、ということです。前者についてはDPI_AWARENESS_CONTEXTは列挙子ではなくハンドル扱い、と書かれていますのでUINT_PTR型などとしないとGetProcAddress関数で処理を持ってきたときに見事な失敗となりますし、後者はマニフェストや(この後紹介する)実行ファイルのの互換性設定でDPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2相当の値を設定してあるからといってGetThreadDpiAwarenessContext関数はMSDNでDPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2として定義されている値である(-4)ではなく別の値を返すパターンがあるということです。これが判定にはやっかいすぎる現象だと思います。設定する場合の値はMSDNの値が正しいようですが…。

なお、WindowModePatchでは前者はちゃんとハンドルとして定義する構文を借りて値を再定義していますし、後者についてはGetAwarenessFromDpiAwarenessContext関数を一度通してDPI_AWARENESS列挙子にすることでモードを判定するようにしています。この辺も実装に手間取った理由だったりします。

 

あとは必要に応じてDPI変更時の処理を組み込むだけ

DPIを変更する必要がある場合はWM_DPICHANGEDメッセージがウィンドウプロシージャに飛んできますのでこれを見てウィンドウの大きさや文字の大きさなどを制御します。このあたりは今回の考察範囲からは外れますので詳しく書きません。

 

使用者側から見るのであれば実行ファイルのプロパティから設定可能

Win10系であればこれが可能です。Win8.1系でもできるかな?やり方としては対象アプリケーションの実行ファイルのプロパティを開き「互換性」タブから「高DPI設定の変更」を選択して誰がスケーリングをするのか設定が可能です。ちなみにこれを使うと単にウィンドウモード時の解像度が低いだけなら無理矢理解像度を引き上げることが可能だったりします。メニューバーまで拡大されてしまうのはご愛敬といったところですか。WindowModePatchのような自由度はないですが、OSの機能ですのでうまく使えるのであれば損はないかな~と思ったりもしています。

 

ゲームの場合は直接解像度を見るので気にしない方がよい機能かな

と思います。といっても、名前の入力などでダイアログを出す必要がある場合は対応する必要もあるかもしれません、とだけ付け加えておきましょう。

WindowModePatch 0.71 Alphaを公開

過去の記録を見てみると大体1年半ぶりの更新なんですね…。ちょっと感慨深いものが。とりあえずまだ生きていますよ~という意味とビルド環境の変化に対応するための試しという意味がかなり大きいバージョンとなっています。

 

開発環境をVisualStudio2017へと移行したためにいろいろと不具合が…

今回の大きな変化がこれです。一つ前まではVisualStudio2010だったのですが、今回は最新一つ手前のVisualStudio2017でビルドを行っています。というのも、昨年の段階でPCを組み直したときに環境をまっさらにした関係でVisualStudio2010の開発環境まで完全に吹っ飛んでしまったのが大きかったのと、その後開発環境を戻す余裕がなくなったためにどうせならとVisualStudio2017を購入したためにさらに開発環境の再構築に時間がかかってしまったのが大きかったです。実際にプロジェクトの更新だけではなくソースコードの漢字コードを変換していったり、ランタイムライブラリの動作が変更されているものがあったために対応する処理を書いたりするなどがあったためだったりします。

まあ、副産物としてWindowModePatchの設定ファイルの漢字コードをUTF-8に変更しております。もちろん、旧の設定ファイルであるShiftJIS形式でも読み込めますし、UNICODE形式で保存されても読み込めるようになっています。この「複数文字コードに対応したテキストファイルの読み込み処理」が作られたのがちょっと大きいです。こういうフリーソフト側に使うだけではなく仕事で作成するツール類にも使えるのが非常に大きかったりします。

 

一応DPIのスケーリング処理に「暫定的に」対応

といってもやっていることは「スケーリング処理をアプリケーション側で行うように見せかけているだけ」だったりします。実際、WindowModePatchは解像度変換機能がメインなので逆に現在のモニタの解像度に合わせた設定ができないと微妙に困るからですね。Win10の初期段階のものだと設定を正しくしないとマニフェストファイルにスケーリング処理対応の方法が書かれていない場合にWinAPI側で勝手にスケーリングされて設定の数字が誤ってしまう、という現象もありましたので。

一応この処理はWin10の1703(Creators Update)以降であれば(WindowModePatchが対応できれば)対応処理が入ると思います。問題はそれより古いバージョンでの動作。一応対応コードは書いておいたのでWin8.1以降であれば動いて「ほしい」コードなのですが、手元でやったいる段階では動かないことが多かったです。これについてはDPIAwarenessと呼ばれる部分の処理の説明をもう少し読み込んだ方がよいかもしれません。実際、はじめに思っていた設定値の役割が完全に逆になっていたことに気がついたのはかなり後だったりしますので。

 

対応したソフトが増えていないだろうとは思う

頑張ってDirectX5時代のDirect3Dに対応しようとした形跡が見えるコードは今のところ封印しておりますし、やりたいことも増えてきているながらやらなければならないことも増えてきていますので…。頑張って増やしていければな~と思いながら。

Fedora30をインストールしてみた

Fedora28をインストールしてから1年たっていませんが、まあそれはそれとして。すでにFedora30がリリースされ、Fedora28のサポートも終了することが決まるタイミングですのでさっさとインストールをやってみようということでやってみました。

といっても、コツさえ知っていればServer版なのでFedora28だろうがFedora30だろうがあまり変わらずにインストールできます。今回はその中でもちょっと思ったことを少しだけ書いていきたいと思います。

 

Btrfsを使おうとすると、ブートパーティションが面倒になるのでは?

まず一つ目。毎度のことながら、dnfによるバージョン更新処理は絶対にしない、というのが私のFedoraを扱う上での一種の信念ですので完全な新規インストールです。ところで、新規インストールするのであればパーティションも新しくなるのでは?というところなのですが、実際はそんなことはみじんもありませんでした。

なぜかというと、Fedoraといえば今ではBtrfsも十分な完成度になったと思うのでそちらを使おうかと思ったのですが、まずはじめにUEFIとGPTの影響で特殊ななブートパーティションが必要になるということ。もう一つは/bootの領域に対してBtrfsを使うことができないので、その領域だけ別の形式のパーティションを当てる必要がある、という点です。こうなってしまうと最低でも

  • UEFIブート用パーティション
  • /boot用データ領域
  • Btrfsの通常データ領域
  • スワップパーティション(これは任意か?)

の4つのパーティションが必要となるわけで、個人用とであれば下手をすればすべて/に統合してもよいはず、と考えると4つもあるのは逆に面倒という事態になりかねないな~というのが正直なところでした。なので今回も結局Btrfsを使わずにext4形式になっています。もう少し規模が大きいとか実験用サーバとかならそれもありなのかもしれませんが。

 

WOLの設定はフラグが立っていても起動するごとにethtoolを動かさないとだめなのね

という事実に今更ながら気がついた人でした。以前の記事にも書いたような気がしますが、まずマザーボードに実装されているLANデバイスは、特にゲーミングマザーだとWOLの機能が必要ないと見なされているらしく、WOLができない状態でした。なので、内蔵のLANを無効化した上で外付けのLANカードを使って通信するようにしています。

そしてWOLの設定でもう一つ気をつけなければならないことは、ドライバにもよるようですが、基本的にethtoolを毎回動かしてシャットダウンを行う前までにWOLを受け付けるように設定しないとだめ、ということでした。これがちょっと面倒だったですね。いくつかのサイトを回ってみるとudevを使ったパターンが紹介されていましたが、私の場合はudevを使ったパターンではうまく動かなかったです。ちょっと不思議。

というわけで別パターンとして載っていたcronを使う方法を使わせてもらいました。これはcronにはcrontabに書くときにいくつかのフラグがあり、その中の一つである@rebootというものを使うと起動が完了したタイミング(正しくは起動後cronが動かされたタイミング)で指定したコマンドを実行する、というものです。今回はこれを使って

@reboot /usr/sbin/ethtool -s [デバイス名] wol g

という命令をcrontabに書き込むことで、起動時に実行させることができるようになり、WOLの機能が回復した、ということになりました。ちなみにWOL機能設定を自動的に行うようにしているいくつかのサイトでethtoolの位置が/usr/bin/ethtoolとなっているものがいくつかあるようですので、whichコマンドでethtoolの場所を確認した上で書いておかないと正しく動いてくれませんので気をつけましょう。

 

少しネットワークの受付が遅くなった?

といっても、対象はTelnetとSambaですので、なんともいえないところです。両者ともログイン前のID入力にいくまでに妙な時間がかかっている印象です。一応IPv6の影響で何度か試しているのかな~とも思ったのですがそうでもないようで。なぜなのでしょうか…。ちなみにTelnetを使っているのはNATがあるので外部からTelnetではログインできませんし、やりとりもハブ1個分しかない内部ネットワークだけのものですので簡単な方がよいだろうということです。そこもSSHにするのが正しいのはわかっているのですが…。