2017-05-17 6 views
0

Wir haben die Definitive guide to form based website authentication mit dem Ziel gelesen, schnelle Anmeldeversuche zu verhindern.Verhindern von Rapid-Fire-Anmeldeversuchen mit Rack :: Attack

Ein Beispiel hierfür könnte sein:

  • 1 gescheiterten Versuch = keine Verzögerung
  • 2 Versuche scheiterten = 2 Sekunden Verzögerung
  • 3 Fehlversuche = 4 sec Verzögerungs
  • etc

In der Anleitung werden andere Methoden angezeigt, die jedoch alle einen Speicher benötigen, der frühere fehlgeschlagene Versuche aufzeichnen kann.

Blocklisting wird in einer der Beiträge in this issue (erscheint unter dem alten Namen Blacklisting, die in der Dokumentation zu Blocklisting geändert wurde) als eine mögliche Lösung diskutiert.

Wie pro Rack :: Angriff gesagt, ein naives Beispiel der Implementierung sein könnte:

Wo die Anmeldung fehlschlägt:

StorageMechanism.increment("bad-login/#{req.ip") 

Im Rack-attack.rb:

Rack::Attack.blacklist('bad-logins') { |req| 
    StorageMechanism.get("bad-login/#{req.ip}") 
} 

Hier gibt es zwei Teile, die die Antwort zurücksenden, wenn sie gesperrt ist und überprüfen, ob ein vorheriger fehlgeschlagener Versuch (StorageMechanism) stattgefunden hat.

Der erste Teil, der die Antwort zurückgibt, kann automatisch vom Edelstein bearbeitet werden. Allerdings sehe ich den zweiten Teil nicht so klar, zumindest mit der De-facto-Wahl für Cache-Backend für die Edelstein- und Rails-Welt, Redis.

Soweit ich weiß, werden die abgelaufenen Schlüssel in Redis automatisch entfernt. Dies würde den Zugriff auf die Information unmöglich machen (selbst wenn sie abgelaufen ist), einen neuen Wert für den Zähler setzen und entsprechend das Zeitlimit für die Refraktärperiode inkrementieren.

Gibt es eine Möglichkeit, dies mit Redis und Rack :: Attack zu erreichen?

Ich dachte, dass der 'StorageMechanism' in diesem Fall absolut agnostisch bleiben muss und nichts über Rack :: Attack und seine Speichermöglichkeiten wissen sollte.

+1

