42CRUNCH BLOG


Creating High Quality OAS Definitions with .Net Core


This document highlights how code annotations can be used to enhance the quality and the security posture for customers using .Net Core.

42Crunch security recommendations help enterprises discover and remediate vulnerabilities much more quickly (up to 25X more quickly) while saving 90% of manual costs (whether through internal efforts or external pen-testing).

Using the Available Native Support from .Net

In order to produce OAS files when developing with .NET core framework, developers can use: 

Using Swashbuckle

In this document’s example, we have an entity (Person), which will represent the JSON Schema, and a Controller (PersonController), that will expose the API’s operations for us.

The Swagger-UI looks pretty much like the following image:

 

Image 1 – Swagger UI presenting the APIs Operations and Schema

The 42Crunch IDE Support

Based on the Open API Specification file generated by the .net framework with the support of Swashbuckle, we can get this file and add it into our IDE, just to start the process to execute de Security Audit Score, to make sure potential vulnerabilities in our API, please, refer to the following screenshot using Visual Studio Code and 42Crunch’s extension:

 

Image 2 – Initial Score generated by Swashbuckle

The 42Crunch’s extension for Visual Studio Code is presenting us with the following Audit Score: 10 of 100, in the rest of this document, we will cover how we could improve this score, essentially improving the C# code that is responsible for it.

Recommendations from 42Crunch

Most of 42Crunch’s recommendations can be fixed straight from the code to protect your API either from your preferred IDE or through the CI/CD process. We will always look only and just only at the generated OAS/Swagger file in order to identify if your API has potential security vulnerabilities. 

Note: Please, keep in mind that even having the best code and the whole coverage in the backends components does not necessarily mean that your API is free from potential attacks.

Let’s take a look first into the Person entity, which is our Schema:

Original Code for Entity/JSON Schema

using System.ComponentModel.DataAnnotations;
 
namespace TodoApi.Models
{
   public class Person
   {
 
       public string firstName { get; set; }
       public string lastName { get; set; }
       public long id { get; set; }
      public int age { get; set; }
   }
}

Enhanced Code for Entity/JSON Schema

using System.ComponentModel.DataAnnotations;
using System;
 
namespace TodoApi.Models
{
   public class Person
   {
       [Required, RegularExpression("/^[a-zA-Z ]{2,100}$/"),MinLength(5), MaxLength(100)]
       public string firstName { get; set; }
       [Required, RegularExpression("/^[a-zA-Z ]{2,100}$/"),MinLength(5), MaxLength(100)]
       public string lastName { get; set; }
       [Required, Range(1, long.MaxValue)]
       public long id { get; set; }
      [Required, Range(0, 150)]
      public int age { get; set; }
   }
}

Let’s see how that slight change had impacted our new Audit Score: 


Image 3 – Having just small changes, our audit score were considerably improved

One of the most critical aspects is in the security that is not defined, so we will show how to add it right into the code ad the following: 

private static void addSec(IServiceCollection services)
       {
 
           //Swaggers with Security
           services.AddSwaggerGen(c =>
           {
               // configure SwaggerDoc and others
 
               c.SwaggerDoc("v1", new OpenApiInfo
               {
                   Version = "v1",
                   Title = "42Crunch - some best practices demo API",
                   Description = "A simple example ASP.NET Core Web API",
                   TermsOfService = new Uri("https://example.com/terms"),
                   Contact = new OpenApiContact
                   {
                       Name = "Shayne Boyer",
                       Email = string.Empty,
                       Url = new Uri("https://twitter.com/spboyer"),
                   },
                   License = new OpenApiLicense
                   {
                       Name = "Use under LICX",
                       Url = new Uri("https://example.com/license"),
                   }
               });
               // jwt
 
               // add JWT Authentication
               var securityScheme = new OpenApiSecurityScheme
               {
                   Name = "JWT Authentication",
                   Description = "Enter JWT Bearer token **_only_**",
                   In = ParameterLocation.Header,
                   Type = SecuritySchemeType.Http,
                   Scheme = "bearer", // must be lower case
                   BearerFormat = "JWT",
                   Reference = new OpenApiReference
                   {
                       Id = JwtBearerDefaults.AuthenticationScheme,
                       Type = ReferenceType.SecurityScheme
                   }
               };
               c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
               c.AddSecurityRequirement(new OpenApiSecurityRequirement
   {
       {securityScheme, new string[] { }}
   });
 

               // add Basic Authentication
               var basicSecurityScheme = new OpenApiSecurityScheme
               {
                   Type = SecuritySchemeType.Http,
                   Scheme = "basic",
                   Reference = new OpenApiReference { Id = "BasicAuth", Type = ReferenceType.SecurityScheme }
               };
               c.AddSecurityDefinition(basicSecurityScheme.Reference.Id, basicSecurityScheme);
               c.AddSecurityRequirement(new OpenApiSecurityRequirement
   {
       {basicSecurityScheme, new string[] { }}
   });
           });
 
       }

Now, it’s time to improve our API’s operations, in order to that, for .net, we have to take a look into the Controller component. Please, see the original PersonController:

