扫描 REST API 中的漏洞

Andrew2020-09-03 17:04:24

许多复杂的Web应用程序都是使用REST API构建的。与整体Web应用程序和网站一样,Acunetix可以帮助您确保所有REST API的安全性。在本文中,您将学习如何使用OpenAPI,Swagger或WADL定义来发现和修复REST API中的漏洞:

  1. 构建一个简单的REST API
  2. 创建不同规格的API定义文件:
    • OpenAPI 3.0
    • Swagger 2.0
    • WADL
  3. 扫描API
  4. 识别漏洞
  5. 缓解和/或解决漏洞
  6. 重新扫描API以确认解析度

步骤1:构建简单的REST API

第一步是构建一个可以扫描的简单REST API。您将构建一个故意易受攻击的REST API,以便以后可以查看Acunetix如何发现该漏洞。

为了能够构建简单的REST API,您需要一个本地Web服务器以及一个附带的数据库服务器。在此示例中,我们在本地主机(也可通过192.168.0.11访问)上将本地WampServer(wamp64)与MariaDB数据库服务器一起使用。您也可以使用任何其他Web服务器和其他数据库类型,例如MySQL。如果这样做,则只需相应地修改示例中的路径。

要构建REST API,请执行以下步骤:

  1. 在Web服务器上创建数据库以存储数据
  2. 创建一个C:\ wamp64 \ www \ example \ includes \ config.php文件来存储连接到数据库所需的参数
  3. 创建一个C:\ wamp64 \ www \ example \ core \ initialize.php文件,其中将包含初始化API所需的命令
  4. 为用户类创建一个C:\ wamp64 \ www \ example \ core \ user.php文件,该文件定义了我们将提供的API函数;在此示例中,我们将提供一个名为read_by_id的 API函数
  5. 为API函数read_by_id创建一个C:\ wamp64 \ www \ example \ api \ read_by_id.php文件
  6. 创建一个C:\ wamp64 \ www \ example \ client \ client.php文件,该文件将向用户显示输入表单,并使用API检索请求的信息;这将是您的Web应用程序用户界面

在您的Web服务器上创建数据库

从数据库服务器根提示符下运行以下命令:

MariaDB [(none)]> CREATE USER 'restuser'@'localhost' IDENTIFIED BY 'restuserpass';
MariaDB [(none)]> CREATE DATABASE restdb;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON restdb.* TO 'restuser'@'localhost';
MariaDB [(none)]> USE restdb;
MariaDB [restdb]> CREATE TABLE `users` (`id` int(11) NOT NULL AUTO_INCREMENT,`fname` varchar(30) DEFAULT NULL, `lname` varchar(30) DEFAULT NULL, `email` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) );
MariaDB [restdb]> INSERT INTO users (fname, lname, email) VALUES ('John', 'Smith', 'john@example.com');
MariaDB [restdb]> INSERT INTO users (fname, lname, email) VALUES ('Jane', 'Doe', 'jane@example.com');

生成配置文件

创建C:\ wamp64 \ www \ example \ includes \ config.php文件,如下所示:

<?php
$db_host = 'localhost';
$db_name = 'restdb';
$db_user = 'restuser';
$db_pass = 'restuserpass';
$db = new PDO('mysql:host='.$db_host.';dbname='.$db_name.';charset=utf8',$db_user,$db_pass);
?>

生成初始化文件

创建一个C:\ wamp64 \ www \ example \ core \ initialize.php文件,如下所示:

<?php
  defined('SITE_ROOT') ? null : define('SITE_ROOT', 'C:\wamp64\www\example');
  require_once(SITE_ROOT . '\includes\config.php');
  require_once(SITE_ROOT . '\core\user.php');
?>

生成用户类文件

创建C:\ wamp64 \ www \ example \ core \ user.php文件,如下所示:

