シン・クラウド for FreeのアクセスログをPythonで解析する方法

シン・クラウド for Freeでアクセスログが見れるようになったので
Python(JupiterNotebook)を使って解析してみました。
アクセスログについてはこちらで説明されています。
https://www.xfree.ne.jp/manual/man_server_log.php
実施してみて危なそうなログが見つかったので
対策(XML-RPCを無効、ユーザー名非表示)しました。
アクセスログを見たほうがいいなと思ったきっかけは以下のことがあったからです。

アクセスログ解析(自己流)

アクセスログをPythonのpandasでデータフレーム(表)にします。
JupiterNotebookで実施しました。
解析プログラム(以下のコード)と同じところにアクセスログを持ってきます。
以下の解析プログラムを実行。

# アクセスログをDataFrameとして読み込む
# sep (オプション): 列を区切るための文字を指定します。デフォルトはカンマですが、タブなど他の文字を使用する場合に指定します。設定値はBingに聞いたので意味は分かりませんがうまく動いてます。

import pandas as pd

log_file_path="人によって違う(私の場合kikuichigebkn.website).access_log"
df = pd.read_csv(log_file_path, sep=r'\s(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)(?![^\\[]*\\])', engine='python', na_values='-', header=None, usecols=[1, 4, 6, 7, 9, 10], names=['ip', 'time', 'request', 'status', 'Referrer', 'user_agent'])
df

アクセスログをデータフレーム化することができました。
次に表示する条件を絞ります。

#表示しないでいいipをvalues_listに書きます。
# 自分ip123.456.7.224
# シンクラウド・フリーの自分のip987.65.432.15

#表示しないでいい条件を& ~で追加します。
#& ~df['項目名'].str.contains('ここに書いた文字列を含む行は表示しなくなります')
#& ~ではなく&で追加すれば表示したい条件を指定できます。
例& df['status'].str.contains('200') を追加すればステータスが200のものだけ抽出できます。
values_list=['123.456.7.224','987.65.432.15']
df['status'] = df['status'].astype(str)#数字はエラーになるので文字列に変換
filtered_df = df[~df['ip'].isin(values_list) 
& ~df['request'].str.contains('GET /wp-content/')
& ~df['request'].str.contains('GET /wp-includes/')
& ~df['status'].str.contains('404')]
print(len(filtered_df))
filtered_df

除外したのは
普通に閲覧してるときにrequestに残る’GET /wp-content/”GET /wp-includes/’
またステータス404は存在しない=つながらない=悪いことはできないという解釈です。
また以下は調べる限りアクセスされてもいいものではないかと思います。
GET /robots.txt 、GET /feed/(下記参照)、GET /ads.txt 、GET /wp-sitemap.xml、GET / HTTP/(GET /だけだとその下も除外されるのでhttp/も追加) 、通常のurl。
数が多い場合、まずはstatusが200(リクエストが成功)と
300番台(リダイレクト)で絞り込んだ方がいいかもしれません。

実行結果

データフレームが見づらいときは以下を実行後、
もう一度データフレームを作ると見やすくなります。
#書く要素の表示文字数を以下で調整できます。30文字
の場合
pd.set_option('display.max_colwidth', 30)
# 行を省略せずに表示したいとき
pd.set_option('display.max_rows', None)
目次へ

サイト一覧を取得する方法

自分の各記事にアクセスしたものを除外する条件を追加する方法です。
Pythonで処理すると特殊文字(\,&など)の置換が面倒なので
メモ帳とPythonのライブラリpyperclipを使います。
まず自分のURL/wp-sitemap.xmlにアクセスするとサイトマップのリンクがあるので
https://kikuichigebkn.website/wp-sitemap.xml
そこのリンクをクリックするとXML サイトマップが出てきます。
これをメモ帳に貼り付けて以下の置換(すべて置換)を実施。

