sunny / graph_attack

Ruby GraphQL analyser for blocking & throttling calls by IP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support rate limiting by custom field

italotabatinga opened this issue · comments

Hello there! We've been using this library on our product and it has been working great!

We have another use case, however, that I think might fit in this gem. Instead of rate limiting by IP, we wanted to rate limit by client id.

In our case, this is useful in some sensitive OTP mutations where we want to block brute force attacks per client instead of per IP, considering that an attacker will have a great range of IPs to use.

There's a possible solution below and I'm happy to open a PR if you find this useful too!

suggestion

This solution allows other context keys to be defined as the rate limited key while still keeping IP by default for backward compatibility.

def resolve(object:, arguments:, **_rest)
  value = object.context[value]
  raise GraphAttack::Error, "Missing #{value} value on the GraphQL context" unless value

  return RateLimited.new('Query rate limit exceeded') if calls_exceeded_on_query?(value)

  yield(object, arguments)
end

private

def value
  options[:value] || :ip
end

In our case, we'd use this like:

extension GraphAttack::RateLimit,
                  threshold: 60,
                  interval: 60,
                  redis_client: REDIS,
                  value: :customer_uuid,

Hi Ítalo, that sounds like a great addition, I’d be happy to merge something like this! 😍

A few thoughts:

  • Maybe we can find a better name than value. To me I find it a bit counter-intuitive that it should hold the key we are using in the context. I’m having a hard time thinking of a better name: rate_limited_key:, for_context_key:, field:, limit_key: or limited_by_context_key: could be ok. Perhaps it could be something even shorter like on:. What do you think?

  • Since this option is not global but per-field this means we can have several rate limits on one field, for example one per IP and one per customer. For this to work we might need to change the key that is used for rate limiting for it to include the new field. E.g.:

    def key
      on = "-#{options[:on]}" if options[:on]
      "graphql-query-#{field.name}#{on}"
    end

I’m having a hard time thinking of a better name:

I was too with rate_limited_key and field 😆 But I really liked your on suggestion!

For this to work we might need to change the key that is used for rate limiting for it to include the new field.

Indeed! ty

I think we can follow through with this, sounds good?

Yeah, go for it! 🔥 🔥 \o/