JSON 劫持是一种安全风险,攻击者可以通过 script 标签获取敏感数据。Gin 提供了 SecureJSON 来防护。
攻击者可以在恶意网站中这样获取数据:
<script>
function steal(data) {
// 发送数据到攻击者服务器
}
</script>
<script src="https://victim.com/api/user.json"></script>
如果返回的是数组 [{"name":"admin"}],某些旧浏览器会把它当作合法的 JS 执行。
Gin 的 SecureJSON 会在响应前添加 while(1);:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/secure", func(c *gin.Context) {
c.SecureJSON(http.StatusOK, []gin.H{
{"name": "张三", "email": "zhangsan@example.com"},
{"name": "李四", "email": "lisi@example.com"},
})
})
r.Run(":8080")
}
响应内容:
while(1);[{"email":"zhangsan@example.com","name":"张三"},{"email":"lisi@example.com","name":"李四"}]
攻击者通过 script 标签加载时,while(1); 会造成死循环,阻止数据被窃取。
前端需要去掉前缀再解析:
fetch('/secure')
.then(response => response.text())
.then(text => {
const json = text.replace(/^while\(1\);/, '');
return JSON.parse(json);
})
.then(data => console.log(data));
可以自定义防护前缀:
func main() {
r := gin.New()
r.GET("/custom", func(c *gin.Context) {
c.SecureJSON(http.StatusOK, gin.H{
"message": "hello",
})
})
r.Run(":8080")
}
SecureJSON 主要用于:
对于普通 API,使用 c.JSON() 即可:
r.GET("/normal", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "普通响应",
})
})
确保非 ASCII 字符被转义:
r.GET("/ascii", func(c *gin.Context) {
c.AsciiJSON(http.StatusOK, gin.H{
"name": "张三",
"city": "北京",
})
})
响应:
{"name":"\u5f20\u4e09","city":"\u5317\u4eac"}
不转义 HTML 特殊字符:
r.GET("/pure", func(c *gin.Context) {
c.PureJSON(http.StatusOK, gin.H{
"html": "<b>bold</b>",
"script": "<script>alert(1)</script>",
})
})
支持跨域 JSONP 请求:
r.GET("/jsonp", func(c *gin.Context) {
c.JSONP(http.StatusOK, gin.H{
"message": "jsonp response",
})
})
访问 /jsonp?callback=myCallback,返回:
myCallback({"message":"jsonp response"});
func main() {
r := gin.Default()
r.Use(func(c *gin.Context) {
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-Frame-Options", "DENY")
c.Header("X-XSS-Protection", "1; mode=block")
c.Next()
})
r.GET("/api/users", func(c *gin.Context) {
users := []gin.H{
{"id": 1, "name": "张三"},
{"id": 2, "name": "李四"},
}
c.SecureJSON(http.StatusOK, users)
})
r.GET("/api/profile", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"name": "张三",
"email": "zhangsan@example.com",
})
})
r.Run(":8080")
}
SecureJSON 是防止 JSON 劫持的简单有效方法。对于返回敏感数据或数组类型的 API,建议使用 SecureJSON。配合其他安全响应头,可以更好地保护 API 安全。