Closed undergroundwires closed 8 months ago
This is a consequence of the WebDAV protocol and how it handles authentication.
The Radicale web UI and the WSGI are very simple and lightweight for performance reasons. As such, authentication is handled in accordance with the WebDAV spec's Basic Authentication (see http://www.webdav.org/specs/rfc2617.html#n-basic-authentication-scheme).
This means implementing a CAPTCHA challenge or similar in the web UI may slow down attackers (and legitimate users) using the web UI but it won't stop attackers attacking through other methods (like brute-forcing credentials in the URLs directly).
Radicale has an option to set a delay when authentication fails (see https://radicale.org/v3.html#delay). This does not increment with the number of failed logins, so it will equally punish attackers and legitimate users who enter incorrect credentials.
Doing some quick research I see that the approach used by Nextcloud was to implement a similar approach with a throttle in the web server on an incorrect login, but with an option to set maximum attempts and blocking attempts beyond that point (See here for more info: https://github.com/nextcloud/security-advisories/security/advisories/GHSA-fjv7-283f-5m54).
An immediate approach could be handling authentication using a reverse proxy and passing the authenticated user to Radicale with the (see https://radicale.org/v3.html#manage-user-accounts-with-the-reverse-proxy).
In the long run, perhaps an approach could be to keep track of the number of failed attempts and after a certain point multiply the delay by the attempts or block attempts after a maximum threshold is reached. This would require keeping attempts from IP's in memory for a certain duration.
Thank you for all context @MatthewHana, constructive but also critical that weighs pros and cons.
This is about who we want to protect us against, i.e. the threat actors. Captcha provides a security layer against script kiddies with these penetration testing tools brute-forcing the form which I see as the common problem with all of us keeping Radicale public-facing. There are hundreds of bots out there that does it everyday on any public page. I'm unsure if delaying when authentication fails is good measure against this or just creates DoS attack vector for myself due to the kiddies. For more dedicated attackers on brute-forcing WebDAV protocol, we are most likely fucked in one way or another.
I think your suggestion with reverse-proxy sounds like the best one to me, because this way we could enforce MFA to tackle these attacks. However, if WebDAV does not support MFA as I understand so none of the client applications will be available after this. Could we somehow enforce MFA for web client only? I'm not sure about this, it feels like everything is tightly coupled.
fail2ban
could mitigate this including WebDAV but using captcha is still the most convenient option. On another level, most of use Radicale to "own" it, introducing Google's RECAPTCHA and similar public services harvesting massive amounts of data may not be so fun. So it would be nice with a setting there we could point it to a self-hosted captcha service. But I personally think keeping RECAPTCHA on main form and giving away data to Google or similar on main page only would be a mitigation better than nothing.
I very recently spent some time redesigning the appearance of the Radicale Web UI (see PR #1329 if you're interested) so fortunately how the UI and web server are implemented is still very fresh in my mind. The Radicale Web UI is just a very simple HTML/JS CarDAV/CalDAV client with few features. The authentication of login from the web UI is handled in the exact same way as any other authentication.
As far as Radicale is concerned, it doesn't know or particularly care if you're logging in with the web UI. No additional authentication is performed by the Radicale WSGI for the web login, and while you could easily add the CAPTCHA to the HTML of the UI (just edit the loginscene element in the radicale/web/internal_data/index.html file) there would be no easy way to implement the server side CAPTCHA checks inside Radicale. But if you did want to go down this path, line 226 of radicale/app/init.py is where I would start.
Another idea could be just using an alternative HTML CalDAV/CarDAV client, implement your own CAPTCHA and checking into that client, and just disable the Radicale UI (option to disable web UI: https://radicale.org/v3.html#web).
This would accomplish what you want with the Web UI, and as you rightly suggested, fail2ban could be used to temporarily block attackers. But to state the point once again, fail2ban would be your entire defence against attacks using any other means other than the HTML Client that you implement.
All of this aside, there are still significant security concerns with using HTTP basic authentication, such as username and passwords being in the URL of every single request (disastrous if not using HTTPS) and clients having to store passwords in plain text so that they can be reused.
Personally I think a good solution that strikes a good balance is the following combination:
If you do end up finding a fail2ban config file for Radicale I would be very appreciative if you share it. Thank you!
But I personally think keeping RECAPTCHA on main form and giving away data to Google or similar on main page only would be a mitigation better than nothing.
As of today at least 4 CAPTCHA services are available, so there is no need to send data to Google:
Thank you for this very simple but also powerful project. I started to use it to host my calendar application AWS. It is publicly accessible. I plan to open-source the configuration / infrastructure as code etc. soon. The direct TLS, and bcrypt hash solutions are great, I see that security is a prioritized concern.
The application is still vulnerable to brute-force of the admin panel to possibly enumerate accounts, crack password. The lack of protection enables automated scanning tools go crazy as well. Can we implement some kind of captcha protection against it?