R言語で自分のブログへのログイン履歴を分析してみました

こんにちはー!ニアです。

このブログではWordPressのセキュリティ対策として、ログイン履歴を残すためにプラグイン「Crazy Bone(狂骨)(→Github)」を入れており、先日はADO.NETとLINQを使って、ログインエラー時のユーザー名とパスワードを取り出した記事をQiitaに書きました。

[C#] ADO.NETとLINQを使って、Crazy Bone(狂骨)で記録したWordPressへのログイン履歴を分析してみよう - Qiita
こんにちはー、ニアです。今回はADO.NETとLINQを使って、MySQLからCSVファイルにエクスポートしたWordPressへのログイン履歴を分析したお話です。1. 開設から1年も経っていな…

今回はデータ解析に特化したR言語を使って、MySQLデータベースからエクスポートしたCSVファイルを読み込み、分析してみました。

1. 実行環境

今回はR for Windows 3.2.2を使用しました。また、使用したパッケージは以下の2つです。

  • data.table
  • stringr

2. R言語を使って、ログイン履歴を分析

2.1. CSSファイルを読み込み、ログインエラーのレコードを抽出

まずは、fread関数を使ってCSVファイルを読み込みます。先頭行はフィールド名なので、headerをTRUEに、encodingはUTF-8を指定します。

次にactivity_statusフィールドをキーに条件式を使って、値が「login_error」であるレコードを抽出します。

# wp_user_login_log.csvを読み込みます。
linlog <- fread( "wp_user_login_log.csv", header = TRUE, encoding = "UTF-8" )

# activity_statusフィールドの値がlogin_errorのレコードを抽出します。
linerr <- linlog[linlog$activity_status == "login_error"]

これで、ログインエラー時のdata.tableオブジェクトができました。

2.2. ユーザー名とパスワードを取り出す。

ログインエラー時のユーザー名とパスワードはactivity_errorsフィールドに格納されていますが、値がPHPのシリアライズ形式なので、正規表現を使って取り出します。

a:3:{
	s:6:"errors";
	a:1:{
		s:16:"invalid_username";
		a:1:{
			i:0;s:177:"<strong>エラー: 無効なユーザー名です。 <a href="[ブログのアドレス]/wp-login.php?action=lostpassword">パスワードをお忘れですか ?</a>";
		}
	}
	s:10:"user_login";
	s:5:"admin";
	s:13:"user_password";
	s:3:"123";
}

a:3:{
	s:6:"errors";
	a:1:{
		s:16:"invalid_username";
		a:1:{
			i:0;s:177:"<strong>エラー: 無効なユーザー名です。 <a href="[ブログのアドレス]/wp-login.php?action=lostpassword">パスワードをお忘れですか ?</a>";
		}
	}
	s:10:"user_login";
	s:8:"wpengine";
	s:13:"user_password";
	s:8:"password";
}

まずは「s:10\:””user_login””;s\:\d*\:””.*?””; 」及び「s:10\:””user_login””;s\:\d*\:””.*?””;」のパターンとstr_match関数を使って、activity_errorsフィールドの値から「s:10:”user_login”;s:\d:”ユーザー名”;」及び「s:13:”user_password”;s:\d:”パスワード”;」の形で取り出します。

次に「s:10\:””user_login””;s\:\d*\:””|””; 」及び「s:10\:””user_login””;s\:\d*\:””|””;」のパターンとgsub関数を使って、余分な文字列「s:10:”user_login”;s:\d:”」や「s:13:”user_password”;s:\d:”」、「”;」を取り除き、ユーザー名及びパスワードを取り出します。

# ログインエラー時のユーザー名及びパスワードを取り出すための正規表現です。
usrrep1 <- c( "s:10\\:\"\"user_login\"\";s\\:\\d*\\:\"\".*?\"\";" )
usrrep2 <- c( "s:10\\:\"\"user_login\"\";s\\:\\d*\\:\"\"|\"\";" )
passrep1 <- c( "s:13\\:\"\"user_password\"\";s\\:\\d*\\:\"\".*?\"\";" )
passrep2 <- c( "s:13\\:\"\"user_password\"\";s\\:\\d*\\:\"\"|\"\";" )

# ログインエラー時のユーザー名及びパスワードを取り出します。
errusr <- gsub( usrrep2, "", str_match( linerr$activity_errors, usrrep1 ) )
errpass <- gsub( passrep2, "", str_match( linerr$activity_errors, passrep1 ) )

そして、ユーザー名とパスワードのベクトルからdata.tableを作成します。

# ユーザー名とパスワードのベクトルからdata.tableを作成します。
errtable <- data.table( data.frame( User = errusr, Pass = errpass ) )

print( "自分のブログへのログインエラーの概要" )
print( summary( errtable ) )

試しにsummary関数を使ってみると、ログインエラー時のユーザー名とパスワードそれぞれの試行回数(TOP5)が表示されます。

wperr

作成したdata.tableからユーザ名、パスワードでそれぞれグループ化し、.Nで試行回数をカウントします。さらにorder関数で試行回数の多い順にソートします。

# ユーザー名、パスワードでグループ化し、試行回数をカウント。さらに試行回数が多い順にソートします。
errusrs <- errtable[ , .N, by = User ][ order( -N ) ]
errpasses <- errtable[ , .N, by = Pass ][ order( -N ) ]

print( errusrs )
print( errpasses )

◆ ソースファイル

library( data.table )
library( stringr )

# wp_user_login_log.csvを読み込みます。
linlog <- fread( "wp_user_login_log.csv", header = TRUE, encoding = "UTF-8" )
#setkey( linlog, activity_status, activity_errors )

# activity_statusフィールドの値がlogin_errorのレコードを抽出します。
linerr <- linlog[linlog$activity_status == "login_error"]

# ログインエラー時のユーザー名及びパスワードを取り出すための正規表現です。
usrrep1 <- c( "s:10\\:\"\"user_login\"\";s\\:\\d*\\:\"\".*?\"\";" )
usrrep2 <- c( "s:10\\:\"\"user_login\"\";s\\:\\d*\\:\"\"|\"\";" )
passrep1 <- c( "s:13\\:\"\"user_password\"\";s\\:\\d*\\:\"\".*?\"\";" )
passrep2 <- c( "s:13\\:\"\"user_password\"\";s\\:\\d*\\:\"\"|\"\";" )

# ログインエラー時のユーザー名及びパスワードを取り出します。
errusr <- gsub( usrrep2, "", str_match( linerr$activity_errors, usrrep1 ) )
errpass <- gsub( passrep2, "", str_match( linerr$activity_errors, passrep1 ) )
# ユーザー名とパスワードのベクトルからdata.tableを作成します。
errtable <- data.table( data.frame( User = errusr, Pass = errpass ) )

#print( "自分のブログへのログインエラーの概要" )
#print( summary( errtable ) )

# ユーザー名、パスワードでグループ化し、試行回数をカウント。さらに試行回数が多い順にソートします。
errusrs <- errtable[ , .N, by = User ][ order( -N ) ]
errpasses <- errtable[ , .N, by = Pass ][ order( -N ) ]

print( errusrs )
print( errpasses )

◆ 実行結果

wperr2
Nia-TN-SDfs-normal2.png
よい子はユーザー名に「admin」を使っちゃダメだぞ~

3. おわりに

今回初めてR言語を使ってみました。data.tableパッケージを使うだけでCSVファイルをテーブル形式として読み込めるのがいいですね。

今度はグラフのプロットや簡単な画像処理をやってみようかな。

[END]

コメント

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