目的
弊社が運営しております https://www.saecanet.com/ と https://www.contact.am-consulting.co.jp/ の2つのWebサイトの2026年10月25日時点のWordPressコアファイル(ルート直下ファイル、wp-admin および wp-includes。以降同様)と同じく2026年10月25日に https://wordpress.org/ からダウンロードしました公式コアファイル(wordpress-7.1.3.zip)との整合性をRを利用して検査します。
コアファイルダウンロード時点の両WebサイトのWordPressバージョンが 7.1.3 であることは確認済みです。
なお、両Webサイトのルートに置かれていますGoogleサーチコンソール用HTMLファイル(google*******.html)は、いずれも google-site-verification: google*******.htmlと1行書かれているのみであることを確認しておりますので検査から除外しています。
また、Webサイトのセキュリティプラグインが設置していますPHPファイルが1つありますが、サイトにあるが公式に無いファイルの個別検査 を利用して eval、base64_decode、include / require は無いこと及び外部の URL を読み込んでいないことを確認しています。
同PHPファイルのファイル名には利用しておりますセキュリティプラグインの名称が含まれていますため、D:/wp-saecanet および D:/wp-contact から上述の検査後に削除して、以降の検査を実施しています。
さらに、弊社自身がWebサイト管理のために置いておりますファイル(PHPファイル×1、HTMLファイル×1。当然公式コアファイルには存在しません)もサイトにあるが公式に無いファイルの個別検査 を利用して不審な記述のパターンが無いことを確認した後に削除し、以降の検査を実施しています、
Rコード
全体検査
# 設定 ------------------------------------------------------------------
# 公式WordPressの展開先
dir_official <- "D:/wp-official/wordpress"
# 稼働中サイトのフォルダ(名前付きベクトル)
dir_sites <- c(
saecanet = "D:/wp-saecanet",
contact = "D:/wp-contact"
)
# ルート直下のファイルに加えて比較するコアディレクトリ
core_dirs <- c("wp-admin", "wp-includes")
# 検査から除外するルート直下のファイル名(正規表現)
# Googleサーチコンソールの所有権確認用ファイル(google + 英数字 + .html)
pattern_exclude <- "^google[0-9a-z]+\\.html$"
# 関数定義 --------------------------------------------------------------
# コアファイルの相対パス一覧を取得する関数
get_core_files <- function(root_dir) {
# ルート直下のファイル(ディレクトリは除く、隠しファイルを含む)
root_all <- list.files(root_dir, all.files = TRUE, no.. = TRUE)
root_files <- root_all[!dir.exists(file.path(root_dir, root_all))]
# 検査対象外のファイルを除外
root_files <- root_files[!grepl(pattern_exclude, root_files)]
# wp-admin, wp-includes 以下の全ファイル(隠しファイルを含む)
sub_files <- lapply(core_dirs, \(d) {
p <- file.path(root_dir, d)
if (!dir.exists(p)) {
warning(p, " が存在しません")
return(character(0))
}
list.files(p, recursive = TRUE, all.files = TRUE, no.. = TRUE) |>
(\(x) file.path(d, x))()
}) |>
unlist()
sort(c(root_files, sub_files))
}
# 公式とサイトのコアファイルを比較する関数
compare_core <- function(dir_official, dir_site) {
files_official <- get_core_files(dir_official)
files_site <- get_core_files(dir_site)
# 両方に存在するファイルのSHA-256ハッシュを比較
files_common <- intersect(files_official, files_site)
hash_official <- tools::sha256sum(file.path(dir_official, files_common)) |>
unname()
hash_site <- tools::sha256sum(file.path(dir_site, files_common)) |>
unname()
# 読み込み失敗(NA)は差異ありとして扱う
is_same <- !is.na(hash_official) & !is.na(hash_site) &
hash_official == hash_site
list(
n_official = length(files_official),
n_site = length(files_site),
same = files_common[is_same],
different = files_common[!is_same],
only_official = setdiff(files_official, files_site),
only_site = setdiff(files_site, files_official)
)
}
# 結果を整理して表示する関数
print_result <- function(site_name, result) {
print_files <- function(files) {
if (length(files) == 0) {
cat(" (なし)\n")
} else {
cat(paste0(" ", files), sep = "\n")
}
}
cat("\n", strrep("=", 60), "\n", sep = "")
cat("サイト:", site_name, "\n")
cat(strrep("=", 60), "\n", sep = "")
cat("公式のコアファイル数 :", result$n_official, "\n")
cat("サイトのコアファイル数:", result$n_site, "\n")
cat("同一のファイル数 :", length(result$same), "\n")
cat("\n【差異のあるファイル】", length(result$different), "件\n")
print_files(result$different)
cat("\n【公式にあるがサイトに無いファイル】", length(result$only_official), "件\n")
print_files(result$only_official)
cat("\n【サイトにあるが公式に無いファイル】", length(result$only_site), "件\n")
print_files(result$only_site)
}
# 実行 ------------------------------------------------------------------
results <- lapply(dir_sites, \(d) compare_core(dir_official, d))
for (nm in names(results)) {
print_result(nm, results[[nm]])
}
# 件数の一覧表
summary_table <- data.frame(
site = names(results),
same = sapply(results, \(r) length(r$same)),
different = sapply(results, \(r) length(r$different)),
only_official = sapply(results, \(r) length(r$only_official)),
only_site = sapply(results, \(r) length(r$only_site)),
row.names = NULL
)
cat("\n", strrep("=", 60), "\n【件数の一覧】\n", sep = "")
print(summary_table)
============================================================
サイト: saecanet
============================================================
公式のコアファイル数 : 3338
サイトのコアファイル数: 3341
同一のファイル数 : 3338
【差異のあるファイル】 0 件
(なし)
【公式にあるがサイトに無いファイル】 0 件
(なし)
【サイトにあるが公式に無いファイル】 3 件
.htaccess
wp-cli.local.yml
wp-config.php
============================================================
サイト: contact
============================================================
公式のコアファイル数 : 3338
サイトのコアファイル数: 3337
同一のファイル数 : 3335
【差異のあるファイル】 0 件
(なし)
【公式にあるがサイトに無いファイル】 3 件
license.txt
readme.html
wp-config-sample.php
【サイトにあるが公式に無いファイル】 2 件
.htaccess
wp-config.php
============================================================
【件数の一覧】
site same different only_official only_site
1 saecanet 3338 0 0 3
2 contact 3335 0 3 2サイトにあるが公式に無いファイルの個別検査
公式コアファイルに含まれていない wp-config.php 、.htaccess、wp-cli.local.yml はそれぞれ以下のコードで不審な記述のパターンの有無を確認します。
PHPファイル
以下のコードを利用して、不審な記述のパターンが無いことを確認済みです。
-
https?://を含む行は全てコメント行です。 - (注意) https://www.saecanet.com/のWordPressはホスティング事業者のクイックインストールを利用してインストールしています。インストール時に作成された
wp-config.phpにはコメント行ではありますが、sourceforge.jp の文字列があります。現在の公式日本語サポートはhttps://ja.wordpress.org/support/article/editing-wp-config-php/に移行されており、sourceforge.jpは2025年3月31日に閉鎖されておりますので、コメント行内のリンクには決して移動しないようにしてください。なお、https://www.contact.am-consulting.co.jp/はhttps://wordpress.org/からダウンロードしたzipファイル(英語版)を弊社自身で展開、アップロードして運営しています。
# 検査対象(サイト固有の確認ファイル)
files_check <- c("wp-config.php")
# 不審な記述のパターン
patterns_suspicious <- c(
"eval\\s*\\(",
"base64_decode",
"gzinflate",
"str_rot13",
"auto_prepend_file",
"auto_append_file",
"AddHandler",
"RewriteRule.*https?://",
"https?://"
)
# 該当行を表示する関数
scan_suspicious <- function(dir_site, file_name) {
path <- file.path(dir_site, file_name)
lines <- readLines(path, warn = FALSE, encoding = "UTF-8")
hit <- Reduce(`|`, lapply(patterns_suspicious, \(p) grepl(p, lines)))
line_no <- which(hit)
# 該当なしの場合も0行のデータフレームを返す
data.frame(
file = rep(file_name, length(line_no)),
line = line_no,
text = trimws(lines[line_no])
)
}
for (nm in names(dir_sites)) {
cat("\n", strrep("=", 60), "\nサイト:", nm, "\n", sep = "")
for (f in files_check) {
# ファイルが存在しない場合はスキップ
if (!file.exists(file.path(dir_sites[[nm]], f))) {
cat("\n[", f, "] ファイルなし(スキップ)\n", sep = "")
next
}
res <- scan_suspicious(dir_sites[[nm]], f)
cat("\n[", f, "] 該当", nrow(res), "行\n", sep = "")
if (nrow(res) > 0) print(res, row.names = FALSE)
}
}
============================================================
サイト:saecanet
[wp-config.php] 該当3行
file line text
wp-config.php 15 * より詳しい情報は {@link http://wpdocs.sourceforge.jp/wp-config.php_%E3%81%AE%E7%B7%A8%E9%9B%86
wp-config.php 28 // (http://wpdocs.sourceforge.jp/Codex:%E8%AB%87%E8%A9%B1%E5%AE%A4 参照)
wp-config.php 55 * {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org の秘密鍵サービス} で自動生成することもできます。
============================================================
サイト:contact
[wp-config.php] 該当2行
file line text
wp-config.php 18 * by visiting {@link http://codex.wordpress.org/Editing_wp-config.php Editing
wp-config.php 52 * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}(注意) 上述のとおり、sourceforge.jpは2025年3月31日に閉鎖されております。リンク先には移動しないようにして下さい。
.htaccessファイル
.htaccess に関しましても外部サイトへのリダイレクト、auto_prepend_file、AddHandler がないことを以下のコードで確認します。
なお、弊社Webサイトのリモートディレクトリが出力されますため検査結果をここでは表示していませんが、不審な記述のパターンが無いことを確認済みです。
# 検査対象(サイト固有の確認ファイル)
files_check <- c(".htaccess")
for (nm in names(dir_sites)) {
cat("\n", strrep("=", 60), "\nサイト:", nm, "\n", sep = "")
for (f in files_check) {
# ファイルが存在しない場合はスキップ
if (!file.exists(file.path(dir_sites[[nm]], f))) {
cat("\n[", f, "] ファイルなし(スキップ)\n", sep = "")
next
}
res <- scan_suspicious(dir_sites[[nm]], f)
cat("\n[", f, "] 該当", nrow(res), "行\n", sep = "")
if (nrow(res) > 0) print(res, row.names = FALSE)
}
}yml ファイル
ファイルの更新日時、サイズおよび記述されている全コードから問題ないことを確認済みです。
# 確認するファイル
file_wpcli <- "wp-cli.local.yml"
for (nm in names(dir_sites)) {
path <- file.path(dir_sites[[nm]], file_wpcli)
if (!file.exists(path)) next
cat("\n", strrep("=", 60), "\nサイト:", nm, "\n", sep = "")
cat("更新日時:", format(file.mtime(path), "%Y-%m-%d %H:%M:%S"), "\n")
cat("サイズ :", file.size(path), "バイト\n\n")
cat(readLines(path, warn = FALSE, encoding = "UTF-8"), sep = "\n")
}
============================================================
サイト:saecanet
更新日時: 2023-06-13 10:44:33
サイズ : 32 バイト
apache_modules:
- mod_rewrite以上です。
