Adoption of Rules #2 and #3 from AppSec Manifesto

Introduction

Alex Tatulchenkov
4 min readApr 27, 2021

This is another regarding the adoption of the AppSec Manifesto. In previous articles, we looked into how the first two rules (Absolute Zero and The Lord of the Sinks) can be adopted in PHP applications. These rules don’t rely on the previous two (actually all of them may be used independently) and even can be implemented separately but due to the proposed solution, it makes sense to clarify them together.

RULE #2

Least (Computational) Power Principle

Access to computational power is a privilege

This rule is a compilation of the Principle of least privilege and Fail-fast approach. If at some point it becomes obvious that input data doesn’t conform to our domain then we should stop immediately and do not transfer invalid data across the application. This is very similar to the Rule of Repair and is widely used Design by contract

  • fail immediately instead of postponing the failure
  • don’t working around the failure

This is absolutely obvious if we are talking about the malicious input that is the result of an attack but if invalid input is caused by a legitimate user’s mistake then we come to the well-known trade-off between the security and the UX. This will be additionally covered during clarification of the Rule #4.

In general, prefer Fail-fast approach over Forgive approach.

In this context, it’s worth mentioning the 6th commandment from The Ten Commandments for C Programmers.

RULE #3

Forget-me-not

Don’t forget information regarding the validity of a certain input

If you’ve validated some input once there is no sense to validate it again, just don’t lose that knowledge. How can we “forget” obtained knowledge? e.g.

public function foo(string $input)
{
if (isValid($input)) {
$this->bar($input);
}
}
public function bar(string $input)
{
...
}

In the example above method foo() obtains knowledge regarding the validity of $input but immediately loses it during the bar() method call. Inside bar() you can not be certain that $input is valid. So you should rely on the validation inside the foo() and pray for nobody will call bar() directly or duplicate validation introducing a Shotgun parsing problem.

There are two methods to ensure that provided input conforms to our domain rules: parsing and validation.

Parsing: A parser is a software component that takes input data (frequently text) and builds a data structure

Validation: Validation is a process that uses routines, often called "validation rules", "validation constraints", or "check routines", that check for correctness, meaningfulness, and security of data that are input to the system.

Parsing

<?php
/**
* creates instance of the custom type DirectoryName from
*
the user input
*/
$directoryName = DirectoryName::fromString($user_input);

Validation

<?php
/**
* returns boolean answer whether user input valid or not
*/
$isValid = DirectoryValidator::validate($user_input);

Both rules (Rule #2 and Rule #3) can be satisfied by the adoption of TypeDD.

Let’s see how we can do it in PHP. In the heart of the TypeDD is Parse, don’t validate principle. To implement custom data types we’ll use ValueObjects.

<?phpnamespace app\types;use Assert\Assertion;

final class DirectoryName
{
private $name;

private function __construct()
{
}

public static function fromString(string $name): self
{
Assertion::betweenLength($name, 1, 127);
Assertion::regex($name, '/^[a-z0-9\-_]*$/i', sprintf('Invalid Directory Name "%s" (a-z, 0-9, dash and underscore allowed)', $name));
$directoryName = new self();
$directoryName->name = $name;
return $directoryName;
}

public function value(): string
{
return $this->name;
}
}

Using such ValueObject we can mitigate Path Traversal and Null byte injections in filesystem-related operations.

First of all — create an instance of the ValueObject as close to the Source as possible.

Source: The program point that reads external resource (user-input or any other data that can be manipulated by a potential attacker).

After you have an instance of the ValueObject you pass it across the application:

<?php

function readDirectory(DirectoryName $dir)
{
...
}

readDirectory() doesn’t validate $dir instead it relies on the knowledge obtained by the system previously. In addition, by extracting all parsing logic into ValueObjects we follow DRY principle.

The benefits of such an approach are well described in books related Domain-Driven Design where value objects are heavily used.

Although AppSec Manifesto uses ValueObject term its ValueObjects are much more sophisticated than ones described in Wikipedia and more similar to domain primitives idea extended to other layers (not only domain level but application and infrastructure levels as well). So in fact it’s a custom datatype, often with a custom operators represented by its methods and follows Information Expert pattern from GRASP.

Lets recap:

  • Parse, don't validate input data as close to the source as possible
  • Use Always valid ValueObjects (force invariants)
  • Do not transfer malicious data from source to sink

Instantiate once, use everywhere

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Alex Tatulchenkov
Alex Tatulchenkov

Written by Alex Tatulchenkov

Senior Software Engineer at Intetics Inc., AppSec Manifesto evangelist

No responses yet

Write a response