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

Missing global per-ip rate limit leads to service abuse #14951

Open
1 task
ThisIsMissEm opened this issue Nov 13, 2024 · 19 comments
Open
1 task

Missing global per-ip rate limit leads to service abuse #14951

ThisIsMissEm opened this issue Nov 13, 2024 · 19 comments
Labels
✨Feature This adds/improves/enhances a feature

Comments

@ThisIsMissEm
Copy link

💡 Summary

Hi, currently there is an issue with Misskey servers being attacked by spam from a discord server. The way the script that I've seen works is to use access tokens for a list of accounts and then to make requests until the individual accounts has exhausted the API request limit for the account. This effectively means that there is no upper bound on the number of requests a malicious actor could make.

As a global middleware on all API requests, Misskey should rate-limit by IP Address, the value for this would depend on the number of concurrent accounts you want to receive API requests from for a single IP address within a given window. For instance, in Mastodon, we have a global rate limit for API requests of 1,500 requests per 5 minutes.

Have such a global per-IP address rate limit in place would prevent Misskey servers from being abused as much by these kids that are fighting over their discord servers.

🥰 Expected Behavior

Too many requests from the same IP Address within a given window results in rate limiting

🤬 Actual Behavior

No rate limiting occurs because all the current rate limits are tied to the authenticated account and there is no global rate limit per IP Address

📝 Steps to Reproduce

n/a

💻 Frontend Environment

n/a

🛰 Backend Environment (for server admin)

* Installation Method or Hosting Service: n/a
* Misskey: all versions to my knowledge
* Node: n/a
* PostgreSQL: n/a
* Redis: n/a
* OS and Architecture: n/a

Do you want to address this bug yourself?

  • Yes, I will patch the bug myself and send a pull request
@ThisIsMissEm ThisIsMissEm added the ⚠️bug? This might be a bug label Nov 13, 2024
@ThisIsMissEm
Copy link
Author

The current outcome of this issue is that we're having to report service abuse to hosting provides of misskey instances that are being attacked and used to spam the Fediverse. To maintain a good standing within the Fediverse, implementing rate limiting is a necessity.

IFTAS has been tracking these spam attacks and coordinating responses.

@ThisIsMissEm
Copy link
Author

(translated)

💡 まとめ
こんにちは、現在Misskeyサーバがdiscordサーバからのスパムに攻撃されている問題があります。私が見たスクリプトの動作方法は、アカウントのリストにアクセストークンを使用し、個々のアカウントがアカウントのAPIリクエスト制限を使い果たすまでリクエストを行うというものです。これは事実上、悪意のあるアクターが行えるリクエスト数に上限がないことを意味する。
全てのAPIリクエストのグローバルミドルウェアとして、MisskeyはIPアドレスによるレート制限を行うべきで、この値は与えられたウィンドウ内で1つのIPアドレスからAPIリクエストを受けたい同時アカウント数に依存する。例えば、MastodonではAPIリクエストのグローバルレートリミットを5分あたり1,500リクエストとしています。
このようなグローバルなIPアドレスごとのレート制限を設けることで、Discordサーバーをめぐって争っている子供たちによってMisskeyサーバーが悪用されるのを防ぐことができます。

🥰 予期される動作
同一IPアドレスからのリクエストが一定時間内に多すぎると、レートが制限される。

🤬 実際の動作
現在のレートリミットはすべて認証済みアカウントに関連付けられ、IPアドレスごとのグローバルなレートリミットはないため、レートリミットは発生しません。

⚠️ 結果
この問題の現在の結果は、Fediverseへのスパムに攻撃され、利用されているミスキーインスタンスのホスティング提供者にサービスの不正利用を報告しなければならないことです。Fediverse内で良好な状態を維持するためには、レート制限を実施することが必要です。
IFTASはこれらのスパム攻撃を追跡し、対応を調整している。

@KisaragiEffective
Copy link
Collaborator

This suits better as a feature request than bug report.

@KisaragiEffective KisaragiEffective added ✨Feature This adds/improves/enhances a feature and removed ⚠️bug? This might be a bug labels Nov 13, 2024
@KisaragiEffective
Copy link
Collaborator

