Introduction

In our previous post, we explored Contract Testing with REST Assured and Pact, learning how to ensure API compatibility between services. Now, we’ll advance our REST Assured skills with Security Testing, a critical practice for verifying API protection against unauthorized access and vulnerabilities. This guide covers testing authentication, authorization, and common security issues using REST Assured. It’s designed for beginners and experienced developers, providing clear explanations and practical examples.

Key Point: Security testing with REST Assured ensures APIs are protected against threats like unauthorized access, data leaks, and injection attacks, safeguarding application integrity.

What is Security Testing for APIs?

Security Testing involves validating that an API enforces proper security measures, such as:

  • Authentication: Verifying user identity (e.g., via API keys, OAuth, JWT).
  • Authorization: Ensuring users only access permitted resources.
  • Data Protection: Preventing exposure of sensitive data.
  • Injection Attacks: Testing resistance to SQL or script injections.

REST Assured simplifies security testing by providing methods to handle authentication schemes, manipulate headers, and validate responses. We’ll use a public API with authentication (e.g., https://reqres.in for simulated OAuth) and test scenarios with REST Assured.

Setting Up for Security Testing

We’ll use REST Assured to test authentication and authorization scenarios, with Allure for reporting. For simplicity, we’ll use https://reqres.in, a public API that supports testing with mock authentication.

Ensure your pom.xml includes dependencies for REST Assured, JUnit, Allure, and Jackson (for JSON handling):



    11
    11
    2.27.0
    1.9.22


    
        io.rest-assured
        rest-assured
        5.4.0
        test
    
    
        org.junit.jupiter
        junit-jupiter
        5.10.2
        test
    
    
        org.hamcrest
        hamcrest
        2.2
        test
    
    
        com.fasterxml.jackson.core
        jackson-databind
        2.15.2
        test
    
    
        io.qameta.allure
        allure-junit5
        ${allure.version}
        test
    


    
        
            org.apache.maven.plugins
            maven-surefire-plugin
            3.2.5
            
                
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                
            
            
                
                    org.aspectj
                    aspectjweaver
                    ${aspectj.version}
                
            
        
        
            io.qameta.allure
            allure-maven
            2.12.0
            
                ${allure.version}
            
        
    

No additional setup is needed for https://reqres.in, as it provides mock endpoints for testing.

Security Testing Scenarios with REST Assured

Let’s explore common security testing scenarios, including authentication, authorization, and injection testing.

Example 1: Testing OAuth 2.0 Authentication

Test an API endpoint requiring an OAuth 2.0 token using REST Assured’s OAuth support.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("Security Testing")
public class OAuthTest {

    @Test
    @Description("Verify access to protected resource with OAuth token")
    public void testOAuthAuthentication() {
        RestAssured.baseURI = "https://reqres.in";

        // Simulate obtaining an access token (mock token from reqres.in)
        String token = given()
            .contentType("application/json")
            .body("{\"email\": \"eve.holt@reqres.in\", \"password\": \"cityslicka\"}")
            .when()
                .post("/api/login")
            .then()
                .statusCode(200)
                .extract().path("token");

        // Use token to access protected resource
        given()
            .auth().oauth2(token)
            .log().all()
            .when()
                .get("/api/users/2")
            .then()
                .statusCode(200)
                .body("data.id", equalTo(2))
                .body("data.email", notNullValue());
    }
}

Explanation:

  • post("/api/login"): Obtains a mock token from reqres.in.
  • auth().oauth2(token): Attaches the token as a Bearer token in the Authorization header.
  • The test validates access to a protected endpoint using the token.
Important: For real OAuth 2.0 APIs, configure client ID, client secret, and token endpoints. Use libraries like scribejava for complex OAuth flows if needed.

Example 2: Testing Authorization

Verify that users can only access resources they’re authorized for by testing role-based access.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;

@Feature("Security Testing")
public class AuthorizationTest {

    @Test
    @Description("Verify unauthorized access to restricted resource")
    public void testUnauthorizedAccess() {
        RestAssured.baseURI = "https://reqres.in";

        // Attempt to access a resource without a token
        given()
            .log().all()
            .when()
                .get("/api/users/2")
            .then()
                .statusCode(401); // Expect unauthorized status
    }
}

Explanation:

  • The test attempts to access a protected endpoint without a token.
  • Expects a 401 Unauthorized status, verifying the API enforces authentication.

Note: reqres.in doesn’t enforce 401 for GET endpoints, so adjust expectations for real APIs requiring authorization.

Example 3: Testing for Injection Attacks

Test the API’s resistance to injection attacks by sending malicious input (e.g., SQL or script injection).


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("Security Testing")
public class InjectionTest {

    @Test
    @Description("Verify API resistance to injection attacks")
    public void testInjectionAttack() {
        RestAssured.baseURI = "https://reqres.in";

        String maliciousInput = "{\"email\": \"user' OR '1'='1\", \"password\": \"pass\"}";

        given()
            .contentType("application/json")
            .body(maliciousInput)
            .log().all()
            .when()
                .post("/api/login")
            .then()
                .statusCode(400) // Expect bad request due to invalid input
                .body("error", containsString("Invalid"));
    }
}

Explanation:

  • maliciousInput: Simulates a SQL injection attempt in the request body.
  • The test expects a 400 Bad Request with an error message, indicating the API sanitizes input.
  • For real APIs, test query parameters, headers, and body fields for injection vulnerabilities.
Pro Tip: Use tools like OWASP ZAP or Burp Suite alongside REST Assured for comprehensive security testing, focusing on vulnerabilities like XSS or CSRF.

Using Request Specification for Security Tests

Centralize authentication and logging settings with a RequestSpecification for consistent security tests.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("Security Testing")
public class SecureSpecTest {

    private static RequestSpecification requestSpec;

    @BeforeAll
    public static void setup() {
        RestAssured.baseURI = "https://reqres.in";

        // Obtain token
        String token = given()
            .contentType("application/json")
            .body("{\"email\": \"eve.holt@reqres.in\", \"password\": \"cityslicka\"}")
            .post("/api/login")
            .then()
                .extract().path("token");

        requestSpec = new RequestSpecBuilder()
            .setBaseUri("https://reqres.in")
            .addHeader("Authorization", "Bearer " + token)
            .log(LogDetail.ALL)
            .build();
    }

    @Test
    @Description("Verify access to protected resource with request specification")
    public void testSecureAccess() {
        given()
            .spec(requestSpec)
            .when()
                .get("/api/users/2")
            .then()
                .statusCode(200)
                .body("data.id", equalTo(2));
    }
}

Explanation:

  • requestSpec: Includes the base URI, Bearer token, and logging.
  • spec(requestSpec): Applies the specification to ensure authenticated requests.

Integrating with Allure Reporting

Use Allure to document security test results, attaching request/response details for debugging.


import io.qameta.allure.Allure;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("Security Testing")
public class AllureSecurityTest {

    @Test
    @Description("Verify OAuth token retrieval with Allure reporting")
    public void testOAuthWithAllure() {
        RestAssured.baseURI = "https://reqres.in";

        String requestBody = "{\"email\": \"eve.holt@reqres.in\", \"password\": \"cityslicka\"}";
        Allure.addAttachment("Request Body", "application/json", requestBody, ".json");

        Response response = given()
            .contentType("application/json")
            .body(requestBody)
            .log().all()
            .when()
                .post("/api/login")
            .then()
                .statusCode(200)
                .body("token", notNullValue())
                .extract().response();

        Allure.addAttachment("Response Body", "application/json", response.asString(), ".json");
    }
}

Explanation:

  • Allure.addAttachment: Attaches request and response bodies to the Allure report.
  • Run mvn clean test and mvn allure:serve to view the report.

Integrating with CI/CD

Add security tests to a GitHub Actions pipeline, reusing the CI/CD setup from previous posts.

Update .github/workflows/ci.yml:


name: REST Assured CI Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK 11
        uses: actions/setup-java@v4
        with:
          java-version: '11'
          distribution: 'temurin'

      - name: Cache Maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-maven-

      - name: Run tests
        run: mvn clean test

      - name: Generate Allure report
        run: mvn allure:report

      - name: Upload Allure results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: allure-results
          path: target/allure-results

      - name: Publish Allure report
        if: always()
        uses: simple-elf/allure-report-action@v1.7
        with:
          allure_results: target/allure-results
          gh_pages: gh-pages
          allure_report: allure-report

      - name: Deploy report to GitHub Pages
        if: always()
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: allure-report

Explanation:

  • Runs security tests with mvn clean test.
  • Publishes Allure reports to GitHub Pages for visibility.

Tips for Beginners

  • Start with Authentication: Test API keys or OAuth before tackling complex authorization scenarios.
  • Use Mock APIs: APIs like reqres.in simplify learning without real credentials.
  • Secure Credentials: Store tokens or secrets in environment variables or CI/CD secrets.
  • Combine Tools: Use REST Assured for functional security tests and tools like OWASP ZAP for penetration testing.
Troubleshooting Tip: If authentication fails, verify token generation, expiration, and header formats. Enable log().all() to inspect requests and responses.

What’s Next?

This post enhances our REST Assured series with security testing, a vital skill for protecting APIs. To continue your learning, consider exploring:

  • GraphQL Testing: Testing GraphQL APIs with REST Assured.
  • Advanced Allure Reporting: Customizing reports with environments and categories.
  • End-to-End Testing: Combining REST Assured with Selenium for UI and API testing.
Stay tuned for more testing tutorials!