Clickjacking in Gin with Cockroachdb
Clickjacking in Gin with Cockroachdb — how this specific combination creates or exposes the vulnerability
Clickjacking is a client-side attack where an attacker tricks a user into interacting with a hidden or disguised UI element inside an embedded frame. When a Gin application uses Cockroachdb as its data store, the database itself does not introduce clickjacking, but application design patterns common with Cockroachdb can expose clickjacking risks if defenses are missing. The risk arises when sensitive actions (for example, changing account settings or performing financial transactions) are rendered in pages that do not enforce frame isolation and the backend uses Cockroachdb to serve or update state based on forged requests.
With Cockroachdb, developers often build APIs with Gin that expose user-specific data or admin endpoints. If these endpoints respond to GET requests that perform state changes (a design anti-pattern), an attacker can craft a malicious page that embeds the endpoint in an iframe and overlays invisible controls. Because Cockroachdb supports distributed SQL and is often used in production systems with high integrity requirements, the impact of an authenticated clickjacking attack can be severe: attackers can change user preferences, update billing details, or modify permissions stored in Cockroachdb rows. The vulnerability is not in Cockroachdb but in the Gin handlers that do not verify same-origin context or use anti-CSRF tokens when rendering forms that write to the database.
Consider a Gin route that updates a user’s email by reading a value from a form and persisting it to Cockroachdb. If the form page is served without proper X-Frame-Options or Content-Security-Policy frame-ancestors directives, and the update handler does not validate the Origin header or use anti-CSRF tokens, an attacker can load the user’s settings page in a frame, overlay a transparent form, and capture the email change action. Because Cockroachdb transactions ensure strong consistency, the malicious update will be committed reliably once the user is tricked into submitting the framed page. This combination of a stateful Cockroachdb backend and a Gin handler that does not enforce frame busting or CSRF protection creates a practical clickjacking vector.
Cockroachdb-Specific Remediation in Gin — concrete code fixes
Remediation focuses on HTTP headers, anti-CSRF tokens, and safe handler design in Gin while using Cockroachdb safely for reads and writes. Below are concrete examples that show how to implement protections when interacting with Cockroachdb using the pgx driver and database/sql patterns.
1. Set anti-clickjacking HTTP headers
Ensure every response includes headers that prevent framing. In Gin, use middleware to add headers globally.
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(func(c *gin.Context) {
c.Writer.Header().Set("X-Frame-Options", "DENY")
c.Writer.Header().Set("Content-Security-Policy", "frame-ancestors 'self'")
c.Next()
})
// routes here
r.Run()
}
2. Use anti-CSRF tokens in forms that write to Cockroachdb
Generate and validate per-session CSRF tokens for any form that performs state-changing operations against Cockroachdb. Store the token in the session and include it in forms; verify it on submission.
package main
import (
"github.com/gin-gonic/gin"
"math/rand"
"net/http"
"time"
)
func setCSRFToken() string {
rand.Seed(time.Now().UnixNano())
return string(rune(rand.Intn(1000000)))
}
func main() {
r := gin.Default()
r.Use(func(c *gin.Context) {
if c.Request.URL.Path == "/settings" && c.Request.Method == "GET" {
token := setCSRFToken()
c.SetCookie("csrf_token", token, 3600, "/", "", false, true)
c.HTML(http.StatusOK, "settings.html", gin.H{
"csrfToken": token,
})
c.Abort()
return
}
c.Next()
})
r.POST("/settings", func(c *gin.Context) {
userToken := c.GetCookie("csrf_token")
formToken := c.PostForm("csrf_token")
if userToken != formToken {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "invalid csrf token"})
return
}
var email string
if err := c.Bind(&email); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Use Cockroachdb/sql DB here to update email safely
c.JSON(http.StatusOK, gin.H{"status": "email updated"})
})
r.Run()
}
3. Safe Cockroachdb interaction with parameterized queries
Always use parameterized queries to avoid injection when handling user input that comes from forms protected by anti-CSRF measures.
package main
import (
"context"
"database/sql"
"log"
"net/http"
"os"
_ "github.com/jackc/pgx/v5/stdlib"
)
func main() {
dsn := os.Getenv("COCKROACH_DSN")
db, err := sql.Open("pgx", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
http.HandleFunc("/update-email", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
email := r.FormValue("email")
userID := r.FormValue("user_id")
// Use parameterized query to prevent injection
_, err := db.ExecContext(r.Context(),
"UPDATE users SET email = $1 WHERE id = $2",
email, userID)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write([]byte("email updated"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
4. Avoid GET endpoints that perform writes
Ensure handlers that change state in Cockroachdb only respond to POST, PUT, or DELETE. Use Gin’s method-specific routing to enforce this and reduce clickjacking risk.
r := gin.Default()
r.GET("/user/:id", getUserHandler) // read-only
r.POST("/user/:id/update-email", updateEmailHandler) // write-protected