自分のサイトのURL→& ~df['request'].str.contains('GET 
例https://kikuichigebkn.website/→& ~df['request'].str.contains('GET 
注意:最後のGETの後ろに半角スペースが入っているのを忘れないように

すべてコピー→JupiterNotebookで以下実行
str1='''ここに貼り付ける'''
str2=str1.replace('\n',"')\n")
import pyperclip

pyperclip.copy(str2)
これを上の除外する文字列のコードに貼り付ければOKです。
注意:SyntaxError: unterminated string literalの場合は1番最後に')を追加
目次へ

XML-RPCを無効にしてみる

アクセスログを見ると以下が連続して約100回あります。
"POST /xmlrpc.php HTTP/1.1"	200
調べると対策した方がよさそうなのでやってみました。

XML-RPCを無効にして正常な機能に影響はないのかと
.htaccessを書き換えると
サイト動かなくなる可能性もあるので自己責任で実行します。

参考:https://wordpressdeec.jp/howto/howto-040/
xmlrpc.phpが有効かどうか、自分のサイトのURL/xmlrpc.phpで確認できるということなので
https://kikuichigebkn.website/xmlrpc.phpにアクセスすると
XML-RPC server accepts POST requests only.と表示されました。
(今は無効にしたので403Forbiddenが出ます。)
これはxmlrpc.phpが有効な状態なようです。
.htaccessを書き換えて無効にしてみます。
こちらにシン・クラウド for Free.htaccessの編集の仕方が書いてあるのでやってみました。
https://www.xfree.ne.jp/manual/man_server_htaccess.php

略
# END WordPress
# xmlrpc.phpへのアクセスを無効化する
<Files xmlrpc.php>
Order Allow,Deny
Deny from all
</Files>
#BEGIN COCOON HTACCESS
略

設定後、https://kikuichigebkn.website/xmlrpc.phpにアクセスすると
403Forbiddenが出るようになったのでxmlrpc.phpは無効になったと思います。
サイト全体の動作は今のところ正常に動いてます。

シン・クラウド for FreeのサーバーパネルのWordPressセキュリティ設定に
XML-RPC API アクセス制限があってONにしてるのですが
国外IPアクセス制限で国内は制限されないのかも。
実際アクセス成功してるIPを調べたら所在地はtokyoだった。
目次へ

ユーザー名を見えなくした

WordPressの変なところにアクセスしてる
GET /wp-json/wp/v2/users HTTP/1.1
ここに実際にアクセスするとユーザー名が分かってしまう。
このあとのログが
"GET /wp-login.php HTTP/1.1"
"POST /wp-login.php HTTP/1.1" 200が11回
"POST /wp-login.php HTTP/1.1" 403が100回続いていた。
これはユーザー名でログインしようとして、
あってずっぽうにパスワードをPOSTしていると思われる。
staus200から403になったのは連続してログインすると
ログイン試行回数制限(サーバーパネルのWordPressセキュリティ設定ON)で
ログイン画面につながらなくなるようになってるのかもしれない。
多分、そのipで中には入られていないようなので大丈夫だろう。

もう少し実験した。
自分で/wp-login.phpに間違ったパスワードでPOST(ログイン)するとレスポンスは200
つまり「パスワードが間違っています」の画面が返ってきて、
その通信がうまくいったことを意味すると思われる。
正常なパスワードでログインするとレスポンスは200ではなく302が返ってくる。
302リダイレクトとは、要求されたWebページが一時的に別の場所にあることを意味し、
ユーザーを自動的に移動先に転送するらしいのでログインされたわけではなさそうです。
自分でログインしたときのログ
POST /wp-login.php HTTP/2.0″ 302
GET /wp-admin/ HTTP/2.0″ 200
なので、このパターンで自分以外のIPのログが残っていたらログインされているかも。

脱線しましたが、
こちらに書いてある/?author=1でもユーザー名が分かってしまったので 2つ対策しました。
参考:【WordPress】必ずやるべきセキュリティ対策!ログインユーザー名の特定を防ぐ
function.phpの編集はこちらを参考にしてできました。
参考:WordPress functions.phpとは?意味や書き方・記載場所
対策結果  
https://kikuichigebkn.website/wp-json/wp/v2/users ユーザー名が表示されないようになりました。
https://kikuichigebkn.website/?author=1 対策前は本文にユーザー名が表示されていました。
しかし本文には表示されないのですが、開発者ツールのネットワークでみたらリダイレクト先にユーザー名が載っていたので、こちら↓の方法に変更しました。
参考:https://www.doe.co.jp/hp-tips/wordpress-login-id-guard/

さらにwp-login.phpを.htaccessでIP制限しました。
自分のIPがプロバイダ都合で変更されるかもしれないので、
IP制限したことを忘れないようにしましょう。

参考:https://kingsite.jp/blog/0174#11

また、こちらの対策も追加しました。
参考:WordPress REST API によるユーザー情報を隠す – Xakuro
目次へ

OPTIONSで何かわかるのか?

以下のように画像にOPTIONSメソッドでアクセスしている。
"OPTIONS /wp-content/uploads/2023/10/girlsband_bing-259x180.jpg HTTP/1.1" 200

ブラウザではHTTPのメソッドを自由に指定できないのでPowerShellで以下のようにするとOPTIONSメソッドでアクセスできます。
 Invoke-WebRequest -Method OPTIONS https://kikuichigebkn.website/wp-content/uploads/2023/10/girlsband_bing-259x180.jpg

結果(特に危険な感じはしない)
StatusCode        : 200
StatusDescription : OK
Content           : {}
RawContent        : HTTP/1.1 200 OK
                    Connection: keep-alive
                    Allow: GET,POST,OPTIONS,HEAD
                    Accept-Ranges: bytes
                    Content-Length: 0
                    Cache-Control: max-age=31536000, public
                    Content-Type: image/jpeg
                    Date: Fri, 09 Feb 202...
Headers           : {[Connection, keep-alive], [Allow, GET,POST,OPTIONS,HEAD], [Accept-Ranges, bytes], [Content-Length,
                     0]...}
RawContentLength  : 0

OPTIONSはCORSのプリフライトリクエストで使われるが、
CORSの関係で何かしようとしているのか?よくわからないです。
目次へ

feed

“GET /feed/ HTTP/1.1″は以下が参考になりました。
参考:https://otogeworks.com/blog/wordpress-rss-should-not-disabled/
こちらのユーザー名を隠すだけ実行しました。
またRSSリーダーで検索して調べると
「フィードの各投稿に含める内容」も抜粋にしたほうが
よさそうなので抜粋にした。

アップロードMAXサイズを変更

ワードプレス管理画面のメディア→「新しいメディアファイルを追加」で
最大アップロードサイズ: 1Gになっていたので小さくしました。
取り合えず32Mに設定。
サーバーパネル→PHP→php.ini設定→詳細設定で
post_max_sizeを1Gから32M
upload_max_filesizeを1Gから32Mに変更
→設定する

怪しいアクセス

URLパラメータで何かしてる?
GET /?m=im&a=status HTTP/1.1
GET /?u=aHR略NvbS8=&p=Lw== HTTP/1.1

これは何をしているのかよくわかりませんでした。
この302はどこへいくのか?
GET /wp-login.php HTTP/1.1" 302

正常にログインするときはPOSTして302になります
POST /wp-login.php HTTP/2.0" 302
パスワードが間違っていたら
POST /wp-login.php HTTP/2.0" 200(パスワードが間違っているという画面が返ってくる)
GET /wp-login.phpは普通は200でログイン画面が表示されるのですが、
どうやったら302になって、そしてリダイレクト先はどこなのか気になります。

まとめ

取りあえず2つ対策できたけど、アクセスログを見ると色々怪しいのがあるので、
分かったらこの記事に追加していきます。
目次へ

イチゲをOFUSEで応援する(御質問でもOKです)Vプリカでのお支払いがおすすめです。
MENTAやってます(ichige)

タイトルとURLをコピーしました