🤖 Id obfuscation based on Knuth's multiplicative hashing method for PHP.

Overview

Optimus id transformation

Latest Stable Version Build Status Coverage Status

With this library, you can transform your internal id's to obfuscated integers based on Knuth's integer hash. It is similar to Hashids, but will generate integers instead of random strings. It is also super fast.

Installation

Install using composer:

composer require jenssegers/optimus

If you will be running your code on a 32 bit system or will be working with large prime numbers it is suggested that you install the GMP extension. For debian/ubuntu you can install the extension with one of these commands:

apt-get install php7.0-gmp
apt-get install php7.1-gmp
apt-get install php7.2-gmp

Usage

To get started you will need 3 things;

  • Large prime number lower than 2147483647
  • The inverse prime so that (PRIME * INVERSE) & MAXID == 1
  • A large random integer lower than 2147483647

Luckily for you, I have included a console command that can do all of this for you. To get started, just run the following command:

> php vendor/bin/optimus spark

Prime: 2123809381
Inverse: 1885413229
Random: 146808189

If you prefer to choose your own prime number (from this list for example), you can pass it to the command to calculate the remaining numbers:

> php vendor/bin/optimus spark 1580030173

Prime: 1580030173
Inverse: 59260789
Random: 1163945558

Using those numbers, you can start creating instances of Optimus($prime, $inverted, $random):

use Jenssegers\Optimus\Optimus;

new Optimus(1580030173, 59260789, 1163945558);

NOTE: Make sure that you are using the same constructor values throughout your entire application!

Encoding and decoding

To encode id's, use the encode method:

$encoded = $optimus->encode(20); // 1535832388

To decode the resulting 1535832388 back to its original value, use the decode method:

$original = $optimus->decode(1535832388); // 20

Framework Integrations

Laravel

This is an example service provider which registers a shared Optimus instance for your entire application:

<?php

namespace App\Providers;

use Jenssegers\Optimus\Optimus;
use Illuminate\Support\ServiceProvider;

class OptimusServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(Optimus::class, function ($app) {
            return new Optimus(1580030173, 59260789, 1163945558);
        });
    }
}

Once you have created the service provider, add it to the providers array in your config/app.php configuration file:

App\Providers\OptimusServiceProvider::class,

Laravel's automatic injection will pass this instance where needed. Example controller:

<?php

namespace App\Http\Controllers;

use Jenssegers\Optimus\Optimus;
use App\Http\Controllers\Controller;

class UserController extends Controller
{
    public function show($id, Optimus $optimus)
    {
        $id = $optimus->decode($id);
    }
}

More information: https://laravel.com/docs/5.3/container#resolving

Third-party integrations

An integration with Laravel is provided by the propaganistas/laravel-fakeid package.

Laravel Optimus with multiple connections provided by the cybercog/laravel-optimus package.

An integration with Silex 2 is provided by the jaam/silex-optimus-provider package.

An integration with Laravel is provided by the elfsundae/laravel-hashid package.

A PSR-15 middleware provided by the icanhazstring/optimus-middleware package.

Security contact information

To report a security vulnerability, follow these steps.