<?php
  class User{
    private $conn, $table = 'users';
    public $id, $fname, $lname, $email;

    public function __construct($db) { $this->conn = $db; }

    public function read_by_id() {

      $query = 'SELECT id, fname, lname, email FROM ' . $this->table . ' WHERE id = ' . $this->id;
      $query_count = 'SELECT count(*) FROM ' . $this->table . ' WHERE id = ' . $this->id;
      $statement_count = $this->conn->prepare($query_count);
      $statement_count->execute();      
      $statement = $this->conn->prepare($query);
      $statement->execute();

      $row = $statement->fetch(PDO::FETCH_ASSOC);

      $statement_row_count = $statement_count->fetchColumn();
      if ($statement_row_count==0) {
        http_response_code(404);
      } else {
        $this->id = $row['id'];
        $this->fname = $row['fname'];
        $this->lname = $row['lname'];
        $this->email = $row['email'];
      }
    }
  }
?>

构建read_by_id API函数

创建C:\ wamp64 \ www \ example \ api \ read_by_id.php文件,如下所示:

<?php

  header('Content-Type: application/json');
  include_once('../core/initialize.php');

  $user = new User($db);
  $user->id = isset($_GET['id']) ? $_GET['id'] : die();
  $user->read_by_id();

  $uarray = array('id'=>$user->id,'fname'=>$user->fname,'lname'=>$user->lname,'email'=>$user->email);

  print_r(json_encode($uarray));

?>

构建Web应用程序用户界面

创建C:\ wamp64 \ www \ example \ client \ client.php文件,如下所示:

<?php

if (isset($_GET['id']) && $_GET['id']!="") {
  $id = $_GET['id'];
  $url = "http://192.168.0.11/example/api/read_by_id.php?id=".$id;

  $client = curl_init($url);
  curl_setopt($client,CURLOPT_RETURNTRANSFER,true);
  $response = curl_exec($client);
  $result = json_decode($response);

  echo "<table style='border: 1px solid black;'>";
  echo "<tr><td>Order ID:</td><td>$result->id</td></tr>";
  echo "<tr><td>First Name:</td><td>$result->fname</td></tr>";
  echo "<tr><td>Last Name:</td><td>$result->lname</td></tr>";
  echo "<tr><td>Email Address:</td><td>$result->email</td></tr>";
  echo "</table><br><br><hr><br><br>";
 }
?>

<form action="" method="GET">
<label>Enter User ID:</label><br />
<input type="text" name="id" placeholder="Enter User ID" required/>
<br /><br />
<button type="submit" name="submit">Submit</button>
</form>

步骤2.创建API定义文件

OpenAPI 3.0规范

创建C:\ wamp64 \ www \ example \ api \ api_example_OpenAPI3.yaml文件,如下所示:

openapi: '3.0.0'
info:
  title: UsersExample
  version: '1.0'

servers:
  - url: http://192.168.0.11/example/api

paths: 
  /read_by_id.php:
    get:
      summary: Single user identified by id
      parameters:
        - name: id
          in: query
          description: id to identify user
          schema:
            type: integer
      responses:
        '200':
          description: Successfully returned a single user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/user'
        '404':
          description: No user with specified id was found

components:
  schemas:
    user:
      type: object
      properties:
        id:
          type: integer
        fname:
          type: string
        lname:
          type: string
        email:
          type: string

Swagger 2.0规范

创建C:\ wamp64 \ www \ example \ api \ api_example_Swagger2.yaml文件,如下所示:

swagger: '2.0'
info:
  version: '1.0'
  title: UsersExample
  contact: {}
host: 192.168.0.11
basePath: /example/api
schemes:
- http
consumes:
- application/json
produces:
- application/json
paths:
  /read_by_id.php:
    get:
      description: Single user identified by id
      summary: Single user identified by id
      operationId: Singleuseridentifiedbyid
      deprecated: false
      produces:
      - application/json
      parameters:
      - name: id
        in: query
        required: false
        type: integer
        format: int32
        description: id to identify user
      responses:
        200:
          description: Successfully returned a single user
          schema:
            $ref: '#/definitions/user'
          headers: {}
        404:
          description: No user with specified id was found
          schema: {}