KisaragiEffective commented Nov 14, 2024

はっきり言ってこのアイデアは気に入らないです。
荒らしが大変というのであればIPアドレスという原理的に可変なものによっていたずらに制限を増やすよりも、発信内容によって制限するべきだと考えています。
私は荒らし対策というものを偽陽性率ができるだけ低くなるように調整されるべきであるものとして認識しています。それに該当するものとして、 https://github.com/shrimpia/great-ebichiri-wall に見られるActivityPubのパケットに介入して条件付きでパケットを捨てるものや、 #13210 (#13207) などがすでに全世界のサーバーで使える機能としてその地位を築いてきました。

「偽陽性率ができるだけ低くなるような防御方法」という点において、このissueは少し考えればわかるような欠点を抱えています。一番大きな点は変動IPアドレスや共有IPアドレスについて全く考えられていないことです。一般にIPアドレスと言っても、IPv4アドレスとIPv6アドレスの2種類が存在し、全世界において広く普及しているのはIPv4アドレスになるであることは自明に了解されるものかと思います。現時点のIPv4アドレスの割当はISPのポリシーによっても異なりますが、時間帯によって異なるエンドユーザーへ割り当てられたり、NATのように終端がISP側のゲートウェイだったり、ルーターの電源を消すと違うIPアドレスが割り当てられたりといつまでも同じIPアドレスで有り続けるケースのほうが少ないです。IPv6アドレスであればCIDR記法における/64以下がランダムに割り当てられるケースが多いためなおのことそうですが、日本以外ではIPv6網があまり普及していないためここでは考えないことにします。
話を戻すと、そのような切迫したIPv4アドレス空間の運用においてIPアドレスが頻繁に変わるため、特定のIPアドレスをキーとすることは無意味です。無意味どころか、マンションやアパートなどのケースにおいてNATまがいのことが行われていた場合にはNATより終端側の潜在的な利用者が無実の罪で利用を制限されることに繋がります。
そういったケースをよく理解しないままこの機能を有効にする管理者は多いのではないでしょうか?私はMisskeyのUIやmisskey-hubの説明不足が解消されない限り、こういった諸刃の剣の防御機能には賛同できません。潜在的に必要な管理者が居る可能性があるのは同意しますが、Misskey本体に導入するにはリスクが大きいと考えます。

@fruitriin
Copy link
Contributor

fruitriin commented Nov 14, 2024

私もKisaragiEffectiveさんと同意見で、その機能を実現するのはMisskey本体ではなく、もしどうしてもあなたがその機能が必要であるならば、前段のFirewallやWAFで実現するの方が本質的な問題解決のアプローチであるように思います

@ThisIsMissEm
Copy link
Author

Whilst I'm aware that IP rate limiting isn't the pinnacle of security, it's certainly a low cost measure. If the limit is say 2000 request's within a 5 minute window, that'd allow an apartment with NAT to have 500 requests per person assuming 4 people all use the exact same Misskey server.

But right now Misskey's approach is not mitigating spam and flood attacks, so something must be done to improve that situation.

Increasing difficulty of attacks is one such measure. You could also do a global limit on posting or the hemming distance between recent posts.

However, Misskey should not become synonymous with spam from japanese script kiddies fighting like the school children they are over discord servers.

--

IPレート制限はセキュリティの頂点ではないことは承知していますが、確かに低コストの手段です。制限が5分間のウィンドウ内に2000件のリクエストの場合、4人全員がまったく同じMisskeyサーバーを使用していると仮定すると、NATのあるアパートで1人あたり500件のリクエストが可能になります。

しかし、現在、Misskeyのアプローチはスパムや洪水攻撃を緩和していないため、その状況を改善するために何かを行わなければなりません。

攻撃の難易度を上げることは、そのような手段の1つです。また、投稿のグローバル制限や、最近の投稿間のヘミング距離を設定することもできます。

しかし、ミスキーは、不和サーバーを介して学童のように戦う日本のスクリプトの子供たちからのスパムの代名詞になるべきではありません。