Comments
  • Inconsistent encoding/decoding

    Inconsistent encoding/decoding

    I'm not sure whether I'm doing something wrong, but in an initial test of the code using the numbers in the readme, the mappings I get are:

    0       =>1163945558    =>0
    1       =>458047115     =>0
    2       =>2033899500    =>0
    3       =>1609067713    =>4
    4       =>1037370658    =>0
    5       =>327342599     =>0
    6       =>1886417784    =>8
    7       =>1448937565    =>8
    8       =>873111742     =>0
    9       =>184056211     =>16
    

    The code that I'm using to generate this simply encodes the integer and then decodes it again:

    <?php
    
    require __DIR__.'/vendor/autoload.php';
    
    use Jenssegers\Optimus\Optimus;
    
    $optimus = new Optimus(1580030173, 59260789, 1163945558);
    
    for($i = 0; $i < 10; $i++)
    {
            $enc = $optimus->encode($i);
            echo $i . "\t=> " . $enc . "\t=> " . $optimus->decode($enc) . "\n";
    }
    

    I then used spark to generate a different triplet of parameters corresponding to new Optimus(297501427, 217343547, 139284431) and got:

    0       => 139284431    => 0
    1       => 435604796    => 0
    2       => 725240361    => 0
    3       => 1031784214   => 0
    4       => 1319322627   => 0
    5       => 1357169008   => 0
    6       => 1646812797   => 0
    7       => 1953356650   => 0
    8       => 93411415     => 8
    9       => 400217412    => 16
    

    Also, for me, both the README and spark generated parameters fail the test on the README and give (PRIME * INVERSE) & MAXID = 0. Is there perhaps a step I have missed or a particular PHP plugin I need to have active for it to work?

    opened by jeteon 12
  • Configurable bit length

    Configurable bit length

    This PR is in regards to #35

    Currently Optimus only supports encoding of integers up to 2^31-1 (e.g. 0 to 2147483647). This PR allows you to choose a different range for Optimus to use.

    This is done by specifying a bit length, where the default bit length is 31, (to preserve current behaviour.) For example, the following will setup Optimus for 48 bit integers (e.g. 0 to 281474976710655).

    $ php bin/optimus spark --bits 48
    Prime: 59927384369137
    Inverse: 166258800519441
    Random: 3091726089
    Bits: 48
    
        new Optimus(59927384369137, 166258800519441, 3091726089, 48);
    

    The method for encoding values only works where the maximum integer is a power of 2, which is why the argument for initialising Optimus is bits rather than a maximum integer. (Please correct me if I'm wrong about the maths Optimus uses not working with arbitrary integer ranges.)

    On the command line you cannot specify a bit length of 63 or higher -- the system breaks with numbers that large.

    The one change I would like to make, but have not to preserve current logic, is for Optimus::encode() to throw an exception if you attempt to encode an integer greater that the bit length would allow. Or at least trigger a warning. This is because people may be encoding values without realising they may decode to a different value.

    If the PR is tentatively accepted, I will also update the README.md.

    I'm very open to any suggested revisions, or suggestion for a completely different strategy.

    opened by courtney-miles 6
  • deprecate EOL php versions

    deprecate EOL php versions

    Hi @jenssegers

    Highlights

    • Remove support for EOL php versions (7.0, 7.1, 7.2, 7.3)
    • See package usage stats here
    • Upgrade phpunit to 9.x
    • Test on php 8.1 as well
    • Remove support for symfony/console v3 and v4
    • Allow symfony/console v6.x

    Thanks.

    opened by ankurk91 5
  • Command not working

    Command not working

    Not sure why this is happening, but I just installed Optimus in a fresh Laravel 5.1 install and once I run php vendor/bin/optimus spark.

    This is the result

    SRC_DIR="`pwd`"
    cd "`dirname "$0"`"
    cd '../jenssegers/optimus'
    BIN_TARGET="`pwd`/optimus"
    cd "$SRC_DIR"
    "$BIN_TARGET" "$@"
    

    And I don't see any number being generated. Running the command directly from vendor/bin does not help.

    opened by MartijnThomas 5
  • Separate logic of spark command

    Separate logic of spark command

    Hi

    Could you please split out the generation logic of the spark command into a separate class so that it can be called from elsewhere more easily?

    I'm building a Laravel wrapper for Optimus.

    Thanks.

    opened by Propaganistas 5
  • How unique is unique

    How unique is unique

    Hi,

    I've been looking for a decent, basic explanation about hashing, but it's a bit hard to find. I know that generating random values will always have a chance to return duplicate results. MD5 hashes, for example are also random, but not unique...

    My big question is if this also applies to the method of hashing that you use. Will there be even the slightest chance that the resulting hash of value X will be the same as the hash of value Y?

    Is there a certain rule behind this idea? Is there a requirement of the amount of possible character combinations used for the resulting hash (like hashids uses a pool of characters instead of just numbers) versus the number of characters in the original value?

    Or should I always check the database for duplicates?

    I hope someone can shine a light on this for me... :)

    opened by ivanvermeyen 4
  • Extract numbers generation into separate class

    Extract numbers generation into separate class

    As suggested previously in #9 .

    Derived packages can as such use a consistent number generation logic instead of needing to define it themselves.

    Since this package already includes "Optimus" and its life essence "Spark", I thought "Energon" (Transformer's fuel) would be a great addition :-)

    Note that the optimus phar still needs to be rebuilt.

    opened by Propaganistas 4
  • chore: replace travis-ci with gh-actions

    chore: replace travis-ci with gh-actions

    Hi @jenssegers ,

    Ref #56

    Lets replace Travis-ci with GitHub actions.

    Highlights

    • Update phpunit testcases - replace deprecated methods
    • Fix readme.md markdown
    • Add download count badge
    • Test on php 8.0 (skip coverage for now)

    Will try to fix any failing test.

    Next todos

    • Allow symfony/console v6.x
    • Remove support for less than php 7.4
    • Test on php 8.1
    • Test with phpunit v9

    Thanks

    opened by ankurk91 3
  • chore: replace travis-ci with gh-actions

    chore: replace travis-ci with gh-actions

    Hi @jenssegers,

    Lets replace travis-ci with GitHub actions.

    There are few warning in logs

    assertInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsInt() instead.
    

    i can fix them, but first let me know if you are willing to merge this PR.

    opened by ankurk91 3
  • GMP is slower than Native

    GMP is slower than Native

    Hello,

    I was using a base64+sodium crypt for "hiding" the IDs. The only issue is that base64 is not "URL friendly". I ran the benchmark and it was a big surprise seeing that Optimus+GMP is quite slow in comparison with my approach. With de-obfuscation is a little worse than obfuscation.

    Is there a reason why we should not be using the native implementation for integers ?

    Optim (php): 2.5046479701996
    Optim (gmp): 4.8686540126801
    Crypt+Base:  2.9968030452728
    SHA1:        1.3528130054474
    MD5:         1.3124771118164
    

    Environment:

    # Ubuntu
    No LSB modules are available.
    Distributor ID: Ubuntu
    Description:    Ubuntu 16.04.5 LTS
    Release:        16.04
    Codename:       xenial
    
    # PHP
    libgmp10/xenial,now 2:6.1.0+dfsg-2 amd64 [installed]
    php7.2-gmp/xenial,now 7.2.13-1+ubuntu16.04.1+deb.sury.org+1 amd64 [installed]
    
    # GMP Version
    GMP_VERSION=6.1.0
    

    Code:

    <?php
    
    require __DIR__ . '/../../vendor/autoload.php';
    
    $salt  = base64_decode('54b20855074befc5a7fa7b93a6925a7d96748fd4');
    $nonce = base64_decode('SjIwnGuPPGV3g8tSCx6d6+ov8svAWm0p');
    $key   = base64_decode('nZZ/WZIr2M4amGAZ2qpccID2T2gX74Q8bDv3uXWV6/Q=');
    
    $optimus = new \Jenssegers\Optimus\Optimus(1752266083, 78885963, 324523680);
    
    $t0 = microtime(true);
    for ($i=0; $i<1000000; $i++) {
        $optimus->encode(1000);
    }
    
    $t1 = microtime(true);
    $optimus->setMode('gmp');
    for ($i=0; $i<1000000; $i++) {
        $optimus->encode(1000);
    }
    
    $t2 = microtime(true);
    for ($i=0; $i<1000000; $i++) {
        base64_encode(sodium_crypto_secretbox($salt . '1000', $nonce, $key));
    }
    
    $t3 = microtime(true);
    for ($i=0; $i<1000000; $i++) {
        sha1(1000);
    }
    
    $t4 = microtime(true);
    for ($i=0; $i<1000000; $i++) {
        md5(1000);
    }
    
    $t5 = microtime(true);
    
    echo 'Optim (php): ' . ($t1 - $t0) . "\n";
    echo 'Optim (gmp): ' . ($t2 - $t1) . "\n";
    echo 'Crypt:       ' . ($t3 - $t2) . "\n";
    echo 'SHA1:        ' . ($t4 - $t3) . "\n";
    echo 'MD5:         ' . ($t5 - $t4) . "\n";
    
    opened by spartan 3
  • Laravel 7.0 incompatibilities

    Laravel 7.0 incompatibilities

    The last released Laravel has updated his internal dependencies based on Symfony to the 5.* series, including the console.

    As for this library, it still requires the 3.0 && 4.0 series, preventing to upgrade the framework.

    For what I could see in the library, the only exception is the InvalidPrimeException, which extends from RangeException, which extends from RuntimeException, which extends from Exception, which at last implements the Throwable interface. Being that the only requirement (AFAIK), this can be satisfiable by just adding |^5.0 to the composer.json file under the symfony/console dependency.

    Happy to PR it if you're busy

    opened by Garanaw 2
  • Implicit conversion from float ... to int loses precision (PHP 8.1)

    Implicit conversion from float ... to int loses precision (PHP 8.1)

    Error with PHP 8.1.11 on this line https://github.com/jenssegers/optimus/blob/a062ac06598e72a25a17ea6cba87f9e02d07b1bc/src/Optimus.php#L68, e.g.

    Implicit conversion from float 3.802099463828355E+21 to int loses precision

    opened by diggy 2
  • Possible for attacker to determine prime, inverse, and random number?

    Possible for attacker to determine prime, inverse, and random number?

    Hi, thanks for this package!

    I'm looking at this as an alternative to Hashids, which I'm avoiding due in part to:

    Clearly if you have access to the salt, it is trivial to calculate the function in either direction so the basis for proving or disproving property 2 lies in how easy it is for an attacker to discover the secret salt.

    and

    " anyone using this library should assume that id's encoded by this library are fully reversible and as such it offers no security over using the raw integer ids.

    source: https://carnage.github.io/2015/08/cryptanalysis-of-hashids:


    With Knuth's integer hash, it seems like it would be impossible (or at least a few orders of magnitude more difficult) for an attacker to determine the prime number, inverse, and random number and defeat the obfuscation.

    Am I correct in assuming this?

    I couldn't find anything discussing this online. Are you aware of any research or discussion on this topic?

    Obviously key obfuscation of any kind is no guarantee of security, and I have to develop the application such that it ultimately doesn't matter if an attacker gets a real id. That said, it would make me feel better to have a sense of just how hard this obfuscation would be to break, especially relative to Hashids.

    Thank you for any info you can provide!

    opened by nathan-io 3
  • Symfony-Flex recipe

    Symfony-Flex recipe

    Hi!

    I like to use Optimus as an easy way to obfuscate Ids. I don't like to configure it every time.

    It would be great to have a Symfony-Flex recipe that would provide an autowireable service with pre-configured random values. A ParamConverter could also be provided by the recipe.

    opened by ckrack 3
  • Why limit integer support to 2147483647?

    Why limit integer support to 2147483647?

    I have adopted Optimus assuming it would support integers up to 2^32-1.

    Is there a technical reason why it is limited to 2^31-1? Is it that the method only works when the max integer is prime number?

    I would be happy to submit a PR to make the range configurable. But I don't fully understand the maths to know if there's a logical problem with this.

    In terms of the maths, why is a large prime preferred over a small prime?

    opened by courtney-miles 10
  • Minimum or fixed integer length

    Minimum or fixed integer length

    I need some level of control over the generated integer length. I don't know if this is even possible as I don't see any updates on https://github.com/jenssegers/optimus/issues/15. If a fixed length is not possible, maybe a minimum length can be easier to implement?

    opened by jonagoldman 1
Releases(v1.1.1)
Owner
Jens Segers
Head of Engineering at CHEQROOM
Jens Segers
Strong cryptography tools and password hashing

laminas-crypt ???? Русским гражданам Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как

Laminas Project 29 Dec 15, 2022
Test a method against a list of XSS known.

php-xss-tests Test a method against a list of XSS known. How to run Just execute "run.sh", it will start a docker container to do all stuff. How I kno

null 1 Oct 25, 2021
PHPIDS (PHP-Intrusion Detection System) is a simple to use, well structured, fast and state-of-the-art security layer for your PHP based web application

PHPIDS PHPIDS (PHP-Intrusion Detection System) is a simple to use, well structured, fast and state-of-the-art security layer for your PHP based web ap

null 752 Jan 3, 2023
HTML sanitizer, written in PHP, aiming to provide XSS-safe markup based on explicitly allowed tags, attributes and values.

TYPO3 HTML Sanitizer ℹ️ Common safe HTML tags & attributes as given in \TYPO3\HtmlSanitizer\Builder\CommonBuilder still might be adjusted, extended or

TYPO3 GitHub Department 18 Jul 18, 2022
Replaces Laravel's built-in encryption with an encryption based on AWS KMS

Laravel Kms Encryption Introduction This package replaces Laravel's built-in encryption with an encryption based on AWS KMS. Two major features provid

Arnaud Becher 3 Oct 26, 2021
php-chmod is a PHP library for easily changing permissions recursively.

PHP chmod php-chmod is a PHP library for easily changing the permissions recursively. Versions & Dependencies Version PHP Documentation ^1.1 ^7.4 curr

Mathias Reker ⚡️ 5 Oct 7, 2022
PHP 5.x support for random_bytes() and random_int()

random_compat PHP 5.x polyfill for random_bytes() and random_int() created and maintained by Paragon Initiative Enterprises. Although this library sho

Paragon Initiative Enterprises 8k Jan 5, 2023
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
Simple Encryption in PHP.

php-encryption composer require defuse/php-encryption This is a library for encrypting data with a key or password in PHP. It requires PHP 5.6 or new

Taylor Hornby 3.6k Jan 3, 2023
Standards compliant HTML filter written in PHP

HTML Purifier HTML Purifier is an HTML filtering solution that uses a unique combination of robust whitelists and aggressive parsing to ensure that no

Edward Z. Yang 2.7k Jan 5, 2023
A database of PHP security advisories

PHP Security Advisories Database The PHP Security Advisories Database references known security vulnerabilities in various PHP projects and libraries.

null 1.9k Dec 18, 2022
A php.ini scanner for best security practices

Scanner for PHP.ini The Iniscan is a tool designed to scan the given php.ini file for common security practices and report back results. Currently it

psec.io 1.5k Dec 5, 2022
㊙️ AntiXSS | Protection against Cross-site scripting (XSS) via PHP

㊙️ AntiXSS "Cross-site scripting (XSS) is a type of computer security vulnerability typically found in Web applications. XSS enables attackers to inje

Lars Moelleken 570 Dec 16, 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
TCrypto is a simple and flexible PHP 5.3+ in-memory key-value storage library

About TCrypto is a simple and flexible PHP 5.3+ in-memory key-value storage library. By default, a cookie will be used as a storage backend. TCrypto h

timoh 57 Dec 2, 2022
Fetches random integers from random.org instead of using PHP's PRNG implementation

TrulyRandom Composer-compatible library to interact with random.org's API in order to generate truly random lists of integers, sequences of integers,

Erik Wurzer 46 Nov 25, 2022
PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically.

PHPGGC: PHP Generic Gadget Chains PHPGGC is a library of unserialize() payloads along with a tool to generate them, from command line or programmatica

Ambionics Security 2.5k Jan 4, 2023
Let's Encrypt/ACME Command Line client written in PHP

Acme PHP Acme PHP is a simple yet very extensible CLI client for Let's Encrypt that will help you get and renew free HTTPS certificates. Acme PHP is a

Acme PHP 539 Dec 30, 2022
PHP Malware Finder

PHP Malware Finder _______ __ __ _______ | ___ || |_| || | | | | || || ___| | |___| || || |___ Webshell finder, |

NBS System 205 Dec 24, 2022