definitions:
  user:
    title: user
    type: object
    properties:
      id:
        type: integer
        format: int32
      fname:
        type: string
      lname:
        type: string
      email:
        type: string
tags: []

WADL规范

创建C:\ wamp64 \ www \ example \ api \ api_example.wadl文件,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:schemas="http://192.168.0.11/example/api/schemas" xmlns="http://wadl.dev.java.net/2009/02">
  <doc title="UsersExample" xml:lang="en" />
  <grammars>
    <xs:schema xmlns:tns="http://192.168.0.11/example/api/schemas" targetNamespace="http://192.168.0.11/example/api/schemas" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:element name="user" type="schemas:user" />
      <xs:element name="Singleuseridentifiedbyid_Response" type="schemas:Singleuseridentifiedbyid_Response" />
      <xs:complexType name="user">
        <xs:sequence>
          <xs:element minOccurs="0" name="id" type="xs:integer" />
          <xs:element minOccurs="0" name="fname" type="xs:string" />
          <xs:element minOccurs="0" name="lname" type="xs:string" />
          <xs:element minOccurs="0" name="email" type="xs:string" />
        </xs:sequence>
      </xs:complexType>
      <xs:complexType name="Singleuseridentifiedbyid_Response">
        <xs:sequence>
          <xs:element minOccurs="1" name="response" type="schemas:user">
            <xs:annotation>
              <xs:documentation>Successfully returned a single user</xs:documentation>
            </xs:annotation>
          </xs:element>
        </xs:sequence>
      </xs:complexType>
    </xs:schema>
  </grammars>
  <resources base="http://192.168.0.11/example/api">
    <resource id="_read_by_id.php" path="/read_by_id.php">
      <method id="Singleuseridentifiedbyid" name="GET">
        <doc title="Single user identified by id" xml:lang="en">Single user identified by id</doc>
        <request>
          <param name="id" style="query" type="xsd:integer">
            <doc title="id" xml:lang="en">id to identify user</doc>
          </param>
        </request>
        <response status="200">
          <doc title="200" xml:lang="en">Successfully returned a single user</doc>
          <representation element="schemas:Singleuseridentifiedbyid_Response" mediaType="application/json" />
        </response>
        <response status="404">
          <doc title="404" xml:lang="en">No user with specified id was found</doc>
        </response>
      </method>
    </resource>
  </resources>
</application>

步骤3.扫描您的API

在此示例中,我们的API在此处定义:

要使用Acunetix扫描API,请执行以下操作:

  1. 使用上面列出的任何规范URL创建一个新目标;请注意,每个URL描述相同的API,因此将暴露相同的漏洞;我们提供了三种不同的方式,以便您证明Acunetix支持所有常见的REST API定义标准
  2. 将PHP AcuSensor部署到您的API
  3. 针对您的API 启动全面扫描,然后等待其完成

步骤4.找出您API中的漏洞

检查目标的漏洞列表。

在此练习中,我们将重点介绍SQL注入漏洞。

在“ 攻击详细信息”部分,Acunetix显示输入字段已成功填充潜在的恶意内容。这意味着未正确验证输入字段中插入的数据。Acunetix还提供了利用证明:它告诉您后端代码使用的数据库名称(API用户不应访问此类信息)。

步骤5.解决漏洞

快速浏览一下该函数读取-by_id内部user.php的类文件可以揭示根本原因。使用字符串连接构建查询:

$query = 'SELECT id, fname, lname, email FROM ' . $this->table . ' WHERE id = ' . $this->id;
$query_count = 'SELECT count(*) FROM ' . $this->table . ' WHERE id = ' . $this->id;
$statement_count = $this->conn->prepare($query_count);
$statement_count->execute();
$statement = $this->conn->prepare($query);
$statement->execute();

