Il modo migliore per implementare la limitazione delle richieste in ASP.NET MVC?


212

Stiamo sperimentando vari modi per limitare le azioni dell'utente in un determinato periodo di tempo :

  • Limitare i post di domande / risposte
  • Limitare le modifiche
  • Limita il recupero dei feed

Per il momento, stiamo usando la cache per inserire semplicemente un record di attività dell'utente - se quel record esiste se / quando l'utente svolge la stessa attività, limitiamo.

L'uso della cache ci fornisce automaticamente finestre di attività di pulizia e scorrimento dei dati obsolete degli utenti, ma il modo in cui verrà ridimensionato potrebbe essere un problema.

Quali sono alcuni altri modi per garantire che le richieste / azioni dell'utente possano essere efficacemente limitate (enfasi sulla stabilità)?


Stai cercando di limitare per utente o per domanda? Se per utente, potrebbe utilizzare la sessione, che sarebbe un set più piccolo.
Greg Ogle,

1
È per utente, ma non è stato possibile utilizzare Session, poiché ciò richiede cookie: al momento stiamo limitando gli indirizzi IP.
Jarrod Dixon

1
Oggi, prendi in considerazione i pacchetti nuget github.com/stefanprodan/MvcThrottle per le pagine MVC e github.com/stefanprodan/WebApiThrottle per le richieste di API web
Andy,

Risposte:


240

Ecco una versione generica di ciò che abbiamo usato su Stack Overflow nell'ultimo anno:

/// <summary>
/// Decorates any MVC route that needs to have client requests limited by time.
/// </summary>
/// <remarks>
/// Uses the current System.Web.Caching.Cache to store each client request to the decorated route.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ThrottleAttribute : ActionFilterAttribute
{
    /// <summary>
    /// A unique name for this Throttle.
    /// </summary>
    /// <remarks>
    /// We'll be inserting a Cache record based on this name and client IP, e.g. "Name-192.168.0.1"
    /// </remarks>
    public string Name { get; set; }

    /// <summary>
    /// The number of seconds clients must wait before executing this decorated route again.
    /// </summary>
    public int Seconds { get; set; }

    /// <summary>
    /// A text message that will be sent to the client upon throttling.  You can include the token {n} to
    /// show this.Seconds in the message, e.g. "Wait {n} seconds before trying again".
    /// </summary>
    public string Message { get; set; }

    public override void OnActionExecuting(ActionExecutingContext c)
    {
        var key = string.Concat(Name, "-", c.HttpContext.Request.UserHostAddress);
        var allowExecute = false;

        if (HttpRuntime.Cache[key] == null)
        {
            HttpRuntime.Cache.Add(key,
                true, // is this the smallest data we can have?
                null, // no dependencies
                DateTime.Now.AddSeconds(Seconds), // absolute expiration
                Cache.NoSlidingExpiration,
                CacheItemPriority.Low,
                null); // no callback

            allowExecute = true;
        }

        if (!allowExecute)
        {
            if (String.IsNullOrEmpty(Message))
                Message = "You may only perform this action every {n} seconds.";

            c.Result = new ContentResult { Content = Message.Replace("{n}", Seconds.ToString()) };
            // see 409 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
            c.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
        }
    }
}

Esempio di utilizzo:

[Throttle(Name="TestThrottle", Message = "You must wait {n} seconds before accessing this url again.", Seconds = 5)]
public ActionResult TestThrottle()
{
    return Content("TestThrottle executed");
}

La cache ASP.NET funziona come un campione qui: utilizzandolo, si ottiene la pulizia automatica delle voci dell'acceleratore. E con il nostro traffico in crescita, non stiamo vedendo che questo è un problema sul server.

Sentiti libero di dare un feedback su questo metodo; quando miglioriamo Stack Overflow, ottieni la tua correzione Ewok ancora più veloce :)


5
domanda veloce: stai usando il valore c.HttpContext.Request.UserHostAddress come parte della chiave. Quel valore è possibile vuoto o nullo o lo stesso valore? (ad esempio, se stai utilizzando un bilanciamento del carico ed è l'IP di quella macchina .. non i client reali) Ad esempio, i proxy o i bilanciatori di carico (ovvero un BIG IP F5) inseriscono gli stessi dati e devi controllare per X-Forwarded-For anche o qualcosa del genere?
Pure.Krome,

7
@ Pure.Krome - sì, potrebbe essere. Quando si recuperano l'IP del client, utilizziamo una funzione di supporto che i controlli sia l' REMOTE_ADDRe HTTP_X_FORWARDED_FORvariabili del server e sanifica in modo appropriato.
Jarrod Dixon

3
@BrettRobi, sono abbastanza sicuro che abbiano affinità con il server in base all'indirizzo IP dell'utente. Quindi probabilmente continueranno a colpire lo stesso server.
mmcdole,

4
Per quelli di voi che si preoccupano e hanno letto molto in fondo nel flusso dei commenti ... abbiamo finito per scrivere i nostri reindirizzamenti che cancellano la chiave della cache dell'acceleratore prima di reindirizzare. In questo modo tutti i reindirizzamenti passano attraverso il codice per rimuovere la chiave e nessuno di essi attiva l'attributo Throttle.
SLoret,

4
Se stai cercando la versione dell'API Web, controlla qui: stackoverflow.com/questions/20817300/…
Papa Borgogna,

68

Microsoft ha una nuova estensione per IIS 7 denominata Estensione di restrizioni IP dinamiche per IIS 7.0 - Beta.

"Le restrizioni IP dinamiche per IIS 7.0 sono un modulo che fornisce protezione contro denial of service e attacchi di forza bruta su server Web e siti Web. Tale protezione viene fornita bloccando temporaneamente gli indirizzi IP dei client HTTP che effettuano un numero insolitamente elevato di richieste simultanee o che effettuano un numero elevato di richieste in un breve periodo di tempo. " http://learn.iis.net/page.aspx/548/using-dynamic-ip-restrictions/

Esempio:

Se si impostano i criteri per bloccare dopo X requests in Y millisecondso X concurrent connections in Y millisecondsl'indirizzo IP verrà bloccato per Y millisecondsallora le richieste saranno nuovamente consentite.


1
Sai se ha causato problemi con i crawler come Googlebot?
Helephant,


1
Ora è rilasciato e in bundle con IIS a partire dalla versione 8 - iis.net/learn/get-started/whats-new-in-iis-8/…
Matthew Steeples

Mi piacerebbe usarlo, ma NON ti permette di rallentare <location>. È ogni richiesta per l'app o nessuna.
Kasey Speakman,

Non sembra utile se i tuoi server web sono dietro un bilanciamento del carico, poiché tutto il traffico sembrerà provenire dallo stesso indirizzo IP. A meno che non mi manchi qualcosa di ovvio ...
Dscoduc,

11

Usiamo la tecnica presa in prestito da questo URL http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx , non per limitare, ma per Denial Of Service (DOS) di un uomo povero. Anche questo è basato sulla cache e potrebbe essere simile a quello che stai facendo. Stai strozzando per prevenire attacchi DOS? I router possono certamente essere usati per ridurre il DOS; pensi che un router sia in grado di gestire la limitazione di cui hai bisogno?


1
È praticamente quello che stiamo già facendo - ma funziona benissimo :)
Jarrod Dixon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.