Der beste Weg, um die Anforderungsdrosselung in ASP.NET MVC zu implementieren?

212

Wir experimentieren mit verschiedenen Möglichkeiten, um Benutzeraktionen in einem bestimmten Zeitraum zu drosseln :

  • Frage / Antwort-Beiträge begrenzen
  • Beschränken Sie die Änderungen
  • Beschränken Sie das Abrufen von Feeds

Derzeit verwenden wir den Cache, um einfach einen Datensatz der Benutzeraktivität einzufügen. Wenn dieser Datensatz vorhanden ist, wenn der Benutzer dieselbe Aktivität ausführt, drosseln wir.

Durch die automatische Verwendung des Caches werden veraltete Datenbereinigungs- und Schiebeaktivitätsfenster von Benutzern angezeigt. Die Skalierung kann jedoch ein Problem darstellen.

Welche anderen Möglichkeiten gibt es, um sicherzustellen, dass Anforderungen / Benutzeraktionen effektiv gedrosselt werden können (Schwerpunkt auf Stabilität)?

Jarrod Dixon
quelle
Versuchen Sie, pro Benutzer oder pro Frage zu begrenzen? Wenn pro Benutzer, könnte Sitzung verwenden, die eine kleinere Menge wäre.
Greg Ogle
1
Es ist pro Benutzer, aber wir konnten Session nicht verwenden, da dies Cookies erfordert - wir beschränken uns auf die aktuelle IP-Adresse.
Jarrod Dixon
1
Heutzutage sollten Sie die Nuget-Pakete github.com/stefanprodan/MvcThrottle für MVC-Seiten und github.com/stefanprodan/WebApiThrottle für Web-API-Anfragen in Betracht ziehen
Andy

Antworten:

240

Hier ist eine generische Version dessen, was wir im letzten Jahr für Stack Overflow verwendet haben:

/// <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;
        }
    }
}

Beispielnutzung:

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

Der ASP.NET-Cache funktioniert hier wie ein Champion - wenn Sie ihn verwenden, erhalten Sie eine automatische Bereinigung Ihrer Drosselklappeneinträge. Und mit unserem wachsenden Datenverkehr sehen wir nicht, dass dies ein Problem auf dem Server ist.

Sie können gerne Feedback zu dieser Methode geben. Wenn wir den Stapelüberlauf verbessern, erhalten Sie Ihren Ewok-Fix noch schneller :)

Jarrod Dixon
quelle
5
kurze Frage - Sie verwenden den Wert c.HttpContext.Request.UserHostAddress als Teil des Schlüssels. Ist dieser Wert möglich leer oder null oder alle der gleiche Wert? (dh, wenn Sie einen Load Balancer verwenden und es sich um die IP dieses Computers handelt, nicht um die echten Clients.) Geben Sie wie Proxys oder Load Balancer (dh eine BIG IP F5) dieselben Daten ein, und Sie müssen dies überprüfen für X-Forwarded-For auch oder so?
Pure.Krome
7
@ Pure.Krome - ja, das könnte sein. Wenn die Client - IP abrufen, verwenden wir eine Hilfsfunktion , dass die Kontrollen sowohl die REMOTE_ADDRund HTTP_X_FORWARDED_FORServervariablen und saniert angemessen.
Jarrod Dixon
3
@BrettRobi, ich bin mir ziemlich sicher, dass sie eine Serveraffinität haben, die auf der IP-Adresse des Benutzers basiert. Sie werden also wahrscheinlich immer noch denselben Server treffen.
mmcdole
4
Für diejenigen unter Ihnen, die sich darum kümmern und dies so weit unten im Kommentar-Stream gelesen haben ... haben wir am Ende unsere eigenen Weiterleitungen geschrieben, die den Gas-Cache-Schlüssel löschen, bevor wir umleiten. Auf diese Weise durchlaufen alle Weiterleitungen den Code, um den Schlüssel zu entfernen, und keiner von ihnen löst das Throttle-Attribut aus.
SLoret
4
Wenn Sie nach der Web-API-Version suchen, überprüfen Sie hier: stackoverflow.com/questions/20817300/…
Papa Burgundy
68

Microsoft hat eine neue Erweiterung für IIS 7 namens Dynamic IP Restrictions Extension für IIS 7.0 - Beta.

"Die dynamischen IP-Einschränkungen für IIS 7.0 sind ein Modul, das Schutz vor Denial-of-Service- und Brute-Force-Angriffen auf Webserver und Websites bietet. Dieser Schutz wird durch vorübergehendes Blockieren der IP-Adressen der HTTP-Clients bereitgestellt, die ungewöhnlich viele gleichzeitige Anforderungen stellen oder die über einen kurzen Zeitraum eine große Anzahl von Anfragen stellen. " http://learn.iis.net/page.aspx/548/using-dynamic-ip-restrictions/

Beispiel:

Wenn Sie die Kriterien festlegen, nach denen blockiert werden soll, X requests in Y millisecondsoder X concurrent connections in Y millisecondsdie IP-Adresse für blockiert Y millisecondswird, werden Anforderungen erneut zugelassen.

notandy
quelle
1
Wissen Sie, ob es Probleme mit Crawlern wie dem Googlebot verursacht hat?
Helephant
1
Es ist jetzt veröffentlicht und mit IIS ab Version 8 gebündelt - iis.net/learn/get-started/whats-new-in-iis-8/…
Matthew Steeples
Ich würde das gerne benutzen, aber es erlaubt dir NICHT, durch zu drosseln <location>. Es ist jede Anfrage für die App oder keine.
Kasey Speakman
Scheint nicht nützlich zu sein, wenn sich Ihre Webserver hinter einem Load Balancer befinden, da der gesamte Datenverkehr anscheinend von derselben IP-Adresse stammt. Es sei denn, ich vermisse etwas Offensichtliches ...
Dscoduc
11

Wir verwenden die von dieser URL http://www.codeproject.com/KB/aspnet/10ASPNetPerformance.aspx entliehene Technik nicht zum Drosseln, sondern für den Denial Of Service (DOS) eines armen Mannes. Dies ist auch cache-basiert und ähnelt möglicherweise dem, was Sie tun. Drosseln Sie, um DOS-Angriffe zu verhindern? Router können sicherlich verwendet werden, um DOS zu reduzieren; Glaubst du, ein Router könnte die Drosselung bewältigen, die du brauchst?

Rob Kraft
quelle
1
Das ist ziemlich genau das, was wir bereits tun - aber es funktioniert großartig :)
Jarrod Dixon