-- Leo's gemini proxy

-- Connecting to gemini.circumlunar.space:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini

CSRF in Gemini


CSRF (Cross-Site Request Forgery) is, briefly, a way of doing malicious stuff on behalf of an user by making them do a request by clicking on a link or otherwise.


Wikipedia explains it better than me


Unsurprisingly, this is a problem for Gemini too.


Some sites are starting to use INPUT (10) as an effectful request type, for example for posting a comment or liking a page. Unfortunately, since the input is passed as query string, there is no way to determine if it actually came from the user or if it was pre-filled with a malicious link.


For example, if gemini://example.org/post was an endpoint for adding a comment, I could put in my site a link to gemini://example.org/post?SPAM and whoever clicked that link would unknowingly comment "SPAM". A redirect would be even more stealthier.


Again, since there's no "Referer" equivalent on Gemini (for good reasons), it's impossible to detect this attack with simple urls like in the previous example.


Moreover, since in Gemini there's no difference between a read-only request and an effectful one, even crawlers are going to trigger that link and post "SPAM" on each update!


Add in authentication/authorization and this all becomes 1000 times worse.


There are sites that are vulnerable to this attack already.


A solution


The crawler problem can simply be avoided with a robots.txt (if the crawlers are well-behaved).


To completely (?) fix the issue, the same method that's used on the web can be implemented.


example.org/post redirects to example.org/nonce/<some_random_nonce>

example.org/nonce/<some_random_nonce> will actually ask the user for input and redirect them to the success page

nonces expire after a few minutes and are hard to guess

optionally, check that the nonce was generated and used by the same IP

in any case, IP throttling/blocking is implemented on the nonce generation endpoint

auth* has to be done in both endpoints


This way, a malicious actor has to first get a fresh nonce before tricking the user into visiting the /nonce endpoint.


As an example, this is already implemented (minus the blocking, plz do not abuse) in:


gemini://gemini-textboard.fgaz.me


Can we do better?


The web failed to natively protect the user from this type of attack, having to resort to ugly hidden form inputs with nonces. Can we do better in Gemini?


The first thing that comes to mind is having a nonce as part of the META in the INPUT response (with a new 1x code), and then requiring the client to send it back. But this would require a separator, and we all know where this leads.


Feedback and ideas welcome!


-- Response ended

-- Page fetched on Thu Mar 28 10:47:12 2024