Why Negative Path testing is an integral part of an Information Security Program

In keeping with a “Security in Depth” model, one should ensure there are controls on multiple layers of the application and checks of controls in multiple steps of the SDLC.

There can be processes in place for detecting vulnerabilities and identifying risks:

  • During design through Security Reviews
  • During development through Static Analysis Security Testing, AKA SAST, and Security Reviews
  • During deployment through Dynamic Analysis Security Testing, AKA DAST, Security Reviews, and Offensive Reviews (Penetration tests in staging environments)
  • Post deployment through Offensive Reviews (Penetration tests in production environments)

However, they are not, and cannot be, fully automated. In particular, during design and during deployment one can be dependent on developer’s making a request for a review. An efficient way to address these gaps in automated processes during deployment is to hook into other processes that take place during that phase.

One such process is the Quality Assurance (QA) testing process. By partnering with the QA work stream a more holistic approach to quality and security can be achieved by explicitly testing what is usually only implied.

Quality Assurance and Information Security have a lot in common: Often seen as a cost center, mostly an afterthought, relegated to the land of non-functional requirements. But also critical to a healthy business, prioritized by smart leadership, and a key ingredient to a good user experience. There’s a certain je ne sais quoi to both quality and security. Their presence is rarely noticeable to the end user, but their absence will be obvious.

The above Venn diagram shows each parties primary focus and where they overlap. Let’s look at an example, take the following snippet of code:

export default async function sendMail(ctx) {
  const mail = ctx.request.body;
  const {sender} = mail || {};
  
  if (!sender.includes('company.com')) {
    ctx.response.status = 403;
    ctx.response.body = 'Unauthorized sender email address.';
    return ctx.response.body;
  }
  ...

The intention of the if statement is to restrict the sender’s email address to the company domain, company.com

If you were only testing this along the happy path you might use an email address like [email protected] and it’d work just fine.

But consider paths that could lead to breakage:

  • [email protected], notice the domain is capitalized.
  • johndoecompany.com, which isn’t a valid email address

Also consider paths that could bypass this check, potentially leading to exploitation:

By testing these Negative paths, the developer would realize the issues waiting to happen and fix the code before even making a pull request. The end result may look something like this:

export default async function sendMail(ctx) {
  const mail = ctx.request.body;
  const {sender} = mail || {};

  const domain = sender.substring(sender.lastIndexOf('@') + 1);
  if (domain.toLowerCase() !== 'company.com') {
    ctx.response.status = 403;
    ctx.response.body = 'Unauthorized sender email address.';
    return ctx.response.body;
  }
  ...

Which accounts for all the Negative paths discussed above. It’s more secure, it’s more robust and it’s handling edge cases better.

Clearly, partnering to drive the adoption of negative path testing would result in a stronger Information Security program and Quality Assurance program.