API安全学习手册:Restful API

Restful API

REST是一种风格,而不是一种协议或者标准(RESTful)

REST以资源为核心,所有事物,如果有被引用的必要性,就是资源;资源既可以是实体,也可以是抽象概念

RESTful API由后台也就是SERVER来提供前端来调用。前端调用API向后台发起HTTP请求,后台响应请求将处理结果反馈给前端。也就是说RESTful 是典型的基于HTTP的协议。那么RESTful API有哪些设计原则和规范呢?

1、资源。首先是弄清楚资源的概念。资源就是网络上的一个实体,一段文本,一张图片或者一首歌曲。资源总是要通过一种载体来反应它的内容。文本可以用TXT,也可以用HTML或者XML、图片可以用JPG格式或者PNG格式,JSON是现在最常用的资源表现形式。

2、统一接口。RESTful风格的数据元操CRUD(create,read,update,delete)分别对应HTTP方法:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源,这样就统一了数据操作的接口。

3、URI。可以用一个URI(统一资源定位符)指向资源,即每个URI都对应一个特定的资源。要获取这个资源访问它的URI就可以,因此URI就成了每一个资源的地址或识别符。一般的,每个资源至少有一个URI与之对应,最典型的URI就是URL。

4、无状态。所谓无状态即所有的资源都可以URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而变化。

有状态和无状态的区别,举个例子说明一下,例如要查询员工工资的步骤为:

第一步:登录系统。

第二步:进入查询工资的页面。

第三步:搜索该员工。

第四步:点击姓名查看工资。

这样的操作流程就是有状态的,查询工资的每一个步骤都依赖于前一个步骤,只要前置操作不成功,后续操作就无法执行。如果输入一个URL就可以得到指定员工的工资,则这种情况就是无状态的,因为获取工资不依赖于其他资源或状态,且这种情况下,员工工资是一个资源,由一个URL与之对应可以通过HTTP中的GET方法得到资源,这就是典型的RESTful风格。

REST依赖于HTTP协议

REST API中的资源就是URI,这些URI遵循一定的规范或者约束,具体样例见:REST资源命名指南 | RESTful API 中文网

在REST API中,URI只用来表示资源,而不能表示对这一资源的任何操作;对资源的操作应该从HTTP方法中加以区分:

HTTP GET <http://api.example.com/device-management/managed-devices> //Get all devices

HTTP POST <http://api.example.com/device-management/managed-devices> //Create new Device

HTTP GET <http://api.example.com/device-management/managed-devices/{id}> //Get device for given Id

HTTP PUT <http://api.example.com/device-management/managed-devices/{id}> //Update device for given Id

HTTP DELETE <http://api.example.com/device-management/managed-devices/{id}> //Delete device for given Id

有时会遇到对某些特定资源进行筛选、限制、排序等要求,合理的解决方案是在一个API中使用参数加以对不同要求的区分:

<http://api.example.com/device-management/managed-devices>

<http://api.example.com/device-management/managed-devices?region=USA>

<http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ>

<http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ&sort=installation-date>

在rest中会通过向服务器提交的请求类型来表示增删改查这些操作

  • GET(SELECT):从服务器取出资源。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源。
  • DELETE(DELETE):从服务器删除资源。
     

oData

OData是Open Data Protocol的缩写,是一种基于REST的数据访问方式,该标准由微软发起,前三个版本1.0、2.0、3.0都是微软开放标准,第版本4.0于2014年在OASIS投票通过成为开放工业标准。有关OData的的协议的详细介绍,可以访问www.odata.org 。

OData由两部分组成,也即“格式(format) + 协议(protocol)”。格式定义了如何描述数据,协议定义了如何操作数据。因此,可以将OData理解为一种“接口协议”,它规定了数据格式以及数据的操作标准。OData服务广泛应用于各种场景的集成,在SAP产品中,SAP Fiori,SAP JAM,SAP Netwear Protal以及在SAP Cloud Platform上都广泛地使用了OData技术。

oData是一种Restful风格API的一种标准化协议,为了补充Restful API在通用方面的欠缺所提出,其组成:

  • 模型:定义数据结构,一般在后端
  • 协议:支持CRUDQ,数据传输为XML或者JSON
  • 客户端库:非必须的,保证客户端可以使用库函数访问oData的服务
  • 服务:被访问的服务端

$filter运算符

  • eq 等于
  • ne 不等于
  • gt 大于

$oderby 排序:host/userInfo?$orderby=name desc

$top / $skip / $inlinecount客户端换页: host/usetInfo?top=5&$skip=1&$inlinecount=allpages

$count数据量 / $expand嵌入内容 / $format格式化 / $select查询字段的列表