$这个- > ID变量被简单地串接到查询字符串,没有任何验证。我们需要通过参数化查询字符串来调整代码,以确保传递的所有参数均正确地转义并用引号封装,从而不允许进一步利用。新的代码段如下所示:

$query = 'SELECT id, fname, lname, email FROM ' . $this->table . ' WHERE id = ?';
$query_count = 'SELECT count(*) FROM ' . $this->table . ' WHERE id = ?';
$statement_count = $this->conn->prepare($query_count);
$statement_count->bindParam(1, $this->id);
$statement_count->execute();
$statement = $this->conn->prepare($query);
$statement->bindParam(1, $this->id);
$statement->execute();

步骤6.重新扫描以确认分辨率

转到扫描漏洞列表,然后选择您试图修复的漏洞。


现在,单击“ 重新测试”按钮-这将创建一个新的扫描,以再次测试所选的漏洞。结果将表明您已经成功解决了这些漏洞。

apirest
本作品采用《CC 协议》,转载必须注明作者和本文链接
CMS最近在WordPress 4.7.0上默认添加并启用了REST API。Sucuri的Marc Alexandre Montpas发现了该漏洞,并将其报告给WordPress安全团队。
扫描 REST API 中的漏洞
2020-09-03 17:04:24
许多复杂的Web应用程序都是使用REST API构建的。与整体Web应用程序和网站一样,Acunetix可以帮助您确保所有REST API的安全性。在本文中,您将学习如何使用OpenAPI,Swagger或WADL定义来发现和修复REST API中的漏洞:...
近日据外媒报道,安全研究人员通过 Shodan 搜索引擎发现近 2300 台安装了 etcd 组件的服务器暴露在互联网上,利用一些简单脚本即可从中获取登录凭证,如 cms_admin、mysql_root、 postgres 之类。目前经过测试已经成功地从这些服务器上检索到了来自 1,485 个 IP 、约 750 MB 的数据,其中包括 8,781 个密码、650 AWS 访问密钥、23 个密
CVE-2021-42567 Apereo CAS单点登录系统REST API接口XSS漏洞分析。
服务之间的内部通信通过定义明确的 API 或任何轻量级通信协议进行。API 通常是为第三方用户创建的。组织利用 API 作为单个微服务相互通信的轻量级解决方案。对此,API 以 JSON 的形式返回文本响应,开发人员可以根据他们的可行性使用这些响应。微服务和 API 经常耦合在一起,尽管它们是两个不同的实体。这种思想类似于使用公共 API 来连接应用程序。有些人会将许多 API 分配给单个服务,而另一些人会使用单个 API 来访问多个服务。
解答API 安全和数据安全之间的关系是什么?怎么样通过 API 安全的手段或 API 安全这个点来解决数据安全的问题。
常见API接口漏洞了解接口常见漏洞,将帮助你在测试接口获取更多的思路。信息披露信息可能会在 API 响应或公共来源中披露。敏感数据可以包含攻击者可以利用的任何信息。例如,使用WordPress API的网站可能会在不知不觉中与导航到API路径的任何人共享用户信息。错误消息可帮助 API 使用者排查其与 API 的交互问题,并允许 API 提供者了解其应用程序的问题。其他良好的信息来源是在侦察期间收集的 API 文档和资源。
由于其可靠性和简单性,API在整个计算领域已经无处不在。目前最流行的API安全工具类型是那些保护API免受恶意请求的工具,这有点像API防火墙。这样,任何漏洞都可以在API使用之前被消除,APIsec在API部署后继续监视,以防万一。这不仅对保护API非常有帮助,而且还有助于遵守需要特定保护的政府法规或行业标准。
Docker-remoter-api渗透
2022-04-18 16:24:50
看雪论坛作者ID:H.R.P
Andrew
暂无描述