Skip to content

Checksum

Goal

Prevent man-in-the-middle types of request changes. By adding a checksum to the request and validate that in the backend

Workings

In order to test vulnerabilities in a system a hacker typically creates a request by clicking on your site, and then intercepting that and changing it. This changing can be detected if your frontend sends a checksum with the request to the backend which validates the checksum. Tripwire handles the checksum validation and recording and blocking if the checksum fails (user modified their request).

NOTE Sure I can hear you think. But an attacker can change the checksum based on the changed request and then tripwire will not detect that. Correct, but that is quite a hassle for every change in request parameters. Meaning that there is much much more effort needed to change a request than without this tripwire. We could go even fancier by encrypting the checksum. But that is on our roadmap

Installation

Add the ChecksumValidator as the first middleware in your set, before anything else. Other middleware could interact with the request (ie trimming) which would invalidate the checksum

php
// kernel.php
class Kernel extends HttpKernel
{
    protected $middleware = [
        ChecksumValidator::class, //[!code focus] // need to be the very first in your middleware set !
        // ...

Enabled

Enable or disable this wire

Attack score

This is this wire severity, the higher the number the more severe. All attackScores will be summarized and if it exceeds the PunishScore the block will be activated. Set this to a number that reflects the severity.

  • A very high number will immediately block the user/ip
  • A low number will only block if there are many requests

TIP

sqli and xss are very common attack vectors with high confidence detection. You should set those to a very high number

Methods

The methods specifies which methods should be inspected

Options:

  • 'post'
  • 'put'
  • 'patch'
  • 'get'
  • 'all' or '*'

The 'all' or '*' is a alias to inspect all methods

php
    ->methods(['post', 'put']) // only post and put method
php
    ->methods(['*']) // all methods

Urls

This specifies which urls to include or exclude

Example

Include all urls that start with members/... however do not include members/dashboard

php
    ->urls(
        UrlsConfig::make()
            ->only([
                'members/*'
            ])
            ->except([
                'members/dashboard'
            ]
        ))

Example

php
// tripwire_wires.php
    ->enabled(env('TRIPWIRE_CHECKSUM_ENABLED', env('TRIPWIRE_ENABLED', true)))
    ->attackScore(1000)
    ->methods(['*'])
    ->urls(
        UrlsConfig::make()
            ->except([
                'admin/*'
            ]
        ))
    ->config([
        'posted'=> 'X-Checksum', // the name of the field that includes your frontend calculated checksum
        'timestamp'=> 'X-sand', // the name of the field that includes your frontend calculated checksum
        'serverside_calculated'=> 'x-checksum-serverside', // the name of the field that includes your frontend calculated checksum
    ])
    ->rejectResponse(
        BlockResponseConfig::make(
            JsonResponseConfig::make()->code(406)->exception(RequestChecksumFailedException::class),
            HtmlResponseConfig::make()->code(406)->view(env('TRIPWIRE_REJECT_PAGE', 'tripwire-laravel::blocked')),
        )
    );

You can change the x-checksum-serverside to whatever fieldname you want, as long as it does not conflicts with possible request fields

optional global overriders

Frontend setup with Axios

Adding a checksum is pretty easy with Axios interceptors

js
import CryptoJS from 'crypto-js/crypto-js';

// Cleans the data for encoding purposes
function createChecksum(postedData) {
    if (postedData) {
        const data = JSON.stringify(postedData);

        return data.replace(/[^a-z0-9]/g, '');
    }

    return '';
}

// Hash the value
function hashValue(value: string) {
        return cryptojs.sha512(value);
}

const addChecksum = (authorizedConfig) => {
    authorizedConfig.headers['X-Checksum'] = hashValue(createChecksum(authorizedConfig.data));
}

axios.interceptors.request.use((config) => {
    const authorizedConfig = {...config};
    addChecksum(authorizedConfig);
    // ...

    return authorizedConfig;
});

Released under the MIT License.