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.

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!