'Rack-:: Attack' normalerweise für * Drosselung großvolumige Anfragen * verwendet wird; Ich würde Regeln wie "3 fehlgeschlagene Versuche = 4 Sekunden Verzögerung" für viel zu streng halten. Lesen Sie die [Beispielkonfigurationen] (https://github.com/kickstarter/rack-attack/wiki/Example-Configuration). Ich glaube, was Sie hier wirklich erreichen wollen, wäre besser mit einem [exponentiellen Backoff] (https://github.com/kickstarter/rack-attack/wiki/Advanced-Configuration#exponential-backoff). –

+0

Hey @ TomLord, vielen Dank für Ihr Feedback. Ich habe das gleiche Gefühl, dass das zu streng ist. Wie auch immer, ich habe die Beispielkonfigurationen gelesen, aber dieser exponentielle Backoff hinterlässt mich mit derselben Frage. In diesem Fall haben wir immer noch eine Drosselung über eine Ressource (das Login), aber das System würde insbesondere diejenigen, die versagen, nicht bestrafen, was, wie ich verstehe, die Absicht der Technik ist, die im Handbuch beschrieben wird. – IoChaos

+1

Sie könnten 'blacklist' anstelle von' throttle' verwenden, mit fast dem gleichen Code wie in diesem Beispiel ... Ich habe genau diese Technik in einer Produktionsanwendung verwendet (einschließlich Tests, um sicherzustellen, dass es funktioniert). Wenn es hilft, könnte ich diesen Code ausgraben und unten einfügen? –

Antwort

1

Entschuldigung für die Verzögerung, um zu Ihnen zurück zu kommen; Es dauerte eine Weile, bis ich meinen alten Code dazu ausgraben konnte.

Wie oben in den Kommentaren diskutiert, hier ist eine Lösung, die ein blacklist, mit einem mit findtime

# config/initilizers/rack-attack.rb 
class Rack::Attack 
    (1..6).each do |level| 
    blocklist("allow2ban login scrapers - level #{level}") do |req| 
     Allow2Ban.filter( 
     req.ip, 
     maxretry: (20 * level), 
     findtime: (8**level).seconds, 
     bantime: (8**level).seconds 
    ) do 
     req.path == '/users/sign_in' && req.post? 
     end 
    end 
    end 
end 

Möglicherweise möchten Sie diese Zahlen zwicken, wie für die jeweilige Anwendung gewünscht; Die obigen Zahlen sind nur das, was ich als "vernünftig" für meine spezielle Anwendung empfand - sie stammen nicht aus irgendeinem offiziellen Standard.

Ein Problem mit den oben, dass die Verwendung bei der Entwicklung/Prüfung (zum Beispiel Ihre rspec Testsuite), um die Anwendung können Sie leicht schlagen die oben genannten Grenzen und drosseln versehentlich selbst.

safelist('allow from localhost') do |req| 
    '127.0.0.1' == req.ip || '::1' == req.ip 
end 

Die häufigste Brute-Force-Login-Angriff ist ein Brute-Force-Angriff, bei dem ein Angreifer einfach eine große Anzahl von E-Mail versucht und Passwörtern, um zu sehen, wenn: Dies kann durch Hinzufügen des folgenden Config Initialisierungsstab vermieden werden alle Anmeldeinformationen übereinstimmen.

Sie sollten dies in der Anwendung durch die Verwendung eines Konto LOCK nach ein paar fehlgeschlagenen Anmeldeversuche mildern. (Wenn Sie z. B. devise verwenden, können Sie eine integrierte verwenden.)

Diese Kontosperrmethode öffnet jedoch einen neuen Angriffsvektor: Ein Angreifer kann das System mit Anmeldeversuchen spammen, Verwenden Sie gültige E-Mails und falsche Passwörter, um alle Konten kontinuierlich neu zu sperren!

Diese Konfiguration hilft, diesen Angriffsvektor zu mindern, indem exponentiell die Anzahl der Anmeldeversuche von einer bestimmten IP begrenzt.

Ich habe auch das folgende "Catch-all" -Anforderung Gas:

throttle('req/ip', limit: 300, period: 5.minutes, &:ip) 

Dies ist vor böswilligen/schlecht konfigurierten Schabern zu drosseln; um zu verhindern, dass sie die gesamte CPU des App-Servers in Beschlag nehmen.

Hinweis: Wenn Sie Assets über das Rack bereitstellen, werden diese Anfragen möglicherweise von Rack-Attacken gezählt und diese Drosselung kann zu schnell aktiviert werden. Wenn dies der Fall ist, aktivieren Sie die Bedingung, um sie vom Tracking auszuschließen.


Ich schrieb auch einen Integrationstest, um sicherzustellen, dass meine Rack::Attack Konfiguration seine Arbeit tat. Es gab ein paar Probleme mit diesen Testarbeiten bei der Herstellung, also werde ich die Code + Kommentare sprechen für sich lassen:

class Rack::AttackTest < ActionDispatch::IntegrationTest 
    setup do 
    # Prevent subtle timing issues (==> intermittant test failures) 
    # when the HTTP requests span across multiple seconds 
    # by FREEZING TIME(!!) for the duration of the test 
    travel_to(Time.now) 

    @removed_safelist = Rack::Attack.safelists.delete('allow from localhost') 
    # Clear the Rack::Attack cache, to prevent test failure when 
    # running multiple times in quick succession. 
    # 
    # First, un-ban localhost, in case it is already banned after a previous test: 
    (1..6).each do |level| 
     Rack::Attack::Allow2Ban.reset('127.0.0.1', findtime: (8**level).seconds) 
    end 
    # Then, clear the 300-request rate limiter cache: 
    Rack::Attack.cache.delete("#{Time.now.to_i/5.minutes}:req/ip:127.0.0.1") 
    end 

    teardown do 
    travel_back # Un-freeze time 
    Rack::Attack.safelists['allow from localhost'] = @removed_safelist 
    end 

    test 'should block access on 20th successive /users/sign_in attempt' do 
    19.times do |i| 
     post user_session_url 
     assert_response :success, "was not even allowed to TRY to login on attempt number #{i + 1}" 
    end 

    # For DOS protection: Don't even let the user TRY to login; they're going way too fast. 
    # Rack::Attack returns 403 for blocklists by default, but this can be reconfigured: 
    # https://github.com/kickstarter/rack-attack/blob/master/README.md#responses 
    post user_session_url 
    assert_response :forbidden, 'login access should be blocked upon 20 successive attempts' 
    end 
end