Original Code for PersonController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
 
namespace TodoApi.Controllers
{
   [Route("api/[controller]")]
   [ApiController]
   public class PersonController : ControllerBase
   {
       private readonly PersonContext _context;
 
       public PersonController(PersonContext context)
       {
           _context = context;
       }
 
       // GET: api/Person
       [HttpGet]
       public async Task<ActionResult<IEnumerable>> GetPersons()
       {
           return await _context.Persons.ToListAsync();
       }
 
       // GET: api/Person/5
       [HttpGet("{id}")]
       public async Task<ActionResult> GetPerson(long id)
       {
           var person = await _context.Persons.FindAsync(id);
 
           if (person == null)
           {
               return NotFound();
           }
 
           return person;
       }
 
       // PUT: api/Person/5
       // To protect from overposting attacks, enable the specific properties you want to bind to, for
       // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
       [HttpPut("{id}")]
       public async Task PutPerson(long id, Person person)
       {
           if (id != person.id)
           {
               return BadRequest();
           }
 
           _context.Entry(person).State = EntityState.Modified;
 
           try
           {
               await _context.SaveChangesAsync();
           }
           catch (DbUpdateConcurrencyException)
           {
               if (!PersonExists(id))
               {
                   return NotFound();
               }
               else
               {
                   throw;
               }
           }
 
           return NoContent();
       }
 
       // POST: api/Person
       // To protect from overposting attacks, enable the specific properties you want to bind to, for
       // more details, see https://go.microsoft.com/fwlink/?linkid=2123754.
       [HttpPost]
       public async Task<ActionResult> PostPerson(Person person)
       {
           _context.Persons.Add(person);
           await _context.SaveChangesAsync();
 
           return CreatedAtAction("GetPerson", new { id = person.id }, person);
       }
 
       // DELETE: api/Person/5
       [HttpDelete("{id}")]
       public async Task<ActionResult> DeletePerson(long id)
       {
           var person = await _context.Persons.FindAsync(id);
           if (person == null)
           {
               return NotFound();
           }
 
           _context.Persons.Remove(person);
           await _context.SaveChangesAsync();
 
           return person;
       }
 
       private bool PersonExists(long id)
       {
           return _context.Persons.Any(e => e.id == id);
       }
   }
}

Now, let’s add some annotations from annotation for improving our Audit Score, at the same time that we are also incrementing our API protection level. We added required annotations into the Objects that will be the foundation for JSON Schema generation.


///
/// Creates a TodoItem.
/// 

/// /// Sample request: /// /// POST /Todo /// { /// “id”: 1, /// “name”: “Item1”, /// “isComplete”: true /// } /// /// /// /// A newly created TodoItem /// Returns the newly created item /// If the item is null /// Forbidden /// Forbidden /// Too many responses [HttpPost] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status429TooManyRequests)] public async Task<ActionResult> PostTodoItem(TodoItem todoItem) { _context.TodoItems.Add(todoItem); await _context.SaveChangesAsync(); return CreatedAtAction(“GetTodoItem”, new { id = todoItem.Id }, todoItem); }

Some initial results:

We could improve your API’s compliance posture from 16% secure to 43% secure if we made these changes. The recommendations do not require changing the generated swagger but leveraging annotations in  the C# Code.

Using NSwag

NSwag has similar capabilities to Swashbuckle. Either one can be configured to achieve a high-quality Open API Specification/Swagger.

For Pipelines and CI/CD

The REST API Security Testing pipeline lets you add our automated tests and validations to your CI/CD pipelines to test your API at every single CI/CD invocation. We let our customers use a wide range of tools in order to execute this practice, some of which are: Jenkins, Azure Pipelines, Bitbucket, Bamboo, and others. We can also quickly and easily make a custom integration using our API. Please, check more info here: https://42crunch.com/resources-free-tools/  

Once the APIs reach an acceptable Audit Score, which our recommendation is at least 70 (of 100), they are good to pass into the CI/CD pipeline such as Jenkins, GitLab, Azure Pipelines, GitHub, Bamboo, etc. In addition, 42Crunch offers the capacity to integrate with many quality tools, as one of those is SonarQube:

 

 

Conclusion

The 42Crunch platform concentrates on Open API/Swagger files, it allows our customers to have a fully integrated protection platform that understands the API behavior from its inception/design to production. To rely on the Spec files is the way to avoid guessing on what and how to protect your APIs. 

For API Security Audit
42Crunch executes 200+ security checks against the API contract, provides detailed security scoring for prioritization, and remediation advice to help developers define the best contract possible. Reaching great results in that phase obtaining high-quality Open API/Swagger files, we can move to the next component in the platform: Conformance Scanning. 

API Conformance Scan
42Crunch Conformance Scan is a dynamic runtime testing of your API to ensure that the implementation behind your API matches the contract set out in the OpenAPI / Swagger definition of the API.

API Protection
42Crunch Platform moves the defense from the network perimeter to in-depth directly in front of your APIs. With API Protection, you can protect each API from malicious intents with a micro-API firewall. 

 

References