oData至少向外暴露的接口:

  • host/: 获取实体列表
  • host/$metadata 获取实体元数据
  • host/$id 对应实体的数据,可结合查询参数 ?$select=name&$filter=id eq 1 and age gt 20

OData的服务结构包括:

  • 服务文档(Service Document):描述了OData Service中可用的数据的概览信息,也即OData中所含的Entity Set信息。
  • 服务元结构文档(Service Metadata Document):在元结构文档中,描述了OData服务中的数据类型及其相关的属性,也即Entity Type和Property。

在以上两种文档中包含了:

  • 实体集合(Entity Set):等同于RSS中的Feed节点,是某一种信息的集合
  • 实体(Entity):等同于RSS中的Entry节点,描述了具体某一条目信息的内容 
  • 实体类型(Entity Type):表明实体的类型,每一个实体都对应一个Entity Type
  • 属性(Property): 实体具体的属性
  • 导航属性(Navigation Property):用于描述层级关系,例如Category - Sub-category这种关系
  • 关联(Association):描述关联关系

示例:可以访问以的OData的示例文件 - 

  • 访问OData的Service Document:https://services.odata.org/V3/Northwind/Northwind.svc/
  • 访问OData的Medata Document:https://services.odata.org/V3/Northwind/Northwind.svc/$metadata
  • 访问OData中具体的某一个Entity Set:https://services.odata.org/V3/Northwind/Northwind.svc/Products
  • 访问OData中具体的某一个Entiry: https://services.odata.org/V3/Northwind/Northwind.svc/Products(1)
  • 访问OData服务中,某一Entity的属性:https://services.odata.org/V3/Northwind/Northwind.svc/Products(1)/ProductID

OData结构小结:

  • 在OData的service document中可以看到所有的Entity Set;在OData的metadata document中可找到Entity Type和Property。
  • 如果想查看某一具体的实体,则直接在OData Service Document的URL后直接append上具体的实体名称即可,例如上例中的实体集合Customers;在实体集合中,每一条entry也即对应着一条数据的Entity。

GraphQL

GraphQL是一门基于API的查询语言,在Restful API中,一个API可能用来维护一个功能,比如:

<http://api.example.com/users/{id}> //列出指定id的user

<http://api.example.com/users/list> //列出所有user

但是在GraphQL中如果想要完成上述功能,只需要一个API即可,我们需要做的就是改变POST请求体的内容,然后服务器根据内容来做出响应。

权限控制

在没有做好权限控制的情况下API可能会执行一些危险的操作,比如查询用户的所有信息

query GetAllUsers {

users {

id

username

password

...

}

}

这种问题并不是API技术本身带来的,却也是API技术不可避免的。

注入

注入的本质是数据和代码的弱分离,在于发出GraphQL查询前就把数据拼接到了查询语句中,形成了GraphQL注入。

用户访问URL → 前端获取参数 → 拼接成GraphQL语句 → 发送 → 后端执行

 解决方案是使用GraphQL中的参数化查询,把拼接的过程交给后端执行

DDoS

如果Schema中定义的类型字段中有互相引用的情况下,通过构造无限重复的查询有可能造成DDoS攻击。


type Query {

author(id: ID!): Author

}

type Author {

id: ID!

book: Book

...

}

type Book {

id: ID!

author: Author

...

}


query {

author(id: {$id}) {

book {

author {

book {

author {

book {

...

}

}

}

}

}

}

}

通过限制查询深度可以解决此问题

内省

通过内省系统可以拿到后端接口的信息,可能会发生信息泄露的问题。

查询存在的类型


query {

__schema {

types {

name

}

}

}

查询类型的字段


query {

__type(name:"Root") {

name

fields{

name

type{

name

kind

ofType{

name

kind

}

}

}

}

}

...

SOAP

soap是基于XML的协议,通过HTTP来交换信息,一般其构成如下:


<?xml version="1.0"?>

<soap:Envelope xmlns:soap="http://www.w3.org/2001/12/soap-envelope" soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">

<soap:Header></soap:Header>

<soap:Body>

<soap:Fault></soap:Fault>

</soap:Body>

</soap:Envelope>
  • sql注入

SOAP请求中一些参数未经过滤直接拼接到sql查询语句中。

<username xsi:type="xsd:string">admin' OR '1'='1'#</username>

另外,可以根据报错信息探测数据库的信息

  • 命令注入

SOAP请求中一些参数未经过滤直接被当作命令执行。

<targetHost xsi:type="xsd:string">192.168.66.1; ls;</targetHost>
  • XML注入

通过在SOAP请求中插入或修改恶意的XML Payload达到攻击的效果。

比如现在有两个SOAP API:检查余额和转账交易

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Bank">

<soapenv:Header/>

<soapenv:Body>