@tai-cha
Copy link
Contributor

tai-cha commented Nov 14, 2024

But right now Misskey's approach is not mitigating spam and flood attacks, so something must be done to improve that situation.

At this point, the fix on #14811 (already released in alpha) should improve the problem.

(One thing to bear in mind is that the attackers' main base of operations is Japan, where Misskey is preferred over other countries. Some of the servers under attack have also stopped updating altogether. It is already possible to limit the rate limit per actor on a role-by-role basis.)

@fruitriin
Copy link
Contributor

「なんらかの対策が必要である」という点において完全に同意できます!私も悩まされている管理人の一人です。

一つの解決策として、フォークではありますが以下のような実装があります
MisskeyIO#466
これは相互フォローの関係のないサーバーからのリレーを介したメンションを拒否するもので、あなたのサーバーにスパムユーザーがいなければ、煩わしいスパム投稿をほとんど完全に遮断することができます。
私のサーバーでもこのPRだけパッチしていますが、外部からの煩わしいスパムを防ぐことに成功しているように見えます。

私はこのPRが公式にMisskeyにバックポートされないのは、Fediverseの自由の守護の観点で公式に取り入れることができないのではないかと思っていますが、私はMisskeyの開発グループの一人ではないので真意のほどはわかりません。
このようなポリシーが実際に存在するかどうかはわからないのですが、もしそのようなポリシーが存在するのであれば ――これは私のただの思いこみに過ぎないかもしれませんが―― IPによるレートリミットをMisskeyに期待しても実装されることはないのではないかと考えています。

このような点を踏まえて、CloudflareのようなWAFの側で制限を行うという手順であなたの期待を達成するのは現実的な手段であるように思います。

@tai-cha
Copy link
Contributor

tai-cha commented Nov 14, 2024

相互フォローかは判定する方法は確かに現状ないですが、自らのサーバーからのフォローがいない場合にメンション数制限を行う等は現時点でのコンディショナルロールによる制限でも可能です(Misskeyでのリモートユーザーのフォロー・フォロワー数は自サーバーのユーザーのみがカウントされるため)

@ThisIsMissEm
Copy link
Author

Another option you might have at your disposal is making access tokens short-lived and requiring that they be refreshed from time to time.

That would make preparing a list of tokens for attack ahead of time more difficult, since they'd expire and have to be refreshed

(I currently maintain a lot of the OAuth related things in Mastodon, and moving away from static access tokens is very high on our list)

--

自由に使える別のオプションは、アクセストークンを短命にし、時々更新する必要があることです。

有効期限が切れて更新する必要があるため、攻撃用のトークンのリストを事前に準備することが難しくなります。

(私は現在、MastodonでOAuthに関連する多くのものを維持しており、静的アクセストークンから離れることは、私たちのリストの上位にあります)

@fruitriin
Copy link
Contributor

fruitriin commented Nov 14, 2024

Another option you might have at your disposal is making access tokens short-lived and requiring that they be refreshed from time to time.

ああ、その方法は利便性とのトレードオフについて詳しく検討する必要がありますが、個人的にはセンスが良いと思います。

相互フォローかは判定する方法は確かに現状ないですが、自らのサーバーからのフォローがいない場合にメンション数制限を行う等は現時点でのコンディショナルロールによる制限でも可能です(Misskeyでのリモートユーザーのフォロー・フォロワー数は自サーバーのユーザーのみがカウントされるため)

なんということでしょう!コンディショナルロールの機能を使って、リモートユーザーの振る舞いをある程度制限することができます。私は全く気づきませんでした。
完璧な対応であるとはとても言えませんが、コードの変更を必要とせず今すぐできる対処方法のうちの一つであるように聞こえます。

@syuilo
Copy link
Member

syuilo commented Nov 14, 2024

  • 環境によっては異なるユーザーが同じIPになることが割とあるから困りそう
  • IPを変えて攻撃することは容易だからスパム対策の効果は限定的そう
  • IPによる云々はMisskeyより前段のレイヤーでやる話な気がする

等の理由から当該機能の恩恵というのは少ない気がするけど、よく要望されるものではあるし大したコストも無く実装できるはずだからオプションでやってもよさそう
ただ相応の注意書きは要りそう(nginxなどの設定が不適切な状態で当該機能を使うと全てのユーザーがブロックされかねないとか)

@ThisIsMissEm
Copy link
Author

Your current rate limit for creating Notes is, if I recall correctly, 300 per hour per account. Maybe there needs to be a lower maximum within a 5 minute timespan? e.g., 20 per 5 minute rolling window? (That's 300 / 60 * 5 * 80%), that would still allow decent uaage within the hour, but would prevent floods where all 300 tokens in the rate limiter couldn't be used as quickly as possible.

--

ノートを作成するための現在のレート制限は、私の記憶が正しければ、アカウントごとに1時間あたり300です。おそらく、5分間の期間内により低い最大値が必要ですか?例えば、5分間のローリングウィンドウあたり20?(それは300 / 60 * 5 * 80%です)、それでも1時間以内にまともなuaageを許可しますが、レートリミッターの300トークンすべてができるだけ早く使用できない洪水を防ぎます。

@ThisIsMissEm
Copy link
Author

That'd mean they'd only be able to publish 20 notes per account per 5 minutes, instead of 300 per account within 5 minutes

@fruitriin
Copy link
Contributor

Yes, and late limit factor affect almost all api.
notice: api limit factor default is 100 and if you put 0 means no limit.
so if you want limit spamming access, you need put factor as 100+ number.
such as 300 or so on

@ThisIsMissEm
Copy link
Author

ThisIsMissEm commented Nov 14, 2024

You currently use the ratelimiter package (did you know about async-ratelimiter for promises? I saw a different implementation in your code to try to support promises)

You could have two different instances:

  1. Per hour with 300 max
  2. Per 5 minutes with 25 max
const createNoteFloodLimiter = new Limiter({
  id: `per-five:${accountId}:createNote`,
  db: redis,
  max: 25,
  duration: 5 * 60 * 1000 // 5 minutes
})

And then evaluate against both the 300 per hour AND the createNoteFloodLimiter to get a general per hour limit and something that protects against flood attacks.

Though, to be honest, this rate limiting package seems very limited. E.g., this is an alternative I know about from Adonis.js, which has possibly a better API for this type of thing and has burst protection: https://github.com/animir/node-rate-limiter-flexible

(It is weird that the ratelimiter package you use requires an entire separate instance per identifier you want to limit, when all the settings are shared)

Yes, it's true that the per-five should almost never trigger in normal usage, you can probably work this out to a correct amount using a statistics query on your database to count notes per 5 minute windows to get the right numbers. It might be lower or higher than 25 notes creations.

@fruitriin
Copy link
Contributor

fruitriin commented Nov 14, 2024

あなたが紹介したライブラリは私にとっては初耳なものです。
原作のレートリミットのコードはこちらです。参考になるでしょうか?
packages/backend/src/server/api/RateLimiterService.ts

@ThisIsMissEm
Copy link
Author

あなたが紹介したライブラリは私にとっては初耳なものです。
原作のレートリミットのコードはこちらです。さ参考になるねーしょ垢?
packages/backend/src/server/api/RateLimiterService.ts

はい、私はそれとそれがエンドポイントでどのように使用されているかを見ました。私はあなたのコードベースをかなり読みました。

(Yes, I've seen that and how it is used in endpoints. I have read your codebase quite a lot.)

@kakkokari-gtyih
Copy link
Contributor

kakkokari-gtyih commented Nov 14, 2024

一つの解決策として、フォークではありますが以下のような実装があります
MisskeyIO#466
私はこのPRが公式にMisskeyにバックポートされないのは、Fediverseの自由の守護の観点で公式に取り入れることができないのではないかと思っていますが、私はMisskeyの開発グループの一人ではないので真意のほどはわかりません。

(Original post #14951 (comment))

FYI: Backporting is being discussed here: #14888

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨Feature This adds/improves/enhances a feature
Projects
None yet
Development

No branches or pull requests

6 participants