Umami.Netti ja Bot-detection (Suomi (Finnish))

Umami.Netti ja Bot-detection

Thursday, 12 September 2024

//

6 minute read

Esitys

Näin ollen posted a LOT aiemmin Umamia käytetään analyyttisesti itsenäisessä ”-” -ympäristössä ja jopa julkaistiin Umami.Net Nuget pacakge. Minulla oli kuitenkin ongelma, jossa halusin seurata RSS-kanavani käyttäjiä.

Ongelma

Ongelma on siinä, että RSS-kanavan käyttäjät yrittävät välittää hyödyllinen Käyttäjäagentit pyydettäessä reittiä. Tämä sallii soveltuva liikenteenharjoittajien on seurattava niiden käyttäjien määrää ja niitä käyttäjiä, jotka kuluttavat reittiä. Tämä merkitsee kuitenkin myös sitä, että Umami tunnistaa nämä pyynnöt botti pyynnöt. Tämä on minulle käyttökelpoinen kysymys, koska sen seurauksena pyyntö jätetään huomiotta eikä jäljitetä.

Feedbinin käyttäjäagentti näyttää tällaiselta:

Feedbin feed-id:1234 - 21 subscribers

Tämä on myös ongelma, koska se tarkoittaa sitä, että Umami jättää pyynnön huomiotta. {"beep": "boop"} meaning that this is identified as a bot request. This is annoying as I can't handle this through normal error handling M SK2it's a {\displaystyle {\mathrm {MSK , eikä sanota \muun muassa \

Kun tarkastelemme Umami Dashboardia tämän muutoksen jälkeen, voin nyt nähdä alkuperäiset UserAgentsit, jotka ovat pyytäneet reittiä:

Umami Dashboard

Voimme siis nähdä, että ' on nyt joukko, joka olisi herättänyt robotin havaitsemisen, mutta voimme nyt nähdä nämä ja seurata pyyntöjä.

Ratkaisu

Mikä on tämän ratkaisu??. Voin'. En voi käsitellä kaikkia näitä pyyntöjä ja havaita, havaitseeko Umami havaitsee ne bottina? ;. Se käyttää IsBotia. Minun on siis pysäytettävä pyyntö ennen kuin se pääsee Umamiin ja muutettava käyttäjäagentti johonkin asiaan, jonka Umami hyväksyy tiettyjen pyyntöjen osalta.

Näin ollen olen nyt lisännyt joitakin lisäparametereja omiin jäljitettämismenetelmiini Umamissa.Net. Näiden avulla voitte täsmentää, että uusi 'Default User AgentM SK3 lähetetään Umamiin alkuperäisen User Agentin sijasta.

Menetelmät

Minun UmamiBackgroundSender Lisäsin seuraavaa:

   public async Task TrackPageView(string url, string title, UmamiPayload? payload = null,
        UmamiEventData? eventData = null, bool useDefaultUserAgent = false)

Tämä on olemassa kaikissa jäljitettävissä menetelmissä ja vain asettaa parametrin UmamiPayload object.

Otetaan käyttöön UmamiClient ne voidaan asettaa seuraavasti:

    [Fact]
    public async Task TrackPageView_WithUrl()
    {
        var handler = JwtResponseHandler.Create();
        var umamiClient = GetServices(handler);
        var response = await umamiClient.TrackPageViewAndDecode("https://example.com", "Example Page",
            new UmamiPayload { UseDefaultUserAgent = true });
        Assert.NotNull(response);
        Assert.Equal(UmamiDataResponse.ResponseStatus.Success, response.Status);
    }

Tässä testissä käytän uutta TrackPageViewAndDecode menetelmä, joka palauttaa UmamiDataResponse object. Objektissa on dekodioitu JWT-tokeni (, joka ei ole pätevä, jos se on botti, joten tämä on hyödyllistä tarkistaa

PayloadService

Tämä kaikki käsitellään Payload Palvelu, joka on vastuussa maksukannan sisällön määrittelystä. UseDefaultUserAgent on asetettu.

Standardin mukaan populaoin maksukantaa HttpContext Näin ollen saatte tavallisesti tämän järjestelmän oikein; IM SK1 osoitan myöhemmin, missä se on vetäytynyt Umamista.

    private UmamiPayload GetPayload(string? url = null, UmamiEventData? data = null)
    {
        var httpContext = httpContextAccessor.HttpContext;
        var request = httpContext?.Request;

        var payload = new UmamiPayload
        {
            Website = settings.WebsiteId,
            Data = data,
            Url = url ?? httpContext?.Request?.Path.Value,
            IpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString(),
            UserAgent = request?.Headers["User-Agent"].FirstOrDefault(),
            Referrer = request?.Headers["Referer"].FirstOrDefault(),
            Hostname = request?.Host.Host
        };

        return payload;
    }

Sitten minulla on eräs koodi, joka kutsutaan PopulateFromPayload jossa pyynnön kohde saa sen

    public static string DefaultUserAgent =>
        $"Mozilla/5.0 (Windows 11)  Umami.Net/{Assembly.GetAssembly(typeof(UmamiClient))!.GetName().Version}";

    public UmamiPayload PopulateFromPayload(UmamiPayload? payload, UmamiEventData? data)
    {
        var newPayload = GetPayload(data: data);
        ...
        
        newPayload.UserAgent = payload.UserAgent ?? DefaultUserAgent;

        if (payload.UseDefaultUserAgent)
        {
            var userData = newPayload.Data ?? new UmamiEventData();
            userData.TryAdd("OriginalUserAgent", newPayload.UserAgent ?? "");
            newPayload.UserAgent = DefaultUserAgent;
            newPayload.Data = userData;
        }


        logger.LogInformation("Using UserAgent: {UserAgent}", newPayload.UserAgent);
     }        
        

Huomaatte, että tämä määrittelee uuden käyttäjäagentin tiedoston ytimessä (, jonka olen vahvistanut olevan tällä hetkellä havaitaan botina). Sen jälkeen menetelmässä se havaitsee, onko joko UserAgent null (joka ei pitäisiM SK2 tapahtua, ellei se 's kutsu kodesta ilman HttpContextia UseDefaultUserAgent on asetettu. Jos se on, se asettaa UserAgentin standardiksi ja lisää alkuperäisen UserAgentin tietoobjektiin.

Tämä on sitten tallennettu, jotta voitte nähdä, mitä UserAgentia käytetään.

Vastauksen dekodiointi.

'AndDecode' -menetelmät, jotka palauttavat UmamiDataResponse väline. Tämä väline sisältää dekoditun JWT-tokenin

    public async Task<UmamiDataResponse?> TrackPageViewAndDecode(
        string? url = "",
        string? title = "",
        UmamiPayload? payload = null,
        UmamiEventData? eventData = null)
    {
        var response = await TrackPageView(url, title, payload, eventData);
        return await DecodeResponse(response);
    }
    
        private async Task<UmamiDataResponse?> DecodeResponse(HttpResponseMessage responseMessage)
    {
        var responseString = await responseMessage.Content.ReadAsStringAsync();

        switch (responseMessage.IsSuccessStatusCode)
        {
            case false:
                return new UmamiDataResponse(UmamiDataResponse.ResponseStatus.Failed);
            case true when responseString.Contains("beep") && responseString.Contains("boop"):
                logger.LogWarning("Bot detected data not stored in Umami");
                return new UmamiDataResponse(UmamiDataResponse.ResponseStatus.BotDetected);

            case true:
                var decoded = await jwtDecoder.DecodeResponse(responseString);
                if (decoded == null)
                {
                    logger.LogError("Failed to decode response from Umami");
                    return null;
                }

                var payload = UmamiDataResponse.Decode(decoded);

                return payload;
        }
    }

Voitte nähdä, että tämä kutsuu normaaliin TrackPageView menetelmä kutsuu sen jälkeen DecodeResponse joka tarkistaa vastauksen beep ja boop strings (bot-tutkimukseenM SK1 Jos se löytää ne, se kirjaa varoituksen ja palauttaa BotDetected status. Jos se ei löydä niitä, se dekoditsee JWT-tokenin ja palauttaa maksukannan

JWT-token itse on vain Base64 kodoitu string, joka sisältää Umamin tallettamat tiedot. UmamiDataResponse object.

Kokonaislähde tästä on jäljempänä:

Response Decoder
using System.IdentityModel.Tokens.Jwt;

namespace Umami.Net.Models;

public class UmamiDataResponse
{
    public enum ResponseStatus
    {
        Failed,
        BotDetected,
        Success
    }

    public UmamiDataResponse(ResponseStatus status)
    {
        Status = status;
    }

    public ResponseStatus Status { get; set; }

    public Guid Id { get; set; }
    public Guid WebsiteId { get; set; }
    public string? Hostname { get; set; }
    public string? Browser { get; set; }
    public string? Os { get; set; }
    public string? Device { get; set; }
    public string? Screen { get; set; }
    public string? Language { get; set; }
    public string? Country { get; set; }
    public string? Subdivision1 { get; set; }
    public string? Subdivision2 { get; set; }
    public string? City { get; set; }
    public DateTime CreatedAt { get; set; }
    public Guid VisitId { get; set; }
    public long Iat { get; set; }

    public static UmamiDataResponse Decode(JwtPayload? payload)
    {
        if (payload == null) return new UmamiDataResponse(ResponseStatus.Failed);
        payload.TryGetValue("visitId", out var visitIdObj);
        payload.TryGetValue("iat", out var iatObj);
        //This should only happen then the payload is dummy.
        if (payload.Count == 2)
        {
            var visitId = visitIdObj != null ? Guid.Parse(visitIdObj.ToString()!) : Guid.Empty;
            var iat = iatObj != null ? long.Parse(iatObj.ToString()!) : 0;

            return new UmamiDataResponse(ResponseStatus.Success)
            {
                VisitId = visitId,
                Iat = iat
            };
        }

        payload.TryGetValue("id", out var idObj);
        payload.TryGetValue("websiteId", out var websiteIdObj);
        payload.TryGetValue("hostname", out var hostnameObj);
        payload.TryGetValue("browser", out var browserObj);
        payload.TryGetValue("os", out var osObj);
        payload.TryGetValue("device", out var deviceObj);
        payload.TryGetValue("screen", out var screenObj);
        payload.TryGetValue("language", out var languageObj);
        payload.TryGetValue("country", out var countryObj);
        payload.TryGetValue("subdivision1", out var subdivision1Obj);
        payload.TryGetValue("subdivision2", out var subdivision2Obj);
        payload.TryGetValue("city", out var cityObj);
        payload.TryGetValue("createdAt", out var createdAtObj);

        return new UmamiDataResponse(ResponseStatus.Success)
        {
            Id = idObj != null ? Guid.Parse(idObj.ToString()!) : Guid.Empty,
            WebsiteId = websiteIdObj != null ? Guid.Parse(websiteIdObj.ToString()!) : Guid.Empty,
            Hostname = hostnameObj?.ToString(),
            Browser = browserObj?.ToString(),
            Os = osObj?.ToString(),
            Device = deviceObj?.ToString(),
            Screen = screenObj?.ToString(),
            Language = languageObj?.ToString(),
            Country = countryObj?.ToString(),
            Subdivision1 = subdivision1Obj?.ToString(),
            Subdivision2 = subdivision2Obj?.ToString(),
            City = cityObj?.ToString(),
            CreatedAt = createdAtObj != null ? DateTime.Parse(createdAtObj.ToString()!) : DateTime.MinValue,
            VisitId = visitIdObj != null ? Guid.Parse(visitIdObj.ToString()!) : Guid.Empty,
            Iat = iatObj != null ? long.Parse(iatObj.ToString()!) : 0
        };
    }
}
Voitte nähdä, että tässä on paljon hyödyllistä tietoa Umamin tallennetusta pyynnöstä. Jos haluatte esimerkiksi näyttää erilaista sisältöä paikalliseen järjestelmään perustuvanaM SK1 kieleen, hakukone jne., tämä antaa teille mahdollisuuden tehdä senMSC3
    public Guid Id { get; set; }
    public Guid WebsiteId { get; set; }
    public string? Hostname { get; set; }
    public string? Browser { get; set; }
    public string? Os { get; set; }
    public string? Device { get; set; }
    public string? Screen { get; set; }
    public string? Language { get; set; }
    public string? Country { get; set; }
    public string? Subdivision1 { get; set; }
    public string? Subdivision2 { get; set; }
    public string? City { get; set; }
    public DateTime CreatedAt { get; set; }
    public Guid VisitId { get; set; }
    public long Iat { get; set; }

Päätelmissä

Joten vain lyhyt artikkeli, jossa käsitellään Umamin uusia toimintoja.Net 0.4.0, jonka avulla voitte määritellä tavanomaisen käyttäjäagentin tietyistä pyynnöistä. Tämä on hyödyllistä sellaisten pyyntöjen jäljittämiseksi, joita Umami muutoin sivuuttaa

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.