<urn:requestBalance soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<wallet_num xsi:type="xsd:decimal">wallet_num</wallet_num>

</urn:requestBalance>

</soapenv:Body>

</soapenv:Envelope>


<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Bank">

<soapenv:Header/>

<soapenv:Body>

<urn:internalTransfer soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<receiver_wallet_num xsi:type="xsd:decimal">receiver_wallet_num</receiver_wallet_num>

<sender_wallet_num xsi:type="xsd:decimal">sender_wallet_num</sender_wallet_num>

<amount xsi:type="xsd:float">amount</amount>

<token xsi:type="xsd:string">token</token>

</urn:internalTransfer>

</soapenv:Body>

</soapenv:Envelope>

对于检查余额,只需要一个钱包的id,对于转账交易,需要接收者、发送者、金额、token四个参数

我们可以将所有用户的余额来汇总到我们手上,但是缺少交易的token,这时候就可以用注入来达到任意转账的目的:

...

receiver={our_id}</receiver_wallet_num><sender_wallet_num xsi:type="xsd:decimal">{another_id}</sender_wallet_num><!--

amount=--><amout xsi:type="xsd:float">999999

经过拼接之后:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Bank">

<soapenv:Header/>

<soapenv:Body>

<urn:internalTransfer soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

<receiver_wallet_num xsi:type="xsd:decimal">{our_id}</receiver_wallet_num>

<sender_wallet_num xsi:type="xsd:decimal">{another_id}</sender_wallet_num>

<!--

</receiver_wallet_num>

<sender_wallet_num xsi:type="xsd:decimal">sender_wallet_num</sender_wallet_num>

<amount xsi:type="xsd:float">

-->

<amount xsi:type="xsd:float">999999</amount>

<token xsi:type="xsd:string">token</token>

</urn:internalTransfer>

</soapenv:Body>
  • SOAP Action欺骗

一个HTTP请求中会包含一个SOAP Action的头部,用于表示此次请求的操作,这个操作可能会被攻击者修改,这样就改变了此次请求的行为逻辑。

...

SOAPAction: urn:ws-user-account#createUser

...

{soap_data}


...

SOAPAction: urn:ws-user-account#deleteUsert

...

{soap_data}

有时候仅仅修改SOAP Action是不够的,可能要根据不同的Action对soap请求的内容进行修改。

  • Dos Attack

服务端对于请求的参数没有进行验证或者参数没有边界,可能会引发服务端的缓冲区溢出,造成服务不可用。

...

<userName xsi:type="xsd:string">gcvjaklsdjaosihfoiqwhfoiqwhbfoqiwheqwoieqwoiejhqwpoohfoqiefo.123asdasdsafdsfosidghsdfoighnodfbnoobidfbnoifhosdfasdasdh</userName>

...
  • WSDL泄露

WSDL文档中包含了web服务的信息,有些web服务的信息是不能公开的(比如支付,合理收集用户信息等)。

# Google Search

inurl: ?wsdl
  • CRLF + SSRF

PHP中的有自己的SOAP扩展,其中的SoapClient类中有一个参数可以自定义UA头,通过控制UA头,再结合CRLF,就可以构造任意POST请求

$payload = new SoapClient(null,array('user_agent'=>"test\r\nCookie: PHPSESSID=08jl0ttu86a5jgda8cnhjtvq32\r\n

Content-Type: application/x-www-form-urlencoded\r\nContent-Length:45\r\n\r\n

username=admin&password=nu1ladmin&code=470837\r\n\r\n\r\n",

'location'=>$location,

'uri'=>$uri));

RPC

首先了解什么叫RPC,为什么要RPC,RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

RPC 的主要功能目标是让构建分布式计算(应用)更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。

RPC要解决的两个问题:

1. 解决分布式系统中,服务之间的调用问题。

2. 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑。

Dubbo

CVE编号 CVE-2023-23638
漏洞情况

Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java、Golang 等多语言 SDK 实现。该项目受影响版本存在反序列化漏洞,由于Dubbo在序列化时检查不够全面,当攻击者可访问到dubbo服务时,可通过构造恶意请求绕过检查触发反序列化,执行恶意代码。

受影响的版本
  • org.apache.dubbo:dubbo-common@[3.0.0, 3.0.14)
  • org.apache.dubbo:dubbo-qos@[3.1.0, 3.1.6)
  • org.apache.dubbo:dubbo-common@[2.7.0, 2.7.22)
修复方案
  • 将组件 org.apache.dubbo:dubbo-common 升级至 3.0.14 及以上版本
  • 将组件 org.apache.dubbo:dubbo-qos 升级至 3.1.6 及以上版本
  • 将组件 org.apache.dubbo:dubbo-common 升级至 2.7.22 及以上版本
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值