Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[limiter] Rate limiting does not work #1237

Closed
mrpaulblack opened this issue May 26, 2022 · 13 comments
Closed

[limiter] Rate limiting does not work #1237

mrpaulblack opened this issue May 26, 2022 · 13 comments
Labels
bug Something isn't working

Comments

@mrpaulblack
Copy link
Member

Version of SearXNG, commit number if you are using on master branch and stipulate if you forked SearXNG

Leons instance: 2022.05.24-fddbc5ed (39077fc0)
ALso my instance: 2022.05.08-85c1c14f 

How did you install SearXNG?

Using the scripts as well as with docker.

What happened?

The limiter plugin does not rate limit requests and uses the X-Forwarded-For header which could be easily set by a client request.

How To Reproduce
Make 15 /search requests in 20sec to trigger rate limiting.
Make another search request, which is going to get blocked with a HTTP 429 response.
Make another /search request which is going to get not blocked again.

Expected behavior
The IP should get blocked for a specific amount of time with a timeout value for example. So a offending host could get blocked for like 2 hours for example.

Screenshots & Logs

Additional context
Another problem is that the IP is read from a header. In a improper setup the IP could maybe not be in that header or a client could abuse this and set the header themselves. The IP should be gotten from a trusted source... (Maube the request lib has something to get the IP?)

@mrpaulblack mrpaulblack added the bug Something isn't working label May 26, 2022
@mrpaulblack
Copy link
Member Author

for vis: @dalf @return42 @tiekoetter

@tiekoetter
Copy link
Member

Tested on my instance :

IPv4:

  • Refresh about 15 times -> 429
  • Wait about 10 seconds -> 200
  • Refresh about once a second after getting blocked initially -> 429

IPv6:

  • Refresh about 15 times -> 429
  • Wait about 10 seconds -> 200
  • Refresh about once a second after getting blocked initially -> 429

@dalf
Copy link
Member

dalf commented May 26, 2022

@return42
Copy link
Member

return42 commented May 26, 2022

How To Reproduce
Make 15 /search requests in 20sec to trigger rate limiting.
Make another search request, which is going to get blocked with a HTTP 429 response.
Make another /search request which is going to get not blocked again.

"is going to get not blocked again." .. I can't reproduce this behaviour .. in my tests, the sliding window works and blocks the following request .. @paul, can you please verify your test.

@mrpaulblack
Copy link
Member Author

well yeah can you try with my instance? https://paulgo.io it happens there for me like this... (Tho I did not update my instance for over 2 weeks => so searxng container and redis is running for over 2 weeks now)

@return42
Copy link
Member

About nginx -- from https://nginx.org/en/docs/http/ngx_http_proxy_module.html#variables

 $proxy_add_x_forwarded_for
    the “X-Forwarded-For” client request header field with the $remote_addr variable
    appended to it, separated by a comma. If the “X-Forwarded-For” field is not present
    in the client request header, the $proxy_add_x_forwarded_for variable is equal to
    the $remote_addr variable.

Does it mean the client can overwrite “X-Forwarded-For” ?

@return42
Copy link
Member

return42 commented May 26, 2022

well yeah can you try with my instance? https://paulgo.io/ it happens there for me like this...

I tested your instance and for me it works like @tiekoetter described "Refresh about once a second after getting blocked initially -> 429" .. what I mean; the sliding window works and blocks the following requests .. the limiter itself is IMO not buggy.


The issue might be that the limiter identifies the request origin by the entire string of:

x_forwarded_for = request.headers.get('X-Forwarded-For', '')

If the client set a “X-Forwarded-For” and change the value with each request, the entire string changes by each request.

@ononoki1
Copy link

ononoki1 commented Jun 6, 2022

If the client set a “X-Forwarded-For” and change the value with each request, the entire string changes by each request.

I think it should be handled by web server like nginx, not backend application like searxng. For example, using set_real_ip_from directive properly or simply using $remote_addr as X-Forwarded-For header are both solutions.


It should be the web server's (or reverse proxy's) responsibility that the X-Forwarded-For header passed to backend application is reliable. SearXNG cannot and should not verify it.

@return42
Copy link
Member

return42 commented Jun 6, 2022

It should be the web server's (or reverse proxy's) responsibility that the X-Forwarded-For header passed to backend application is reliable. SearXNG cannot and should not verify it.

That is absolutely true .. but what when the proxy in front of the app is just one proxy in a chain of proxies .. at its best the first incoming HTTP server should fix (reset) the X-Forwarded-For header .. in most HTTP setups we will find something like this:

proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;

We (the developer) do not know how many proxies in front of the app so I thought it might be better we add a configuration option to select what position in the X-Forwarded-For identifies the IP of the request (client).

@ononoki1
Copy link

ononoki1 commented Jun 6, 2022

Why not use X-Real-IP to replace X-Forwarded-For?

If there is a chain of proxies, only the first incoming one needs to set X-Real-IP to $remote_addr, and other proxies simply keep that header as-is. So SearXNG knows the client IP is just what X-Real-IP shows.

@return42
Copy link
Member

return42 commented Jun 7, 2022

If there is a chain of proxies, only the first incoming one needs to set X-Real-IP

Thats true .. but what if the SearXNG admin does not have the permissions to modify the setup of the first incoming proxy? .. X-Forwarded-For is set by default in most setups: The X-Forwarded-For (XFF) request header is a de-facto standard header for identifying the originating IP address of a client connecting to a web server through a proxy server.

X-Forwarded-For: <client>, <proxy1>, <proxy2>

BTW we have the same issue in the self-info plugin, where we select the first item (#1237 (comment)):

if search.search_query.query == 'ip':
x_forwarded_for = request.headers.getlist("X-Forwarded-For")
if x_forwarded_for:
ip = x_forwarded_for[0]

@return42
Copy link
Member

return42 commented Apr 8, 2023

  1. The X-Forwarded-For (XFF) request header is a de-facto standard header for identifying the originating IP address of ...

  2. the limiter must trust the header X-Forwarded-For given to it by a proxy

The setup of the proxy must pass a correct The X-Forwarded-For to the client.

I don't know what we can do more .. I think this issue can now be closed (if not ask to reopen:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Closed
Development

No branches or pull requests

5 participants