首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang Gin 实战(九)| JSONP跨域和劫持

Golang Gin 实战(九)| JSONP跨域和劫持

作者头像
飞雪无情
发布2020-02-10 12:18:38
1.6K0
发布2020-02-10 12:18:38
举报

浏览器都遵循同源策略,也就是说位于www.flysnow.org下的网页是无法访问非www.flysnow.org下的数据的,比如我们常见的AJAX跨域问题。

要解决跨域问题的办法有CORS、代理和JSONP,这里结合Gin,主要介绍JSONP模式

JSONP原理

JSONP可以跨域,主要是利用了<script>跨域的能力,因为这个标签我们可以引用任何域名下的JS文件。既然是这样,我们就可以利用这个能力,在服务端生成相应的JS代码,并且把返回的Content-type设置为application/javascript即可。

在生成这个这个对应的JS代码的时候,就比较有讲究了,一般是调用客户端网页已经存在的JS函数。

<script type="text/javascript">
function sayHello(data){
    alert(JSON.stringify(data));
}
</script>

以上我们定义了一个JS函数sayHello,可以通过alert的方式,显示对应的data数据。

假设我们通过http://localhost:8080/jsonp?callback=sayHello来调用sayHello函数,那么我们这个URL输出的内容要是这样:

sayHello({"wechat":"flysnow_org"});

并且对应的Content-type设置为application/javascript,这样才能达到调用sayHello函数的目的,也就是通过JSONP的方式解决了数据跨域读取的问题。

最终,我们的网页的源代码是这样的:

<script type="text/javascript">
function sayHello(data){
    alert(JSON.stringify(data));
}
</script>
<script type="text/javascript" src="http://localhost:8080/jsonp?callback=sayHello"></script>

注意前后顺序,必须要先定义sayHello函数。

Gin JSONP 实现

要通过Gin来实现服务端对JSONP的支持非常简单,只需要使用JSONP函数即可。

func main() {
	r := gin.Default()
	r.GET("/jsonp", func(c *gin.Context) {
		c.JSONP(200, gin.H{"wechat": "flysnow_org"})
	})
	r.Run(":8080")
}

它的使用方法和c.JSON一模一样,第一个参数是HTTP Status Code,第二个是返回的数据。

func (c *Context) JSONP(code int, obj interface{}) {
	callback := c.DefaultQuery("callback", "")
	if callback == "" {
		c.Render(code, render.JSON{Data: obj})
		return
	}
	c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
}

通过上面的源代码,我们发现Gin指定callback参数名作为接收回调函数的参数名,所以我们上面的例子,是通过http://localhost:8080/jsonp?callback=sayHello"sayHello这个回调JS函数传递给服务端,这样服务端才会返回对应的JS类型的字符串,才能被浏览器执行,达到跨域的目的。

sayHello({"wechat":"flysnow_org"});

通过上面的源代码,我们也可以发现,如果我们没有传递callback对应的回调函数,它就会调用c.Render(code, render.JSON{Data: obj}),和我们直接使用c.JSON方法是一样的,直接输出JSON字符串。

JSONP劫持

JSONP的方法其实是不推荐的,因为它的安全性是个问题,也就是JSONP劫持。JSONP劫持其实是JSON劫持的一部分,JSON劫持是包含JSONP劫持的。

JSON劫持还会稍微麻烦一些,因为需要数据是JSON数组,并且要重写JS Array的构建函数;而JSONP,因为有现成的JS回调函数,直接重写JS回调函数就可以了,JSONP让JSON劫持更简单了。

JSON劫持

上面我们介绍了JSON劫持,那么我们如何避免它呢?其实现在的浏览器基本上修复了这个问题,但是我们这里介绍下Gin防止JSON劫持的策略。

JSON劫持,其实就是恶意网站,通过<script>标签获取你的JSON数据,因为JSON数组默认为是可执行的JS,所以通过这种方式,可以获得你的敏感数据,当然条件还有很多,可以Google JSON劫持详细了解。

对于Gin,解决JSON劫持的方法很简单:

    r := gin.Default()
	a := []string{"1", "2", "3"}
	r.GET("/secureJson", func(c *gin.Context) {
		c.SecureJSON(200, a)
	})

运行访问http://localhost:8080/secureJson会发现如下信息:

while(1);["1","2","3"]

最前面有个while(1);前缀,这就可以在<script>标签执行我们返回的数据时,就可以无限循环,阻止后面数组数据的执行,防止数据被劫持。

Gin默认的防JSON劫持的前缀是while(1);,我们可以改变,通过r.SecureJsonPrefix方法设置即可,如:

r := gin.Default()
r.SecureJsonPrefix("for(;;);")

据说Google采用的是while的方法,facebook采用的是for的方法。

小结

虽然Gin对JSONP提供了很好的支持,但是我们并不推荐使用,因为JSONP劫持问题,如果要跨域还是使用代理或者CORS比较好。更多关于Gin的讨论可以加入我的星球Golang Gin 实战,有更深入的讨论,一对一的答疑,公众号和博客没有的源代码分析。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020年1月1日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JSONP原理
  • Gin JSONP 实现
  • JSONP劫持
  • JSON劫持
  • 小结
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档