溫故知新,.Net Core利用UserAgent+rDNS雙解析方案,正確識別並反爬蟲/反垃圾郵件


背景

一般有價值的並保有數據的網站或接口很容易被爬蟲,爬蟲會占用大量的流量資源,接下來我們參考歷史經驗,探索如何在.Net Core中利用UserAgent+rDNS雙解析方案來正確識別並且反爬蟲。

image

新建網絡爬蟲識別項目

https://github.com/CraigTaylor/WebBotRecognition

在終端命令行中,基於DotNet-Clinew命令新建名為WebBotRecognitionwebapi項目,並且不需要https,它將自動創建一個net5.0的網絡接口項目。

dotnet new webapi -o WebBotRecognition --no-https

image

cd WebBotRecognition

切換到項目目錄

code .

用Visual Studio Code來打開當前目錄。

image

image

於是,我們便完成一個演示項目創建。

執行命令,先運行起來。

dotnet watch run

image

image

基於DotNet-Clirun命令把項目中的模板示例先運行起來,確保一切正常,這里攜帶watch參數來確保后面熱更新。

好了,接下來,我們刪掉自帶的Controller那些東西。

image

image

嘗試解析請求來源的User Agent

User Agent中文名為用戶代理,簡稱UA,它是一個特殊字符串頭,使得服務器能夠識別客戶使用的操作系統及版本、CPU類型、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器插件等。

在.Net Core中我們可以基於UAParser組件包來實現對User Agent字符串的識別和解析。

https://github.com/ua-parser/uap-csharp

image

安裝UAParser

https://www.nuget.org/packages/UAParser/

dotnet add package UAParser

image

基於DotNet-Cliadd package命令在當前項目中安裝並添加UAParser組件。

新建識別接口測試

在項目的Controllers目錄下新建名為RecognizeController的控制器,繼承自MVC中的ControllerBase,並且設置好路由地址和新建一個名為GetUserAgent的方法。

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace WebBotRecognition.Controllers
{
    /// <summary>
    /// 識別
    /// </summary>
    [Route("api/[controller]")]
    public class RecognizeController : ControllerBase
    {
        /// <summary>
        /// 獲取UserAgent
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetUserAgent")]
        public async Task<JsonResult> GetUserAgent()
        {
            return new JsonResult(null);
        }
    }
}

image

利用UAParser組件來解析來源User Agent

/// <summary>
/// 獲取UserAgent
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetUserAgent")]
public async Task<JsonResult> GetUserAgent()
{
    // 定義解析結果信息對象
    ClientInfo clientInfo = null;

    // 嘗試從頭部里面獲取User-Agent字符串
    if(Request.Headers.TryGetValue("User-Agent", out var requestUserAgent) && !string.IsNullOrEmpty(requestUserAgent))
    {
        // 獲取UaParser實例
        var uaParser = Parser.GetDefault();

        // 解析User-Agent字符串
        clientInfo = uaParser.Parse(requestUserAgent);
    }

    return new JsonResult(clientInfo);
}

這里我們可以從Request.Headers里面通過TryGetValue的方式相對安全的讀取到User Agent字符串。

先獲取一個UaParser實例,然后基於它直接解析RequestUserAgent即可。

我們通過PostMan調用來看看返回結果。

image

Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.62
{
    "string": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.62",
    "os": {
        "family": "Windows",
        "major": "10",
        "minor": null,
        "patch": null,
        "patchMinor": null
    },
    "device": {
        "isSpider": false,
        "brand": "",
        "family": "Other",
        "model": ""
    },
    "userAgent": {
        "family": "Edge",
        "major": "90",
        "minor": "0",
        "patch": "818"
    },
    "ua": {
        "family": "Edge",
        "major": "90",
        "minor": "0",
        "patch": "818"
    }
}

結果很清晰,如果我們需要進一步通過結果去判斷,就可以按這個結構來了。

