Injection

Injection and Go (Gin) remediation guide [April 2026] [CVE-2026-39340]

[April 2026] Updated CVE-2026-39340

Overview

The CVE-2026-39340 incident demonstrates how a SQL injection flaw in a real-world web app can expose sensitive data even when input is filtered for HTML. In ChurchCRM, before version 7.1.0, user-supplied values from Name and Description fields were concatenated directly into raw INSERT and UPDATE queries without proper SQL escaping, enabling authenticated non-admin users with MenuOptions to perform time-based blind injections and exfiltrate data such as password hashes. The root cause was a sanitation step that removed HTML but did not neutralize SQL control characters, coupled with building SQL statements via string concatenation. This is a canonical example of CWE-89 (SQL Injection) and highlights why relying on escaping or HTML-sanitization alone is insufficient for SQL correctness and security in web apps.

Affected Versions

ChurchCRM < 7.1.0 (CVE-2026-39340)

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "database/sql"
  "fmt"
  "log"
  "net/http"

  "github.com/gin-gonic/gin"
  _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func initDB() {
  var err error
  // Replace with real DSN for your environment
  db, err = sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
  if err != nil {
    log.Fatal(err)
  }
  if err = db.Ping(); err != nil {
    log.Fatal(err)
  }
}

type Input struct {
  Name        string
  Description string
}

// Vulnerable: concatenates input directly into SQL (demonstration only)
func vulnerableCreatePropertyType(c *gin.Context) {
  name := c.PostForm("name")
  desc := c.PostForm("description")
  sqlStr := fmt.Sprintf("INSERT INTO property_types (name, description) VALUES ('%s', '%s')", name, desc)
  if _, err := db.Exec(sqlStr); err != nil {
    c.String(http.StatusInternalServerError, `db error`)
    return
  }
  c.Status(http.StatusCreated)
}

// Fixed: parameterized query to prevent SQL injection
func fixedCreatePropertyType(c *gin.Context) {
  name := c.PostForm("name")
  desc := c.PostForm("description")
  sqlStr := `INSERT INTO property_types (name, description) VALUES (?, ?)`
  if _, err := db.Exec(sqlStr, name, desc); err != nil {
    c.String(http.StatusInternalServerError, `db error`)
    return
  }
  c.Status(http.StatusCreated)
}

func main() {
  initDB()
  r := gin.Default()
  r.POST("/vulnerable", vulnerableCreatePropertyType)
  r.POST("/fixed", fixedCreatePropertyType)
  if err := r.Run(":8080"); err != nil {
    log.Fatal(err)
  }
}

CVE References

Choose which optional cookies to allow. You can change this any time.