json start
json 是什么
json 是一种数据交换格式。
(数据交换格式是一种在不同平台间传递数据的文本格式。)
- json 独立于编程语言
- 全称是JavaScript Object Notation<span style="color:#be2532">(JavaScript 对象表示法)</span>
- 关于这个名称,很容易造成误解,虽然 json 源于 JavaScript ,但是 json 是通用的数据交换格式,可以用在不同的系统,不同的语言中。
- <span style="color:#be2532">json 是源于 JavaScript 字面量的。</span>
- 所以,我们可以这么理解 json :<u>一种<span style="color:#be2532">基于对象表示法的数据交换格式</span></u>
- 对象,是面向对象编程中很常见的概念。
- 而不太容易理解的是表示法,<span style="color:#be2532"><u>表示法是指一个可以表示如数字或单词等数据的字符系统</u>。</span>
- json 之所以流行,是因为其不仅独立于语言,而且使用了一种在许多编程语言中都能找到共同元素的表达方式。
- 通过这种表达数据的方式,即便是那 些不支持面向对象编程的语言,也会发现 json 这种格式是可以接受的。
json的文件,媒体类型
- json 文件的后缀就是
.json
- json 的媒体类型(MIME)为
application/json
json 语法
示例
json 所基于的 JavaScript 对象字面量单纯指对象字面量及其属性的语法表示,而且<u>这种属性表示方法也就是通过名称 - 值对来实现的。</u>
<span style="color:#be2532">json 的最外层为花括号{ },表示一个对象,而括号里面,都是由被称为名称-值对(键值对)组成。</span>
{
"animal":"horse"
}
上面这个就是最简单的一段 json 数据。
正确的 json 语法
- 先来看看:名称-值对中的名称。
在,json 中名称即上面的"animal"
,必须- 双引号包裹
- 而且是有效的字符串
- 最好是英文字母 A~Z 或 a~z。
- 多个名称-值对用逗号隔开
json 的数据类型
上面说了,<span style="color:#be2532">名称-值对中的<u>名称为双引号包裹的字符串</u></span>,而 json 的数据类型指的是值对的数据类型。
包括:
1. 对象
- 对象可以嵌套
2. 字符串
- 可能需要用`\`转义
3. 数字
- 整数、小数、负数或者指数
4. 布尔值
- 格式非常严格,只能使用小写的 true,false
5. null
- 表示没有值,必须为小写的 null
6. 数组
- []包裹,值之间用逗号隔开
json 中的字符类型
重点要说的是字符串类型,在 JavaScript 中,使用单引号或双引号没有任何区别,但是 <span style="color:#be2532"><u>json 只是基于 JavaScript 对象字面量</u>。 而<u>在 json 中,仅允许使用双引号来包裹字符串。</u></span>
另外,<u>json 中的字符串还涉及到一个转义的问题</u>,即解析器在解析 json 时,当读到一个双引号 “ 时,就认为接下来是一个字符串文本,并且预期有另一个双引号 “ 结尾,这就意味着,如果这段字符串本身含有双引号,那么就有可能会解析错误。
为了处理这个问题,我们需要 <u>在字符串中的双引号前面加上一个反斜线字符来对其转义</u>。
另外还有其他一些需要转义的字符:
• \\(反斜线)
• \/(正斜线)
• \(退格符)
• \(换页符)
• \(制表符)
• \(换行符)
• \(回车符)
• \u后面跟十六进制字符(如笑脸表情\u263A)
json 中的数字可以是`整数、小数、负数或者指数。
json 中的布尔类型非常严格,<u>仅使用小写形式:true 或 false,任何其他 形式的写法都会报错。</u>
json 中的null类型,编程中当我们需要表示没有,不存在,空,一无所有等等意思的时候,我们不用数字0表示,而是 null ,null 是一个表示“没有值”的值。在 json 中,<u>null 必须使用小写形式。</u>
json 中同一个数组里,可以放置不同类型的数据,但是尽量避免这么做,数组里的值类型可以包括:<u>字符串、数字、布尔值、对象 或数组中的任何一种</u>,数组必须被方括号
[]
包裹,且值与值之间用 逗号隔开。
json 数据的一致性验证
发送方和接收方都可以使用 json Schema 来校验数据。
- 对于发送方来说,在发送数据前,随时可以验证数据是否与 Schema 一致,以便知道会不会被接收方接受。如果出现错误,也可以轻松定位和修复。
- 对于接收方来说,可以将 json Schema 放在接收数据的第一行,以保证数据符合要求。主要在数据被处理前回答三个问题。
- 2.1 <span style="color:#be2532"><u>值的数据类型是否正确?</u></span>
- 可以具体规定一个值是数字、字符串等类型。
- 2.2 <span style="color:#be2532"><u>是否包含所需要的数据?</u></span>
- 可以具体规定哪些数据是需要的,哪些是不需要的。
- 2.3 <span style="color:#be2532"><u>值的形式是不是我需要的?</u></span>
- 可以指定范围、最小值和最大值。
json Schema
<span style="color:#be2532">json Schema 也是使用 json 来书写。</span>
一、第一个名称 - 值对中,声明其为一个 schema 文件。声明的名称必须为 “$schema”,值必须为所用草拟版本的链接。
二、 第二个名称 - 值对应该是 json Schema 文件的标题。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Cat"
}
三、 第三个名称值对中,要定义需要在json中包含的 属性。例如定义猫的属性。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Cat",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number",
"description": "Your cat's age in years."
},
"declawed": {
"type": "boolean"
}
}
}
在第三个名称 - 值对中,我们验证了第一个问题,<u>值的类型是否正确。</u>
如果需要验证第二个问题–<u>是否包含所需要的数据。</u>就需要第四个名称 - 值对。
四、 第四个名称 - 值对,<u>名称为 “require”,值为数组,数组中包含必填的字段</u>。
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Cat",
"properties": {
"name": {
"type": "string"
},
"age": {
"type": "number",
"description": "Your cat's age in years."
},
"declawed": {
"type": "boolean"
},
"description": {
"type": "string"
}
},
"require": [
"name",
"age",
"declawed"
]
}
下面是一个合法的 json:
{
"name": "Fluffy",
"age": 2,
"declawed": false,
}
注:
如果你的 json Schema 中<u>不包含 “require” 名称-值对,那么将不会有必填项。</u>
下面解决第三个问题:值的形式是不是我需要的?
这个可以通过<span style="color:#be2532"><u>对数据的格式提出更具体的要求来实现</u></span>,比如设置数字的最大、最小值,字符串长度的最长最短等等。
下面是示例:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Cat",
"properties": {
"name": {
"type": "string",
"minLength": 3,
"maxLength" : 20
},
"age": {
"type": "number",
"description": "Your cat's age in years.",
"minimum" : 0
},
"declawed": {
"type": "boolean"
},
"description": {
"type": "string"
}
},
"requi#be2532": [
"name",
"age",
"declawed"
]
}
json Schema 同时还支持正则表达式和枚举类型
- 正则表达式
- 一种字符形式
- 枚举类型
- 一个 包含所有可能值的列表
json 中的安全问题
json 本身并不会造成什么安全问题,可能会出现问题的是 json 的使用。
主要是两点:
- <span style="color:#be2532"><u>跨站请求</u></span>
- <span style="color:#be2532"><u>跨站脚本攻击</u></span>
跨站请求伪造
跨站请求伪造,即 CSRF(cross-site request forgery,读作 sea-surf),是一种 <span style="color:#be2532"><u>利用站点对用户浏览器信任而发起攻击的方式。</u></span>
那 json 和 CSRF 攻击又有什么联系呢?
下面这个看上去不像 json 的 json 数据<u>被称为<span style="color:#be2532">顶层 json 数组</span>,是<span style="color:#be2532">可以作为被执行的 JavaScript 脚本。</span></u>
[
{
"user": "bobbarker"
},
{
"phone": "555-555-5555"
}
]
因为浏览器对于跨域名的资源共享有一定的限制,所以,黑客通常使用 <script>
标签来绕开这些规则(<script>可以引用其他站点的脚本
),<span style="color:#be2532">使用顶层级别数组的 json 是合法的 JavaScript 代码</span>
那如何防范这种跨站请求伪造呢?
一、 首先<span style="color:#be2532">不在 json 中使用顶级数组,应该将数组作为一个值存入 json 对象。这样的数组将不再是合法的 JavaScript。</span>
{
"info": [
{
"user": "bobbarker"
},
{
"phone": "555-555-5555"
}
]
}
二、网站,<span style="color:#be2532">应仅允许 POST 请求获取数据,禁止使用 GET 请求</span>
注入攻击
CSRF(跨站请求伪造)攻击仅包括利用信任机制进行的攻击。<span style="color:#be2532">注入攻击则主要通过向网站注入恶意代码</span>来实现。
一、 跨站脚本攻击
跨站脚本攻击(cross-site scripting,XSS)是注入攻击的一种。<u>在使用 json 时常见的安全漏洞通常发生在 JavaScript 从服务器获取到一段 json 字符串并将其转化为 JavaScript 对象时。</u>
要知道,json 本身仅仅是文本。<u>在编程中,<span style="color:#be2532">如果想要对代表对象的文本进行操作,首先要将它转换成对象并装入内存中。</span>这样,它才能被操作、观察,并在程序逻辑中使用。</u>
在 JavaScript 中,可以<span style="color:#be2532">使用 eval()
函数来进行这一操作。该函数<u>获取一段字符串,并对其进行编译与执行</u></span>
示例:
1. var jsonString = '{"animal":"cat"}';
2. var myObject = eval("(" + jsonString + ")");
3. alert(myObject.animal);
使用 eval()
函数来将 animal/cat
对象放入内存中。之后就可以在代码中使用该对象的属性了。
代码相对来说没有什么危险,因为 json 是直接位于代码中的。不过在一些情况下,我们的 json 是从别的服务器上获取的。如果服
务器本身或发来的 json 数据被人劫持,那么很可能就会运行恶意代码。
<span style="color:#be2532"><u>eval()
函数的问题是,它会将传入的字符串无差别地编译执行。</u></span>如果从第三方服务器中获取的 json 被替换为了恶意脚本,那么我的站点就会无辜地蒙冤,在访问者的浏览器中编译执行恶意代码。
如果用一些 JavaScript 代码来替换原本的 json 字符串。
1. var jsonString = "alert('this is bad')";
2. var myObject = eval("(" + jsonString + ")");
3. alert(myObject.animal);
执行结果是,直接弹出 this is bad
<span style="color:#be2532">JSON.parse()
函数</span>就是用来处理这一问题的。<u>该函数仅会解析 json,并不会执行脚本。</u>
1. var jsonString = '{"animal":"cat"}';
2. var myObject = JSON.parse(jsonString);
3. alert(myObject.animal);
JSON.parse()
函数更加安全,目前已经被全部主流浏览器的最新版本所支持。
找出可能的注入点")找出可能的注入点
对于一些合法的 json 数据,也可能包含一些危险。
比如 json 中可能包含了 JavaScript 脚本:
{
message": "hover here."
}
这段 JavaScript,当它在网站的消息页面中输出时,就会构成威胁。示例中的这段“不规矩的 json”会在用户每次将鼠标移动至
屏幕上的消息时弹出内容为“gotcha!”的警告框。
当然,如果是黑客攻击,不是简单的弹出一个警告框这么简单了,可能会将你的私人信息发送到他的服务器。
如何阻止这种情况呢?
一方面,<span style="color:#be2532">可以采取一些手段使得消息中不包含 HTML。</span>可以在客户端和服务端都加上这一认证。
-
此外,<span style="color:#be2532">还可以将消息中所有的HTML字符进行转码,</span>这样的话,诸如
<div>
这样的标签就会被转换成<div>
,然后插入页面(<div>
将不会是合法的 HTML)。总之,<u>抵御注入攻击的关键是<span style="color:#be2532">要找出可能的注入点</span>,并加入一些额外的步骤(有时可能会很麻烦)来加以防范。</u>
总结:
JSON 本身不构成什么威胁,它只是文本。
在定位 JSON 安全问题时,应该记住以下三件事。
1. 不要使用顶级数组。顶级数组是合法的 JavaScript 脚本,它们可以用
<script> 标签链接并使用。
2. 对于不想公开的资源,仅允许使用 HTTP POST 方法请求,而不是 GET
方法。GET 方法可以通过 URL 来请求,甚至可以放在 <script> 标签中。
3. 使用 JSON.parse() 来代替 eval()。eval() 函数会将传入的字符串编
译并执行,这会让你的代码易被攻击。应仅使用 JSON.parse() 来解析 JSON 数据。
安全漏洞通常是由于开发人员没有考虑“黑客如何利用这一点”这一问
题所造成的。
JavaScript中的 XMLHttpRequest 与 Web API
<u>JavaScript 中的 <span style="color:#be2532">XMLHttpRequest</span> 负责<span style="color:#be2532">在客户端发起请求</span>。而 <span style="color:#be2532">Web API</span> 负责<span style="color:#be2532">在服务端返回响应。</span></u>
Web API
<span style="color:#be2532">Web API 是通过 HTTP 服务进行交互的一组指令和标准。</span>这些交互可以包括创建、读取、更新、删除(CRUD)等操作,且 Web API 都会有一份说明,概述如何使用这些指令和标准。
这里要提到 JavaScript 中的异步(后台)操作– AJAX。AJAX这个专有名词使用的时间已经很久了,很多
时候,与其说它是一个首字母缩写,不如说是用来描述 JavaScript 中的任何一种异步操作。
接下来看看如何用 json 和 JavaScript 中的 XMLHttpRequest 实现 AJAX。
JavaScript中的XMLHttpRequest对象
尽管 JavaScript 的 XMLHttpRequest 对象听上去和 XML 有关,但实际上我们使用它来发起 HTTP 请求。然而,<span style="color:#be2532">XMLHttpRequest 并不仅限于使用 XML。</span>
当使用 new XmlHttpRequest()
语法,并将其返回值赋值给一个变量时,<u>它就具有了从某一地址请求资源的功能。</u>
var myXmlHttpRequest = new XMLHttpRequest();
我们说过,JavaScript对象具有属性。这些“属性”就是名称-值
XMLHttpRequest对象所拥有的属性有 onreadystatechange、readyState、 response、 responseText、 responseType、 responseXML、 status、 statusText、timeout、ontimeout、upload 以及 withCredentials
。
XMLHttpRequest 中还包含以下这些可用的函数:
* open(method,url,async(可选),user(可选),password(可选))
* send()
以及下面这些属性:
* onreadystatechange
可以在代码中给它赋值为一个函数
* readyState
返回一个 0~4 的值,用来表示状态码
* status
返回 HTTP 状态码(如 200 表示请求成功)
* responseText
当请求成功时,该属性会包含作为文本的响应体(如我们请求的 JSON)
属性的值可以是一个函数。因为 JavaScript 中的函数也是一类对象。对象是一类数据,因此它可以被赋值给一个变量(属性)、修改和传递。在编程中,这种情况称为“函数是一等公民”。
var myXMLHttpRequest = new XMLHttpRequest();
var url = "http://api.openweathermap.org/data/2.5/weather?lat=35&lon=139";
myXMLHttpRequest.onreadystatechange = function() {
if (myXMLHttpRequest.readyState === 4 && myXMLHttpRequest.
status === 200) {
var myObject = JSON.parse(myXMLHttpRequest.responseText);
var myJSON = JSON.stringify(myObject);
}
}
myXMLHttpRequest.open("GET", url, true);
myXMLHttpRequest.send();
上面这段代码当请求成功,返回 json 文本时,涉及到一个序列化和反序列化问题。
- <span style="color:#be2532">序列化</span>是将<span style="color:#be2532">对象转换成文本的过程</span>
- 在 JavaScript 中,可以通过 <span style="color:#be2333">
JSON.stringify()
对 json 进行序列化。</span>
- 在 JavaScript 中,可以通过 <span style="color:#be2333">
- <span style="color:#be2532">反序列化</span>是将<span style="color:#be2532">文本转换成对象的过程。</span>
- JavaScript 中的 <span style="color:#be2333">
JSON.parse()
进行反序列化操作</span>
- JavaScript 中的 <span style="color:#be2333">
突破同源策略
然而,出于安全考虑,浏览器对资源共享有一定的限制。同源策略就要求此类后台请求仅可以请求来自同一域名的资源。
跨域资源共享CORS(cross-origin resource sharing)
这是因为这些公共 API 的开发者在他们的服务器上实现了<span style="color:#be2532">跨域资源共享(cross-origin resource sharing,CORS)。</span>这些服务器会在响应头额外加上一些带有 Access-Control-Allow
前缀的属性
1. Access-Control-Allow-Credentials:true
2. Access-Control-Allow-Methods:GET, POST
3. Access-Control-Allow-Origin:*
这些头部定义了证书是否可用,哪些 HTTP 方法(GET、POST、PUT、DELETE、HEAD、OPTIONS、TRACE、CONNECT)是可用的,以及最重要的一点,即允许哪些域名。本例中,此处包含一个星号(*)。它表示任意域名都是允许的。
同时,<span style="color:#be2532">CORS 还可用于禁止某些域名的访问</span>,来防止有些人用 API 做坏事。
1. Access-Control-Allow-Methods:POST
2. Access-Control-Allow-Origin:http://www.somebank.com
本例中,我们仅允许通过 POST 方式请求资源。如果有人想通过将 URL 放
入 <script>
标签来进行请求(使用 GET 方式),浏览器会阻止他。同时,由于具体设定了银行站点的 URL,浏览器会禁止除 http://www.somebank.com 以外的站点去获取资源。
JSON-P
JSON-P 指带有 padding 的 JSON。我在第 5 章提到过 <u><script>
标签不受同源策略的影响。JSON-P 就是利用这一点来向不同域名的站点请求 JSON 的。</u> JSON-P 并没有CORS那么理想,它只是一个备选方案(CORS是更好的方案,同时也是一种标准)。不过有些时候,还是很需要这种不是很规范的解决方案的。
getTheAnimal(
{
"animal": "cat"
}
);
<u>内联于 json 文档的 JavaScript 调用了一个函数,函数参数是 json 。</u>函数参数提供了一种将数据传递给函数的方式。