def test_backends_explicit(self): for ha in ["md5", "sha256", "sha512"]: for cache in ["default", "db"]: with override_settings(RATELIMIT_GROUP_HASH=ha, RATELIMIT_KEY_HASH=ha): r = None for i in range(0, 4): r = ratelimit.get_ratelimit(group="test_backends", rate="1/s", key=b"explicittest", cache=cache) self.assertEqual(r["request_limit"], 0) for i in range(0, 2): r = ratelimit.get_ratelimit(group="test_backends", rate="1/s", key=b"explicittest", inc=True, cache=cache) self.assertEqual(r["request_limit"], 1) r = ratelimit.get_ratelimit(group="test_backends", rate="1/s", key=b"explicittest", inc=True, cache=cache) self.assertEqual(r["request_limit"], 1) _get_group_hash.cache_clear()
def test_basic(self): r = None for i in range(0, 4): # just view, without retrieving r = ratelimit.get_ratelimit(group="test_basic", rate="1/s", key=b"abc") self.assertEqual(r["request_limit"], 0) for i in range(0, 2): r = ratelimit.get_ratelimit(group="test_basic", rate="1/s", key=b"abc2", inc=True) self.assertEqual(r["request_limit"], 1) r = ratelimit.get_ratelimit(group="test_basic", rate="1/s", key=b"abc2", inc=True) self.assertEqual(r["request_limit"], 1) time.sleep(2) r = ratelimit.get_ratelimit(group="test_basic", rate="1/s", key=b"abc2", inc=True) self.assertEqual(r["request_limit"], 0)
def test_request_post_get_filter(self): r = None request = self.factory.get('/customer/details') for i in range(0, 4): r = ratelimit.get_ratelimit(group="test_request_post_get_filter", rate="1/s", key="ip", request=request, inc=True, methods=["POST"]) self.assertEqual(r["request_limit"], 0) for i in range(0, 2): r = ratelimit.get_ratelimit(group="test_request_post_get_filter", rate="1/s", key="ip:32/64", inc=True, request=request, methods=["GET"]) self.assertEqual(r["request_limit"], 1) r = ratelimit.get_ratelimit(group="test_request_post_get_filter", rate="1/s", key="ip", inc=True, request=request, methods=["GET"]) self.assertEqual(r["request_limit"], 1)
def authenticate(self, request, username=None, protection_codes=None, nospider=False, **kwargs): """ Use protections for authentication""" # disable SpiderAuthBackend backend (against recursion) if nospider: return uc = UserComponent.objects.filter(user__username=username, name="index").first() if not uc: request.protections = \ Protection.objects.valid().order_by( "code" ).authall( request, scope="auth", ptype=ProtectionType.authentication, protection_codes=protection_codes ) if type(request.protections) is int: # should never happen logger.warning( "Login try without username, should never " "happen, archieved strength: %s", request.protections) return None else: try: request.protections = uc.auth( request, scope="auth", ptype=ProtectionType.authentication, protection_codes=protection_codes) except Http404: # for Http404 auth abort by protections (e.g. Random Fail) pass if type(request.protections) is int: if AssignedContent.travel.auth(request, uc): if request.protections < MIN_PROTECTION_STRENGTH_LOGIN: logger.warning("Low login protection strength: %s, %s", request.protections, username) return uc.user # error path # allow blocking per hour ratelimit.get_ratelimit(request=request, group="spider_login_failed_ip", key="ip", inc=True, rate=(float("inf"), 3600)) ratelimit.get_ratelimit(request=request, group="spider_login_failed_account", key=lambda x, y: username, inc=True, rate=(float("inf"), 3600)) # be less secure here, most probably the user is already known time.sleep(_nonexhaustRandom.random() / 2)
def clean_domain_upgrade(self, context, token): if "referrer" not in self.request.GET: return False # domain mode must be used alone if len(context["intentions"]) > 1: return False if not context["intentions"].issubset(VALID_INTENTIONS): return False if not getattr(self.request, "_clean_domain_upgrade_checked", False): if ratelimit.get_ratelimit(request=self.request, group="clean_domain_upgrade", key=("get", { "IP": True, "USER": True }), rate=settings.SPIDER_DOMAIN_UPDATE_RATE, inc=True)["request_limit"] > 0: return False setattr(self.request, "_clean_domain_upgrade_checked", True) # False for really no token if token is False: return True if not token or token.extra.get("strength", 0) >= 10: return False return True
def rate_limit_default(request, view): group = getattr(view, "rate_limit_group", None) if group: ratelimit.get_ratelimit(request=request, group=group, key="user_or_ip", inc=True, rate=(math.inf, 3600)) results = failed_guess.send_robust(sender=view, request=request) for (receiver, result) in results: if isinstance(result, Exception): logging.error("%s failed", receiver, exc_info=result) # with 0.4% chance reseed if _nonexhaustRandom.randint(0, 249) == 0: _nonexhaustRandom.seed(os.urandom(10)) time.sleep(_nonexhaustRandom.random() / 2) raise Http404()
def get(self, request, *args, **kwargs): request.ratelimit2 = ratelimit.get_ratelimit(group=ratelimit.o2g( self.get), rate="1/s", key=b"o2gtest", inc=True) if request.ratelimit2["request_limit"] > 0: return HttpResponse(status=400) return HttpResponse()
def test_block_empty(self): request = self.factory.get('/customer/details') request.user = AnonymousUser() r = ratelimit.get_ratelimit(group="test_block_empty", rate="1/s", key="user", request=request, empty_to=123) self.assertEqual(r["request_limit"], 123)
def test_inverted(self): request = self.factory.get('/customer/details') r = ratelimit.get_ratelimit(group="test_inverted", rate="1/s", key="ip:32/64", inc=True, request=request, methods=ratelimit.invertedset(["GET"])) self.assertEqual(r["count"], 0)
def test_bypass_empty(self): r = None request = self.factory.get('/customer/details') request.user = AnonymousUser() for i in range(0, 4): r = ratelimit.get_ratelimit(group="test_bypass_empty", rate="1/s", key="user", request=request, empty_to=0) self.assertEqual(r["request_limit"], 0)
def test_window(self): # window should start with first inc and end after period # (fixed window counter algorithm) r = None for i in range(0, 2): r = ratelimit.get_ratelimit(group="test_window", rate="2/4s", key=b"abc", inc=True) self.assertEqual(r["request_limit"], 0) time.sleep(1) r = ratelimit.get_ratelimit(group="test_window", rate="2/4s", key=b"abc", inc=True) self.assertEqual(r["request_limit"], 1) # window times out time.sleep(3) r = ratelimit.get_ratelimit(group="test_window", rate="2/4s", key=b"abc", inc=True) self.assertEqual(r["request_limit"], 0)
def test_request(self): r = None request = self.factory.get('/customer/details') for i in range(0, 4): r = ratelimit.get_ratelimit(group="test_request", rate="1/s", key="ip", request=request) self.assertEqual(r["request_limit"], 0) for i in range(0, 2): r = ratelimit.get_ratelimit(group="test_request", rate="1/s", key="ip:32/64", inc=True, request=request) self.assertEqual(r["request_limit"], 1) r = ratelimit.get_ratelimit(group="test_request", rate="1/s", key="ip", inc=True, request=request) self.assertEqual(r["request_limit"], 1)
def auth(cls, request, obj, **kwargs): if not obj: return False if obj: temp = obj.data.get("rate_accessed", None) if temp and ratelimit.get_ratelimit( request=request, group="spider_ratelimit_accessed", key=cls.count_access(request, obj), rate=temp, inc=True)["request_limit"] > 0: return False temp = obj.data.get("rate_static_token_error", None) if temp and ratelimit.get_ratelimit( request=request, group="spider_static_token_error", key="user_or_ip", rate=(int(temp), 3600), inc=False)["request_limit"] > 0: return False temp = obj.data.get("rate_login_failed_ip", None) if temp and ratelimit.get_ratelimit( request=request, group="spider_login_failed_ip", key="ip", rate=(int(temp), 3600), inc=False)["request_limit"] > 0: return False temp = obj.data.get("rate_login_failed_account", None) if temp and ratelimit.get_ratelimit( request=request, group="spider_login_failed_account", key=lambda x, y: obj.usercomponent.username, rate=(int(temp), 3600), inc=False)["request_limit"] > 0: return False return 1
def refer_with_post(self, context, token): # application/x-www-form-urlencoded is best here, # for beeing compatible to most webservers # client side rdf is no problem # NOTE: csrf must be disabled or use csrf token from GET, # here is no way to know the token value h = hashlib.sha256(context["referrer"].encode("utf8")).hexdigest() def h_fun(*a): return h # rate limit on errors if ratelimit.get_ratelimit(request=self.request, group="refer_with_post.refer_with_post", key=h_fun, rate=settings.SPIDER_DOMAIN_ERROR_RATE, inc=False)["request_limit"] > 0: return HttpResponseRedirect( redirect_to=merge_get_url(context["referrer"], status="post_failed", error="error_rate_limit")) d = { "token": token.token, "hash_algorithm": settings.SPIDER_HASH_ALGORITHM.name, "action": context["action"], } if context["payload"] is not None: d["payload"] = context["payload"] params, inline_domain = get_requests_params(context["referrer"]) if inline_domain: response = Client().post( context["referrer"], data=d, Connection="close", Referer=merge_get_url( "%s%s" % (context["hostpart"], self.request.path) # sending full url not required anymore, payload ), SERVER_NAME=inline_domain) if response.status_code != 200: return HttpResponseRedirect(redirect_to=merge_get_url( context["referrer"], status="post_failed", error="other")) else: try: with requests.post( context["referrer"], data=d, headers={ "Referer": merge_get_url( "%s%s" % (context["hostpart"], self.request.path) # sending full url not required anymore, payload ), "Connection": "close" }, **params) as resp: resp.raise_for_status() except requests.exceptions.SSLError as exc: logger.info("referrer: \"%s\" has a broken ssl configuration", context["referrer"], exc_info=exc) return HttpResponseRedirect(redirect_to=merge_get_url( context["referrer"], status="post_failed", error="ssl")) except (Exception, requests.exceptions.HTTPError) as exc: apply_error_limit = False if isinstance(exc, (requests.exceptions.ConnectionError, requests.exceptions.Timeout)): apply_error_limit = True elif (isinstance(exc, requests.exceptions.HTTPError) and exc.response.status_code >= 500): apply_error_limit = True if apply_error_limit: ratelimit.get_ratelimit( request=self.request, group="refer_with_post", key=h_fun, rate=settings.SPIDER_DOMAIN_ERROR_RATE, inc=True) logger.info("post failed: \"%s\" failed", context["referrer"], exc_info=exc) return HttpResponseRedirect(redirect_to=merge_get_url( context["referrer"], status="post_failed", error="other")) context["post_success"] = True h = get_hashob() h.update(token.token.encode("utf-8", "ignore")) return HttpResponseRedirect(redirect_to=merge_get_url( context["referrer"], status="success", hash=h.finalize().hex()))