我們多看幾組。

Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0
{
    "string": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0",
    "os": {
        "family": "Windows",
        "major": "7",
        "minor": null,
        "patch": null,
        "patchMinor": null
    },
    "device": {
        "isSpider": false,
        "brand": "",
        "family": "Other",
        "model": ""
    },
    "userAgent": {
        "family": "IE",
        "major": "9",
        "minor": "0",
        "patch": null
    },
    "ua": {
        "family": "IE",
        "major": "9",
        "minor": "0",
        "patch": null
    }
}
Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.1
{
    "string": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.1",
    "os": {
        "family": "Mac OS X",
        "major": "10",
        "minor": "7",
        "patch": "0",
        "patchMinor": null
    },
    "device": {
        "isSpider": false,
        "brand": "Apple",
        "family": "Mac",
        "model": "Mac"
    },
    "userAgent": {
        "family": "Chrome",
        "major": "17",
        "minor": "0",
        "patch": "963"
    },
    "ua": {
        "family": "Chrome",
        "major": "17",
        "minor": "0",
        "patch": "963"
    }
}

嘗試解析請求來源的Ip及反查域名

通常常規的搜索引擎的爬蟲機器在爬取你的網站的時候,是有固定爬蟲ip的,我們反爬蟲,其實不想誤傷他們,畢竟有些網站還是需要依賴SEO來提高曝光率的,但是偽裝成主流搜索引擎爬蟲的User-Agent的成本極低,那么我們能進一步判斷和區分呢?

這里,我們可以用到一個叫rDNS的機制,大型的搜索引擎提供商,比如谷歌、百度以及微軟均會有固定的IP地址作為爬蟲的IP,並且會將這些IP做rDNS解析。可以通過查詢某個IP的rDNS解析記錄,來判斷這個IP到底是不是真的來自谷歌或者百度。

反向DNS(rDNS或RDNS)是從IP地址查找域名的域名服務(DNS)。 常規的DNS請求將解析給定域名的IP地址; 因此名稱為“ reverse”。反向DNS也稱為反向DNS查找和反向DNS。

反向DNS請求通常用於過濾垃圾郵件。 垃圾郵件發送者可以使用所需的任何域名輕松地設置發送電子郵件地址,包括銀行或受信任組織等合法域名。

接收電子郵件服務器可以通過使用反向DNS請求檢查發送IP地址來驗證傳入消息。 如果電子郵件是合法的,則rDNS解析器應與電子郵件地址的域匹配。 這種技術的缺點是某些合法的郵件服務器末端沒有正確的rDNS記錄設置才能正確響應,因為在許多情況下,它們的ISP必須設置這些記錄。

image

Google的爬蟲ip(66.249.66.1)為例,我們可以通過nslookup命令來反溯其域名。

image

image

嘗試在NetCore中獲取真實來源Ip

據說(暫時只是看網上說)在.Net Core中如果要獲取真實來源Ip,默認是不行的,但是可以基於一個官方的Microsoft.AspNetCore.HttpOverrides包來實現,接下來我們安裝下這個包。

https://www.nuget.org/packages/Microsoft.AspNetCore.HttpOverrides/

dotnet add package Microsoft.AspNetCore.HttpOverrides

image

然后在項目的Startup.cs文件的ConfigureServices方法中添加ForwardedHeadersOptions的配置支持。

public void ConfigureServices(IServiceCollection services)
{
    // 添加Forwarded Headers Middleware配置
    services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
        options.KnownNetworks.Clear();
        options.KnownProxies.Clear();
    });
    
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebBotRecognition", Version = "v1" });
    });
}

並且在Startup.cs文件的Configure方法中啟用ForwardedHeaders中間件。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebBotRecognition v1"));
    }

    // Forwarded Headers Middleware
    app.UseForwardedHeaders();

    ...
}

接下來,我們就可以獲取到來源真實IP地址了。

/// <summary>
/// 獲取真實客戶端來源Ip
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetRealClientIpAddress")]
public async Task<JsonResult> GetRealClientIpAddress()
{
    // 通過Connection的RemoteIpAddress獲取IPV4的IP地址
    var requestIp = Request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
    return new JsonResult(requestIp);
}

通過真實來源Ip反向查找爬蟲域名

前面我們已經拿到了真實的請求IP,然后我們可以借助.Net中的DNS類來反向解析Ip地址來模擬實現nslookup命令的效果。

