今日はウェブサイトのアクセスログを見ていたら、Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)
というユーザーエージェントのクローラーが非常に頻繁にウェブサイトをクロールしていることに気付きました。20MB 以上のログを見れば、ウェブサイトをクロールしているのはかなりの時間だとわかります。
このクローラーがクロールしているウェブページを見ると、以前にクロールしたページのいくつかのパラメータをランダムに組み合わせて再度アクセスしているようです。そのため、クロールされるリンクは長い文字列になり、基本的に返されるのは 404 です。さらに、数秒ごとにクロールされるため、ログ内の正常なアクセス記録は数万行のゴミクローラーの記録に埋もれてしまっています。
最初は、クローラーなので robots.txt
を遵守するはずだと思い、robots.txt
にルールを追加すればいいだろうと思いました:
User-agent: SemrushBot
Disallow: /
しかし、ネットで調べてみると、このクローラーはどうやら robots.txt
を遵守しないようです 😅(公式ページではクローラーが robots.txt
を厳密に遵守すると主張していますが、ネットのフィードバックを見る限りそうではないようです)。しかも、この SemrushBot
だけでなく、多くのマーケティングクローラーも robots.txt
を遵守しません。仕方がないので、Nginx を使って遮断することにしました。宝塔の Nginx 無料ファイアウォール の グローバル設定 で User-Agent フィルタ の ルール をクリックし、以下の正規表現を追加しました(ネットから集めたもので、こんなに多いとは思いませんでした。ついでに、いくつかの無駄な UA も追加しましたので、使用前に必要な UA がないか確認してください):
(nmap|NMAP|HTTrack|sqlmap|Java|zgrab|Go-http-client|CensysInspect|leiki|webmeup|Python|python|curllCurl|wget|Wget|toutiao|Barkrowler|AhrefsBot|a Palo Alto|ltx71|censys|DotBot|MauiBot|MegaIndex.ru|BLEXBot|ZoominfoBot|ExtLinksBot|hubspot|FeedDemon|Indy Library|Alexa Toolbar|AskTbFXTV|CrawlDaddy|CoolpadWebkit|Java|Feedly|UniversalFeedParser|ApacheBench|Microsoft URL Control|Swiftbot|ZmEu|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|HttpClient|MJ12bot|heritrix|Bytespider|Ezooms|JikeSpider|SemrushBot)
その後、数秒待つと、遮断されたデータが見えるようになります。
その後、常に増加する遮断数を見て、404 でも 444 でもリクエストを発信するだけで、サーバーリソースを消費することになることに気付きました。このように数秒ごとにリクエストを送るのは長続きしません。これらのクローラーの IP を調べると、すべて海外のノードであり、私のウェブサイトの海外回線も Cloudflare に解析されているので、Cloudflare に中間でブロックしてもらうことができ、ウェブサイトへのアクセスを防ぐことができます。
Cloudflare の対応するドメインコンソールに入ったら、セキュリティの WAF セクションに進み、ルールを追加をクリックします。式プレビューに以下の式を追加します(同様に、使用前に必要な UA がブロックされていないか確認してください):
(http.user_agent contains "SemrushBot") or (http.user_agent contains "FeedDemon") or (http.user_agent contains "Indy Library") or (http.user_agent contains "Alexa Toolbar") or (http.user_agent contains "AskTbFXTV") or (http.user_agent contains "AhrefsBot") or (http.user_agent contains "CrawlDaddy") or (http.user_agent contains "CoolpadWebkit") or (http.user_agent contains "Java") or (http.user_agent contains "Feedly") or (http.user_agent contains "UniversalFeedParser") or (http.user_agent contains "ApacheBench") or (http.user_agent contains "Microsoft URL Control") or (http.user_agent contains "Swiftbot") or (http.user_agent contains "ZmEu") or (http.user_agent contains "jaunty") or (http.user_agent contains "Python-urllib") or (http.user_agent contains "lightDeckReports Bot") or (http.user_agent contains "YYSpider") or (http.user_agent contains "DigExt") or (http.user_agent contains "HttpClient") or (http.user_agent contains "MJ12bot") or (http.user_agent contains "heritrix") or (http.user_agent contains "Bytespider") or (http.user_agent contains "Ezooms") or (http.user_agent contains "JikeSpider") or (http.user_agent contains "HTTrack") or (http.user_agent contains "Apache-HttpClient") or (http.user_agent contains "harvest") or (http.user_agent contains "audit") or (http.user_agent contains "dirbuster") or (http.user_agent contains "pangolin") or (http.user_agent contains "nmap") or (http.user_agent contains "sqln") or (http.user_agent contains "hydra") or (http.user_agent contains "libwww") or (http.user_agent contains "BBBike") or (http.user_agent contains "sqlmap") or (http.user_agent contains "w3af") or (http.user_agent contains "owasp") or (http.user_agent contains "Nikto") or (http.user_agent contains "fimap") or (http.user_agent contains "havij") or (http.user_agent contains "BabyKrokodil") or (http.user_agent contains "netsparker") or (http.user_agent contains "httperf") or (http.user_agent contains " SF/") or (http.user_agent contains "zgrab") or (http.user_agent contains "NMAP") or (http.user_agent contains "Go-http-client") or (http.user_agent contains "CensysInspect") or (http.user_agent contains "leiki") or (http.user_agent contains "webmeup") or (http.user_agent contains "Python") or (http.user_agent contains "python") or (http.user_agent contains "wget") or (http.user_agent contains "Wget") or (http.user_agent contains "toutiao") or (http.user_agent contains "Barkrowler") or (http.user_agent contains "a Palo Alto") or (http.user_agent contains "ltx71") or (http.user_agent contains "censys") or (http.user_agent contains "DotBot") or (http.user_agent contains "MauiBot") or (http.user_agent contains "MegaIndex.ru") or (http.user_agent contains "BLEXBot") or (http.user_agent contains "ZoominfoBot") or (http.user_agent contains "ExtLinksBot") or (http.user_agent contains "hubspot")
その後、操作を選択でブロックを選び、保存すれば完了です。
これで、宝塔の Nginx 無料ファイアウォールのリスク遮断数が増えなくなりました。そして、Cloudflare のファイアウォールルールの遮断数が急増しているときは、Cloudflare がこれらのゴミクローラーのアクセスを成功裏に遮断したことを示しています。
目覚めて更新#
目が覚めると、すでに 2000 回以上遮断されていました。このクローラーは本当にしつこいですね😅。
カスタム遮断ページ#
最近、Cloudflare のデフォルトの遮断ページを見たのですが、あまりにも醜いと感じました... カスタムの遮断ページに変更したいと思いました。
結果、カスタムページを使用するには Pro プランにアップグレードする必要があることがわかりました。まあ、無料で使っている人がカスタムページのためにアップグレードすることは不可能なので、比較的単純な方法で実現することにしました。考え方は、上記の User-Agent に該当する訪問者をすべてカスタムページにリダイレクトすることです。
Note
🔔ご注意:Vercel にホスティングされたページは国内ではアクセスできません。なぜなら、当サイトの海外アクセスのみが Cloudflare を経由するため、国内でのアクセス可否は考慮する必要がありません。国内でもアクセス可能にしたい場合は、他のページホスティングプラットフォームを使用してください。
私たちは自分で静的な遮断ページを作成し、Vercelにホスティングする必要があります。
Vercel デプロイの詳細手順#
まず、Vercelに行き、新しいプロジェクトを作成します。
次に、左側で既存のリポジトリからカスタムページをインポートすることも、右側でテンプレートをクローンすることもできます。ここではまだカスタムページがないので、右側のテンプレートをクローンするリンクをクリックしてテンプレートを選択します。
ここでは React を使用してページを作成しているので、Create React App というテンプレートを使用します。デプロイボタンをクリックしてテンプレートを使用します。
次に、Create Git Repository 内で Github を接続し、カスタムリポジトリ名を入力し、デフォルトで Create private Git Repository を選択してプライベートリポジトリに設定し、Create をクリックすると自動的にデプロイされます。
Congratulations! のメッセージが表示されると、成功裏にデプロイされたことを示します。
最後に、Github にコードを新しく作成したリポジトリにコミットします。コミットが完了すると、Vercel が自動的にページを更新します。
以下は、私が React で作成したカスタムページで、直接使用できます。
\src\App.js
ファイル:
import React, { Component } from 'react'
import './App.css';
export default class App extends Component {
state = {
tran: {
lang: 'en',
title: 'このリクエストはブロックされました',
contents_1: 'あなたのいくつかの特徴がブラックリストに存在し、このリクエストはブロックされました。',
contents_2: 'これが誤報だと思う場合は、すぐに私に連絡してください。',
footer: '@Vinking セキュリティセンター',
tips: '詳細はセキュリティセンターの最適化に保存されました'
}
}
handleTranslations = () => {
const { lang } = this.state.tran
const newState = (lang === 'en') ? {
lang: 'zh',
title: 'リクエストがブロックされました',
contents_1: 'あなたのいくつかの特徴がブラックリストに存在し、このリクエストはブロックされました。',
contents_2: 'これが誤報だと思う場合は、すぐに私に連絡してください。',
symbols: '@ Vinking セキュリティセンター',
tips: '詳細情報はセキュリティセンターの最適化に保存されました'
} : {
lang: 'en',
title: 'このリクエストはブロックされました',
contents_1: 'あなたのいくつかの特徴がブラックリストに存在し、このリクエストはブロックされました。',
contents_2: 'これが誤報だと思う場合は、すぐに私に連絡してください。',
symbols: '@Vinking セキュリティセンター',
tips: '詳細はセキュリティセンターの最適化に保存されました'
}
document.title = newState.title
this.setState({ tran: newState })
}
render() {
const { title, contents_1, contents_2, symbols, tips } = this.state.tran
return (
<div className="content">
<div className="card">
<div className="cardHeader">
<div>{title}</div>
<div className='translation' onClick={this.handleTranslations}>
<svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 1024 1024"><path fill="#f8f9fa" d="M608 416h288c35.36 0 64 28.48 64 64v416c0 35.36-28.48 64-64 64H480c-35.36 0-64-28.48-64-64V608H128c-35.36 0-64-28.48-64-64V128c0-35.36 28.48-64 64-64h416c35.36 0 64 28.48 64 64v288zm0 64v64c0 35.36-28.48 64-64 64h-64v256.032C480 881.696 494.304 896 511.968 896H864a31.968 31.968 0 0 0 31.968-31.968V512A31.968 31.968 0 0 0 864 480.032H608zM128 159.968V512c0 17.664 14.304 31.968 31.968 31.968H512A31.968 31.968 0 0 0 543.968 512V160a31.968 31.968 0 0 0-31.936-32H160a31.968 31.968 0 0 0-32 31.968zm64 244.288V243.36h112.736V176h46.752c6.4.928 9.632 1.824 9.632 2.752a10.56 10.56 0 0 1-1.376 4.128c-2.752 7.328-4.128 16.032-4.128 26.112v34.368h119.648v156.768h-50.88v-20.64h-68.768V497.76h-49.504V379.488h-67.36v24.768H192zm46.72-122.368v60.48h67.392V281.92h-67.36zm185.664 60.48V281.92h-68.768v60.48h68.768zm203.84 488H576L668.128 576h64.64l89.344 254.4h-54.976l-19.264-53.664H647.488l-19.232 53.632zm33.024-96.256h72.864l-34.368-108.608h-1.376l-37.12 108.608zM896 320h-64a128 128 0 0 0-128-128v-64a192 192 0 0 1 192 192zM128 704h64a128 128 0 0 0 128 128v64a192 192 0 0 1-192-192z" /></svg>
</div>
</div>
<div className="cardDesc">
<span className="red">{contents_1}</span>
<br />
{contents_2}
</div>
<div className="cardSymbols">
<div>{symbols}</div>
</div>
</div>
<div className="tips">{tips}</div>
</div>
)
}
}
\public\index.html
ファイル:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no">
<meta name="theme-color" content="#07092f">
<title>このリクエストはブロックされました</title>
<meta name="description" content="このリクエストはブロックされました。">
</head>
<body>
<div id="root"></div>
</body>
</html>
Cloudflare に戻り、ルールセクションのリダイレクトルールをクリックして、新しいルールを作成します。上記のマーケティングクローラーを遮断するための式を式編集ボックスに入力し、タイプを静的に選択し、URLに先ほどの Vercel ホスティングページのリンクを入力し、ステータスコードを301に設定し、クエリ文字列を保持しないを選択すれば、カスタム遮断ページが作成されます。
この記事は Mix Space によって xLog に同期更新されました。原文リンクは https://www.vinking.top/posts/codes/blocking-bots-with-cloudflare