Secure API Toolkit

Overview

Sapient: Secure API toolkit

Build Status Latest Stable Version Latest Unstable Version License

Sapient secures your PHP applications' server-to-server HTTP(S) traffic even in the wake of a TLS security breakdown (compromised certificate authority, etc.).

Sapient allows you to quickly and easily add application-layer cryptography to your API requests and responses. Requires PHP 7 or newer.

Sapient was designed and implemented by the PHP security and cryptography team at Paragon Initiative Enterprises.

See our blog post about using Sapient to harden your PHP-powered APIs for more information about its design rationale and motivation.

The cryptography is provided by sodium_compat (which, in turn, will use the libsodium extension in PECL if it's installed).

Because sodium_compat operates on strings rather than resources (a.k.a. streams), Sapient is not suitable for extremely large messages on systems with very low available memory. Sapient only encrypts or authenticates message bodies; if you need headers to be encrypted or authenticated, that's the job of Transport-Layer Security (TLS).

Features at a Glance

  • Works with both Request and Response objects (PSR-7)
    • Includes a Guzzle adapter for HTTP clients
  • Secure APIs:
    • Shared-key encryption
      • XChaCha20-Poly1305
    • Shared-key authentication
      • HMAC-SHA512-256
    • Anonymous public-key encryption
      • X25519 + BLAKE2b + XChaCha20-Poly1305
    • Public-key digital signatures
      • Ed25519
  • Works with arrays
    • i.e. the methods with "Json" in the name
    • Sends/receives signed or encrypted JSON
  • Works with strings
    • i.e. the methods without "Json" in the name
  • Digital signatures and authentication are backwards-compatible with unsigned JSON API clients and servers
    • The signaure and authentication tag will go into HTTP headers, rather than the request/response body.

Additionally, Sapient is covered by both unit tests (provided by PHPUnit) and automated static analysis (provided by Psalm).

Sapient Adapters

If you're looking to integrate Sapient into an existing framework:

If your framework correctly implements PSR-7, you most likely do not need an adapter. However, some adapters provide convenience methods that make rapid development easier.

To learn more about adapters, see the documentation for AdapterInterface.

Sapient in Other Languages

Example 1: Signed PSR-7 Responses

This demonstrats a minimal implementation that adds Ed25519 signatures to your existing PSR-7 HTTP responses.

Server-Side: Signing an HTTP Response

<?php
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\Sapient\Sapient;
use ParagonIE\Sapient\CryptographyKeys\SigningSecretKey;
use Psr\Http\Message\ResponseInterface;

/**
 * @var ResponseInterface $response
 *
 * Let's assume we have a valid ResponseInterface object already.
 * (Most likely, after doing normal framework things.)  
 */

$sapient = new Sapient();
$serverSignSecret = new SigningSecretKey(
    Base64UrlSafe::decode(
        'q6KSHArUnD0sEa-KWpBCYLka805gdA6lVG2mbeM9kq82_Cwg1n7XLQXXXHF538URRov8xV7CF2AX20xh_moQTA=='
    )
);

$signedResponse = $sapient->signResponse($response, $serverSignSecret);

Client-Side: Verifying the Signature

<?php
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\Sapient\Sapient;
use ParagonIE\Sapient\CryptographyKeys\SigningPublicKey;
use ParagonIE\Sapient\Exception\{
    HeaderMissingException,
    InvalidMessageException
};
use Psr\Http\Message\ResponseInterface;

/**
 * @var ResponseInterface $response
 *
 * Let's assume we have a valid ResponseInterface object already.
 * (Most likely the result of an HTTP request to the server.)  
 */

$sapient = new Sapient();
$serverPublicKey = new SigningPublicKey(
    Base64UrlSafe::decode(
        'NvwsINZ-1y0F11xxed_FEUaL_MVewhdgF9tMYf5qEEw='
    )
);

try {
    $verified = $sapient->verifySignedResponse($response, $serverPublicKey);
} catch (HeaderMissingException $ex) {
    /* The server didn't provide a header. Discard and log the error! */
} catch (InvalidMessageException $ex) {
    /* Invalid signature for the message. Discard and log the error! */
}

Example 2: Mutually Signed JSON API with the Guzzle Adapter

This example takes advantage of an Adapter the provides the convenience methods described in ConvenienceInterface.

Client-Side: Sending a Signed Request, Verifying the Response

<?php
use GuzzleHttp\Client;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\Sapient\Adapter\Guzzle as GuzzleAdapter;
use ParagonIE\Sapient\Sapient;
use ParagonIE\Sapient\CryptographyKeys\SigningPublicKey;
use ParagonIE\Sapient\CryptographyKeys\SigningSecretKey;
use ParagonIE\Sapient\Exception\InvalidMessageException;

$http = new Client([
    'base_uri' => 'https://your-api.example.com'
]);
$sapient = new Sapient(new GuzzleAdapter($http));

// Keys
$clientSigningKey = new SigningSecretKey(
    Base64UrlSafe::decode(
        'AHxoibWhTylBMgFzJp6GGgYto24PVbQ-ognw9SPnvKppfti72R8By8XnIMTJ8HbDTks7jK5GmAnvtzaj3rbcTA=='
    )
);
$serverPublicKey = new SigningPublicKey(
    Base64UrlSafe::decode(
        'NvwsINZ-1y0F11xxed_FEUaL_MVewhdgF9tMYf5qEEw='
    )
);

// We use an array to define our message
$myMessage = [
    'date' => (new DateTime)->format(DateTime::ATOM),
    'body' => [
        'test' => 'hello world!'        
    ]
];

// Create the signed request:
$request = $sapient->createSignedJsonRequest(
    'POST',
     '/my/api/endpoint',
     $myMessage,
     $clientSigningKey
);

$response = $http->send($request);
try {
    /** @var array $verifiedResponse */
    $verifiedResponse = $sapient->decodeSignedJsonResponse(
        $response,
        $serverPublicKey
    );
} catch (InvalidMessageException $ex) {
    \http_response_code(500);
    exit;
}

Server-Side: Verifying a Signed Request, Signing a Response

 <?php
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\ServerRequest;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\Sapient\Adapter\Guzzle as GuzzleAdapter;
use ParagonIE\Sapient\Sapient;
use ParagonIE\Sapient\CryptographyKeys\SigningPublicKey;
use ParagonIE\Sapient\CryptographyKeys\SigningSecretKey;
use ParagonIE\Sapient\Exception\InvalidMessageException;

$http = new Client([
    'base_uri' => 'https://your-api.example.com'
]);
$sapient = new Sapient(new GuzzleAdapter($http));
 
$clientPublicKey = new SigningPublicKey(
    Base64UrlSafe::decode(
        'aX7Yu9kfAcvF5yDEyfB2w05LO4yuRpgJ77c2o9623Ew='
    )
);
$request = ServerRequest::fromGlobals();
try {
    /** @var array $decodedRequest */
    $decodedRequest = $sapient->decodeSignedJsonRequest(
        $request,
        $clientPublicKey
    );
} catch (InvalidMessageException $ex) {
    \http_response_code(500);
    exit;
}

/* Business logic goes here */

// Signing a response:
$serverSignSecret = new SigningSecretKey(
    Base64UrlSafe::decode(
        'q6KSHArUnD0sEa-KWpBCYLka805gdA6lVG2mbeM9kq82_Cwg1n7XLQXXXHF538URRov8xV7CF2AX20xh_moQTA=='
    )
);

$responseMessage = [
    'date' => (new DateTime)->format(DateTime::ATOM),
    'body' => [
        'status' => 'OK',
        'message' => 'We got your message loud and clear.'
    ]
];

$response = $sapient->createSignedJsonResponse(
    200,
    $responseMessage,
    $serverSignSecret
);
/* If your framework speaks PSR-7, just return the response object and let it
   take care of the rest. */
Comments
  • Adapters for Various Community Projects

    Adapters for Various Community Projects

    Feel free to suggest more, and/or develop your own.

    What to do if your framework doesn't support PSR-7:

    First, create your own PSR-7 implementation (or reuse e.g. Slim's). Then create methods that convert between the PSR-7 objects and your framework's non-compliant Request/Response objects.

    opened by paragonie-scott 15
  • Better Flexibility (not tied entirely to Guzzle) (Reported on Reddit instead of Github)

    Better Flexibility (not tied entirely to Guzzle) (Reported on Reddit instead of Github)

    https://www.reddit.com/r/PHP/comments/6inksq/paragoniesapient_secure_api_toolkit_for_php_7/

    There are two concerns with moving away from Guzzle:

    1. The create*() methods can't return an Interface, they have to return a Request or Response object, because they invoke new _____(). This is the technical concern.
    2. If we make it totally pluggable, we risk allowing developers to choose HTTP clients that contain vulnerabilities and then we'll get blamed for vulnerabilities existing, especially in bug bounty reports. This is the non-technical concern.

    Unless these clients provide a native createRequest() method, we'll have to continue returning Guzzle's.

    opened by paragonie-scott 13
  • [Query] Key size

    [Query] Key size

    Hi, I like the idea of this library and I'm doing an implementation for golang. But, I did start building with the key size 32 bytes then when I try to check Chronicle while using it I noticed the Secret Key is 64 bytes and Public Key is 32 bytes. Is docs here correct or there is a typo? Or 32 bytes is the minimum key size for Secret and Public keys?

    Thanks

    opened by vzool 5
  • Stream Bodies

    Stream Bodies

    PSR-7 uses streams for bodies, but the sodium primitives in use here (so far as I can tell) expect a whole string representation to work their magic. That's going to cause issues for large bodies.

    Ideally, the code should be able to encrypt, decrypt, sign, verify, and authenticate streams without having the whole stream in memory at once. I'm sure this complicates matters greatly, but is a boon when dealing with large bodies. Without this, there will exist a class of requests and responses that cannot be secured by Sapient.

    For reference, see PSR-7's StreamInterface, how Guzzle implements it, and Symfony's StreamedResponse class for returning streams to the client.

    opened by mcordingley 4
  • Content-Type

    Content-Type

    For encrypted bodies, should the "Content-Type" header refer to the plain-text or cipher-text? If the former, it's technically lying about the contents and leaking information. If the latter, then the "Content-Type" of the body should be safely stowed someplace for reinstatement upon decryption.

    opened by mcordingley 3
  • Introduce a generic adapter

    Introduce a generic adapter

    See the discussion in #6, for when no suitable adapter is provided (i.e. no Guzzle). We just need to provide implementations for the following classes:

    • RequestInterface
    • ResponseInterface
    • StreamInterface
    opened by paragonie-scott 3
  • Verify Signed request consumes body

    Verify Signed request consumes body

    With some PSR-7 implementations, the body stream is non-rewindable. This means that when Sapient reads it to verify the signature it removes it from the request.

    I think that cloning the stream would solve this problem but not sure if there are other considerations to this?

    opened by carnage 1
  • Add reference to symfony bundle

    Add reference to symfony bundle

    Hi,

    I really like your work on PHP ecosystem and I would like to help you to spread the word.

    As a developer who really enjoy Symfony framework, I'm working on a bundle to help using and configure sapient as an easy way for Symfony.

    It is still in development and I'm still thinking on how to make usage of this bundle easy.

    My first MVP is to automate generation of key pair and sign all response automatically. I will also add a feature to encrypt and verify response.

    I'm open to any suggestion and improvement.

    Regards,

    opened by lepiaf 1
  • Consider a better sealing approach

    Consider a better sealing approach

    Currently, we're using crypto_box_seal() which means:

    • Algorithm: XSalsa20-Poly1305
    • Key exchange: ECDH over Curve25519, then HSalsa20
    • Nonce: BLAKE2b(ephemeralPK || recipientPK, null, 24)

    Another approach might be:

    • Algorithm: XChaCha20-Poly1305
    • Key exchange: Like crypto_kx(), but for 56 bytes (the first 32 go into the key).
    • Nonce: The last 24 bits of the key exchange output.
    opened by paragonie-scott 1
  • Built-in method for verifying symmetric authentication uses wrong header

    Built-in method for verifying symmetric authentication uses wrong header

    The method \ParagonIE\Sapient\Sapient::verifySymmetricAuthenticatedResponse uses the header for Ed25519 public-key signatures. This commit fixes that and adds a test for the failing scenario (current code).

    opened by marcusirgens 0
Releases(v1.1.2)
  • v1.1.2(Jun 26, 2021)

    Fixes #17 via #19 (thanks @carnage!)

    This supersedes the bad release v1.1.1 which has been deleted because the syntax of cloning created misbehavior.

    - $body = (string) clone $response->getBody();
    + $body = (string) (clone $response)->getBody();
    
    Source code(tar.gz)
    Source code(zip)
  • v1.1.0(Apr 17, 2021)

  • v1.0.2(Nov 6, 2019)

    • Merged #15 which fixes the header selection for Symmetric Authentication
    • Now unit tested against PHP 7.3, 7.4snapshot, and nightly.
    • Boyscouted the test suite.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.1(May 2, 2018)

  • v1.0.0(Jun 25, 2017)

  • v0.5.0(Jun 25, 2017)

    This may be the final pre-1.0 release. Some changes:

    • Added some documentation.
    • Reorganized some of the code in a non-BC-breaking way.
    • Renamed a few methods that weren't documented, to make our public API more consistent.
    • Improved the README.

    I'll probably do a little more documentation work then tag v1.0 shortly.

    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Jun 23, 2017)

    • We've introduced a generic adapter, and separated the convenience methods (create*()) into their own interface.
    • You no longer need an adapter if you're only passing PSR-7 compliant objects that implement RequestInterface or ResponseInterface to the appropriate methods; it defaults to the generic adapter.
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Jun 22, 2017)

    • (#6) Sapient is no longer a subclass of the Guzzle HTTP client. BC Break
      • Instead, it expects an instance of a new AdapterInterface passed to the constructor, which allows multiple HTTP implementations to be used.
      • Future scope: Make this argument optional, default to a generic adapter (which does not yet exist).
    • (#2) Public key encryption is now based on XChaCha20 instead of XSalsa20. BC Break
      • Our key and nonce derivation now use a similar construction to libsodium's crypto_kx
        • Difference: BLAKE2b output size is 56 bytes instead of 32
        • The first 32 are the key, the last 24 are the nonce
        • This maps a 32 byte shared secret and two 32 byte public keys, passed through a secure hash function, to a 56-byte pseudorandom output; this should prove to be secure so long as X25519 is secure
    Source code(tar.gz)
    Source code(zip)
  • v0.2.0(Jun 21, 2017)

  • v0.1.0(Jun 21, 2017)

Owner
Paragon Initiative Enterprises
Technology should support your ambitions, not hinder them. We are a team of technology consultants that specialize in application security.
Paragon Initiative Enterprises
PHP Secure Communications Library

phpseclib - PHP Secure Communications Library Supporting phpseclib Become a backer or sponsor on Patreon One-time donation via PayPal or crypto-curren

null 4.9k Jan 7, 2023
A multitool library offering access to recommended security related libraries, standardised implementations of security defences, and secure implementations of commonly performed tasks.

SecurityMultiTool A multitool library offering access to recommended security related libraries, standardised implementations of security defences, an

Pádraic Brady 131 Oct 30, 2022
Quickly and easily secure HTML text.

Larasane Quickly sanitize text into safe-HTML using fluid methods. Requirements PHP 7.4, 8.0 or later. Laravel 7.x, 8.x or later. Installation Just fi

Italo 40 Jul 20, 2021
PHP Secure Headers

Secure Headers Add security related headers to HTTP response. The package includes Service Providers for easy Laravel integration. Version Installatio

null 431 Dec 26, 2022
PHP Secure Configuration Checker

PHP Secure Configuration Checker Check current PHP configuration for potential security flaws. Simply access this file from your webserver or run on C

SektionEins GmbH 799 Nov 15, 2022
Create cryptographically secure pseudo-random numbers, and manage big integers

laminas-math This package is considered feature-complete, and is now in security-only maintenance mode, following a decision by the Technical Steering

Laminas Project 23 Nov 24, 2022
An experimental object oriented SSH api in PHP

PHP SSH (master) Provides an object-oriented wrapper for the php ssh2 extension. Requirements You need PHP version 5.3+ with the SSH2 extension. Insta

Antoine Hérault 355 Dec 6, 2022
Api random address

RandomAddress what actually it does? Its scrap Address from Fakeaddress and gives output in json format for api use. This address are working many pla

Nitin1818 6 Dec 28, 2022
HTML/PHP/CSS website that tracks two API data

Detailed instructions on how to build and run Step 1: download XAMPP for a live web server XAMPP download 1 XAMP download 2 Step 2: Download all files

Winsor Tse 0 Jun 2, 2022
API in PHP for DDoS Attacks (sends a command to a SSH Server from a URL)

SSH-PHP-API API in PHP for DDoS Attacks (sends a command to a SSH Server from a URL) [Install on Ubuntu 20.04: apt install apache2 php php-fpm php-ssh

Вентокс 3 Sep 23, 2022
Run locally to export crypto tx data from crypto exchanges using their api connections, and process into a normalised format.

CryptoCredible The missing crypto-exchange data exporter tldr: run locally to export your crypto tx data from popular exchanges via api connections. E

Lee Overy 6 Apr 6, 2022
SЁCU is a public API to store self-destructing data payloads with url shortener and handle anonymous chat-rooms.

SЁCU Introduction SЁCU is a public API to store self-destructing data payloads. This repository includes only backend part using Laravel framework. Fr

SЁCU 27 Nov 21, 2022
A cryptography API wrapping the Sodium library, providing a simple object interface for symmetrical and asymmetrical encryption, decryption, digital signing and message authentication.

PHP Encryption A cryptography API wrapping the Sodium library, providing a simple object interface for symmetrical and asymmetrical encryption, decryp

null 19 Dec 31, 2022
Secure API Toolkit

Sapient: Secure API toolkit Sapient secures your PHP applications' server-to-server HTTP(S) traffic even in the wake of a TLS security breakdown (comp

Paragon Initiative Enterprises 315 Jan 3, 2023
Easy to use cryptographic framework for data protection: secure messaging with forward secrecy and secure data storage. Has unified APIs across 14 platforms.

Themis provides strong, usable cryptography for busy people General purpose cryptographic library for storage and messaging for iOS (Swift, Obj-C), An

Cossack Labs 1.6k Jan 6, 2023
php binding for IUP toolkit

php-iup php-ffi experiment php7.4 interface to the IUP toolkit for building GUI's. Description IUP-Toolkit IUP is a multi-platform toolkit for buildin

Shubham Chaudhary 20 Nov 13, 2022
QPM, the process management framework in PHP, the efficient toolkit for CLI development. QPM provides basic daemon functions and supervision mechanisms to simplify multi-process app dev.

QPM QPM全名是 Quick(or Q's) Process Management Framework for PHP. PHP 是强大的web开发语言,以至于大家常常忘记PHP 可以用来开发健壮的命令行(CLI)程序以至于daemon程序。 而编写daemon程序免不了与各种进程管理打交道。Q

Comos 75 Dec 21, 2021
Twill is an open source CMS toolkit for Laravel that helps developers rapidly create a custom admin console that is intuitive, powerful and flexible. /// Chat with us and others on Spectrum: https://spectrum.chat/twill

About Twill Twill is an open source Laravel package that helps developers rapidly create a custom CMS that is beautiful, powerful, and flexible. By st

AREA 17 3k Jan 6, 2023
A toolkit for using self-hosted Natural Language Processing with Elasticsearch and WordPress

Natural Language Processing Tools for WordPress A toolkit for using self-hosted Natural Language Processing in WordPress This plugin is a Proof of Con

Ricardo Moraleida 8 Dec 23, 2022
A simple PHP Toolkit to parallel generate combinations, save and use the generated terms to brute force attack via the http protocol.

Brutal A simple PHP Toolkit to parallel generate combinations, save and use the generated terms to apply brute force attack via the http protocol. Bru

Jean Carlo de Souza 4 Jul 28, 2021