/// <summary>
/// 獲取rDNS反向域名
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("GetrDnsHostName")]
public async Task<JsonResult> GetrDnsHostName(string requestIp)
{
    if(string.IsNullOrEmpty(requestIp))
    {
        // 通過Connection的RemoteIpAddress獲取IPV4的IP地址
        requestIp = Request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
    }

    IPHostEntry iPHostEntry;
    try
    {
        // 根據真實請求Ip反向解析請求Ip的域名
        iPHostEntry = await Dns.GetHostEntryAsync(requestIp);
    }
    catch
    {
        iPHostEntry = null;
    }

    // 獲取請求的域名地址
    var requestHostName = iPHostEntry?.HostName;
    
    return new JsonResult(requestHostName);
}

image

通過requestIp我們可以傳入指定Ip,這里我們用GoogleBot的Ip做個實驗,發現就能反向查找得到*.googlebot.com這樣的域名。

這里注意,如果Ip反向查找不出來,會拋異常,我們要通過try-catch來穩住這個情況。

結合User-Agent和rDNS技術來判斷真實的爬蟲

我們可以通過UaParser來解析是否為爬蟲的User Agent,同時輔助使用是否為爬蟲的來源Ip來判斷,這樣就比較穩妥。

Content-Type:application/json
Accept:application/json
User-Agent:Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
requestIp=66.249.66.1
/// <summary>
/// 是否來自真實的爬蟲機器人
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("IsRealSpiderBot")]
public async Task<bool> IsRealSpiderBot(string requestIp)
{
    // 定義解析結果信息對象
    ClientInfo clientInfo = null;

    // 嘗試從頭部里面獲取User-Agent字符串
    if(Request.Headers.TryGetValue("User-Agent", out var requestUserAgent) && !string.IsNullOrEmpty(requestUserAgent))
    {
        // 獲取UaParser實例
        var uaParser = Parser.GetDefault();

        // 解析User-Agent字符串
        clientInfo = uaParser.Parse(requestUserAgent);
    }

    if(string.IsNullOrEmpty(requestIp))
    {
        // 通過Connection的RemoteIpAddress獲取IPV4的IP地址
        requestIp = Request.HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
    }

    IPHostEntry iPHostEntry;
    try
    {
        // 根據真實請求Ip反向解析請求Ip的域名
        iPHostEntry = await Dns.GetHostEntryAsync(requestIp);
    }
    catch
    {
        iPHostEntry = null;
    }

    // 獲取請求的域名地址
    var requestHostName = iPHostEntry?.HostName;

    // 當反向解析域名不為空且UserAgent解析結果為爬蟲時,才真正為爬蟲機器人
    var isRealSpiderBot = !string.IsNullOrEmpty(requestHostName) && (clientInfo?.Device?.IsSpider ?? false);
    return isRealSpiderBot;
}

這里我們還是以Google Bot的ip為例,調用下,結果符合預期。

image

常見的搜索引擎爬蟲的User Agent

1. GoogleBot

Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

2. Bingbot

Mozilla/5.0 (compatible; Bingbot/2.0; +http://www.bing.com/bingbot.htm)

3. Slurp Bot

Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)

4. DuckDuckBot

DuckDuckBot/1.0; (+http://duckduckgo.com/duckduckbot.html)

5. Baiduspider

Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)

6. Yandex Bot

Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)

7. Sogou Spider

Sogou Pic Spider/3.0( http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou head spider/3.0( http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou Orion spider/3.0( http://www.sogou.com/docs/help/webmasters.htm#07)
Sogou-Test-Spider/4.0 (compatible; MSIE 5.5; Windows 98)

8. Exabot

Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Exabot-Thumbnails)
Mozilla/5.0 (compatible; Exabot/3.0; +http://www.exabot.com/go/robot)

9. Facebook external hit

facebot
facebookexternalhit/1.0 (+http://www.facebook.com/externalhit_uatext.php)
facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)

10. Alexa crawler

ia_archiver (+http://www.alexa.com/site/help/webmasters; crawler@alexa.com)

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM