Logo
thumbnail

Securing HTMX Applications

Writing your applications in HTMX doesn't necesarily have a lot of security benefits over competing frameworks (React, Vue, Svelte) on the surface as each of these frameworks give you the tools (Content-Security-Policy, nonces and hashes for your forms, etc). But it does simplify securing your application a lot due to the opinions of the framework. We are going to go over the opinons of HTMX that make things simple as well as what you still must secure with our backend. For this article I will be using the Nginx web server and Django as my backend.

Same Origin

One of the hard requirements of HTMX is that the server that serves your pages must also proxy your restful backend. This is because HTMX only supports fetching via URI and not a full domain. You can use the wsgi protocol on your Nginx server to give it the capabilities to proxy your Django application. I will write more about how this all works in a separate article as the library I use uwsgi has a lot of capabilities.

Having the same origin as the backend server gives you the ability to lock down your backend server with less configuration

It is assumed that the web server you are hosting your HTMX files will have the same origin as your backend. This means that you can set up your backend service to only accept requests using the same origin as your frontend and not have to deal with CORS. There will still be configurations needed to handle Cross-Site Request Forgery however your backend handles that. In Django there is the CSRF_DOMAINS and the ALLOWED_DOMAINS settings that you must set in your settings.py file. If you look below, I provide an example that pulls the domain from an environment variable named DOMAIN.

ALLOWED_DOMAINS = [
    os.environment.get("DOMAIN")
]

CSRF_DOMAINS = [
    os.environment.get("DOMAIN")
]

Helpful: environment variables are helpful when we want to set a value, but not hardcode it when deploying our application. This is particularly useful when you make an application that is self-hosted or when deploying many different environments (test, staging, and production).

Content-Security Policy Configuration

Content-Security-Policy is a header that tells the browser which domains to trust for styles, javascript code, and for the fetch client when we perform api requests. This protects our visitors from being the target of a cross-site scripting attack. The code below is a good starting point for those using nginx to serve their website. It adds a Content-Security-Policy header that uses 'self' for the default-src and img-src directives. This means that all images must come from the same origin server. While we have the cdn for htmx in the style-src-elem and script-src-elem directives. This allows htmx to load javascript and css files.

add_header Content-Security-Policy "default-src 'self'; img-src 'self' ; style-src-elem 'self' https://unpkg.com/htmx.org@2.0.3; script-src-elem 'self' https://unpkg.com/htmx.org@2.0.3;";

Disabling HTTP Methods

Disabling unused HTTP Methods can reduce the attack surface of your servers. If you have a blog that only supports the GET method, it is safe to remove access to HEAD, POST, CONNECT, PUT, and DELETE so that nobody can incorporate it in an attack. You can add the code below to a server or location block to accomplish this.

if ($request_method !~ ^(GET)$ ) {
    return 405;
}

For location blocks that proxy a backend server, you will want to allow the required HTTP methods through so that your service can operate properly. This involves granting acces to GET, POST, PUT, and HEAD http methods. DELETE should never be allowed through as any misconfiguration or oversight might risk data being deleted accidentally or maliciously.

if ($request_method !~ ^(GET|HEAD|POST|PUT)$) {
    return 405;
}

Setting Up SSL Certificates

You can set up SSL certificates to encrypt the traffic between your website visitors and your web server easily and for free with certbot. Certbot simplifies getting your SSL certificate properly configured on your webservers and will rotate them on a schedule so you never leave your users unencrypted. You will only need to have the one certificate configured at the server block level. We won't need one for django as long as your network is secure and doesn't allow users to access the service directly. You can add certbot using the command below.

apt install certbot -y

After installing I normally just use the autodetect feature of certbot. It detects my nginx server, confirms that you own the domain by looking for a DNS record is pointing at your server's ip address, and downloads the certificates and configures nginx to use them.


Web Application Firewalls

A Web Application Firewall (WAF) is a piece of software that allows you to define policies that analyze traffic coming into your server and protect it against the traffic that is deemed malicious. A couple of options that you can look into are modsecurity and open appsec. Both will protect your services against most of the layer 7 attacks including sql injection, cross-site script, local file includes, and more. I will also be doing more in-depth content on both of these WAF technologies as I use them more.