Clickjacking in Chi with Mutual Tls
Clickjacking in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side attack that tricks a user into interacting with invisible or disguised UI elements while authenticated to an application. In Chi, a Go HTTP router commonly used for APIs and web services, clickjacking typically arises when responses do not enforce frame-embedding protections. Adding Mutual TLS (mTLS) changes the threat model: the server now also verifies the client’s certificate, which ensures the client is known and trusted for transport-layer identity. However, mTLS does nothing to prevent a compromised or malicious browser from embedding the server’s protected endpoints inside an invisible frame. An authenticated client with a valid certificate can still be lured to a malicious site that performs click interactions via iframes and pointer events. Because mTLS confirms client identity, an attacker may leverage that trusted session to perform privileged actions on behalf of the client, such as changing settings or initiating transactions, without the server detecting unauthorized UI interaction. The server sees a legitimate mTLS-authenticated request, so traditional UI-level defenses like CSRF tokens can be mistakenly assumed sufficient, but they do not mitigate UI redressing. Consequently, the combination of Chi routing with mTLS can expose clickjacking risks if frame-ancestors and related browser protections are not explicitly enforced in HTTP response headers or within application logic.
Mutual Tls-Specific Remediation in Chi — concrete code fixes
Remediation requires both transport-layer mTLS configuration in Chi and explicit UI-level defenses. On the server side, enforce mTLS in Chi by configuring TLS with client certificate verification and by validating the presented certificate details before processing requests. On the client side, prevent framing by setting Content-Security-Policy frame-ancestors, and include X-Frame-Options and X-Content-Type-Options headers. Below are concrete examples for a Chi application in Go that enforces mTLS and adds security headers to mitigate clickjacking.
Mutual TLS setup in Chi
Configure Chi with a TLS configuration that requests and verifies client certificates. The server loads its own certificate and private key, and specifies a trusted CA pool to validate incoming client certificates.
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
// Load server certificate and key
cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
if err != nil {
panic(err)
}
// Load and parse the CA that issued client certificates
caCert, err := ioutil.ReadFile("ca.crt")
if err != nil {
panic(err)
}
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCert); !ok {
panic("failed to add CA cert")
}
// Configure TLS with client certificate verification
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caCertPool,
InsecureSkipVerify: false,
}
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Secure)
// Example protected endpoint
r.Get("/transfer", func(w http.ResponseWriter, r *http.Request) {
// The request is already mTLS-authenticated at this point
// Additional application-level authorization should still apply
w.Write([]byte("secure transfer endpoint"))
})
server := &http.Server{
Addr: ":8443",
Handler: r,
TLSConfig: tlsConfig,
}
// ServeTLS requires server.crt and server.key
http.ListenAndServeTLS(server.Addr, "server.crt", "server.key", server)
}
Clickjacking defenses in Chi handlers
Even with mTLS, include security headers in responses to prevent framing. You can add these headers via Chi middleware or in a middleware wrapper.
func SecurityHeadersMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Prevent framing by any origin
w.Header().Set("Content-Security-Policy", "frame-ancestors 'self'")
// Legacy browser protection
w.Header().Set("X-Frame-Options", "DENY")
// Prevent MIME sniffing
w.Header().Set("X-Content-Type-Options", "nosniff")
next.ServeHTTP(w, r)
})
}
func main() {
r := chi.NewRouter()
r.Use(SecurityHeadersMiddleware)
// ... routes and mTLS configuration as above
}
These headers ensure that browsers do not render the application’s pages inside iframes on other origins, directly mitigating clickjacking. Combine this with per-route or global CSRF tokens for state-changing operations to provide defense-in-depth when mTLS is used for client authentication.