Giter Site home page Giter Site logo

jenssegers / optimus Goto Github PK

View Code? Open in Web Editor NEW
1.3K 27.0 73.0 2.46 MB

๐Ÿค– Id obfuscation based on Knuth's multiplicative hashing method for PHP.

Home Page: https://jenssegers.com

License: MIT License

PHP 100.00%
obfuscation ids optimus hashids transformations laravel

optimus's Introduction

Optimus id transformation

Packagist Downloads Build Coverage

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.4-gmp
apt-get install php8.0-gmp
apt-get install php8.1-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

Security contact information

To report a security vulnerability, follow these steps.

License

The MIT License.

optimus's People

Contributors

amustill avatar ankurk91 avatar barryvdh avatar ckrack avatar courtney-miles avatar elfsundae avatar eugene-borovov avatar icanhazstring avatar jenssegers avatar llevvi avatar lotuashvili avatar notfloran avatar peter279k avatar propaganistas avatar robinvdvleuten avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

optimus's Issues

Information on collisions

Hey, Jens.
I'd like to know if collisions are possible in optimus.

Some info in README would be nice.

Generated number length?

Is it possible to control the generated number length?

Eg. say I need 7-digits unique numbers, generated based on some integer eg.

  1 --> 1234567
 23 --> 7654321
100 --> 0112110

?

Please add SSL certificate to repository

Getting error "...Your configuration does not allow connections to http...".

Solution was to add
"config": {
"secure-http": false
}
to composer.json

For security reasons, please add an SSL certificate to repository.

Foreign key ($value) NULL

Hello,

I have a table "Ratings" which has a foreign key article_id INT(10) UNSIGNED NULL.

Because the FK can be NULL, I always get an invalid argument exception: Argument should be an integer.

I saw that in Optimus.php in encode/decore function you are checking if the value is numeric or not.
I'm new to PHP so I'm just asking why not to check if the foreign key is for example null? Is this a bug, or I'm doing something wrong and a FK shouldn't be NULL?

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... :)

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?

Encoding/Decoding on Windows 64Bit machine

Hi Jens,
I'm currently trying out Optimus on a Windows 64Bit Machine with Laravel 5.1.20 and am having a bit of trouble decoding my numbers :

PRIME=1580030173
INVERSE=59260789
RANDOM=1163945558

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
10=>1755651828=>8
11=>1318237993=>12
12=>792742922=>16
13=>221111663=>16
14=>1662696000=>16
15=>1103647397=>15
16=>666233734=>16

Could I be missing something ?
Thanks for the help ;)

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";

Laravel 5.1 problem

Hi,
I'd like to use it in Laravel but all the time have a problem with namespace,
if I add it in the controller
use Jenssegers\Optimus;
get error 'Class 'jenssegers\optimus' not found'
but in vendor folder it is present with all files
I did comopser dump-autoload
it is the only package, I have problem with

segmentation fault error

I am getting the following error when I run the command php vendor/bin/optimus spark

[1]    23864 segmentation fault  php vendor/bin/optimus spark

I am using PHP 7.1.8 (cli).

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 #15.
If a fixed length is not possible, maybe a minimum length can be easier to implement?

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?

facade

your package don't have any facade? How can i use statically encode method?

Generate random prime

Instead of letting the user pick a prime, why not find a random one with php? Or is that more insecure?

Something like this in the Spark command for example, when the prime is empty (Although random_int would be safer in PHP7)

protected function findRandomPrime()
{
    $min = 1e6;
    $max = 2147483647;
    for ($i=rand($min, $max); $i < $max; $i++) {
        if ($this->isPrime($i)) {
            return $i;
        }
    }
}

/**
 * From http://stackoverflow.com/a/16763365/444215
 * @param $num
 * @return bool
 */
protected function isPrime($num){
    /**
     * if the number is divisible by two, then it's not prime and it's no longer
     * needed to check other even numbers
     */
    if($num % 2 == 0) {
        return false;
    }
    /**
     * Checks the odd numbers. If any of them is a factor, then it returns false.
     * The sqrt can be an aproximation, hence just for the sake of
     * security, one rounds it to the next highest integer value.
     */
    for($i = 3; $i <= ceil(sqrt($num)); $i = $i + 2) {
        if($num % $i == 0)
            return false;
    }

    return true;
}

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

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!

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.

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.

Segmentation fault: 11

Hi

Upon executing the spark command, I get the following error:

Segmentation fault: 11

Debugging reveals that the problem resides in the BigInteger calls to phpseclib...

Any ideas on this? I'm on Mac OS X 10.10.

What's wrong?

Hi.
Using the example numbers:

$optimus = new Jenssegers\Optimus\Optimus(1580030173, 59260789, 1163945558);

$id = "1000000000"; //10 digits
$encoded = $optimus->encode($id);
$original = $optimus->decode($encoded);
print_r(array($id, $encoded, $original), true); 
// ["1000000000", 1768948822, 1000000000]

$id = "10000000000"; //11 digits
$encoded = $optimus->encode($id);
$original = $optimus->decode($encoded);
print_r(array($id, $encoded, $original), true); 
// ["10000000000", 2109978198, 856559616]


$id = "100000000000"; //12 digits
$encoded = $optimus->encode($id);
$original = $optimus->decode($encoded);
print_r(array($id, $encoded, $original), true); 
// ["100000000000", 1163945558, 0]

My IDs are MySQL's bigint that can be 20 digits long! Optimus is not for me?
What am I missing? There is a length limit?


php -v
PHP 5.3.2-1ubuntu4.30 with Suhosin-Patch (cli) (built: Apr 17 2015 15:01:29)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

uname -a
Linux picard 2.6.32-74-server #142-Ubuntu SMP Tue Apr 28 10:12:19 UTC 2015 x86_64 GNU/Linux


I had to change line 108 at src/Optimus.php to get it working:

if (! in_array($mode, [static::MODE_GMP, static::MODE_NATIVE])) {

to

if (! in_array($mode, array(static::MODE_GMP, static::MODE_NATIVE)) ) {

Thanks in advance.

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.

delete / forceDelete bug

so for example in model

public function getIdAttribute($value)
{
    return app('optimus')->encode($value);
}

and in the controller

public function destroy($id)
{
    Post::find(app('optimus')->decode($id))->delete();
    // or
    Post::withTrashed()->find(app('optimus')->decode($id))->forceDelete();
}

all other operations works without an issue get, update, show, etc.. , but for some reason when trying to delete the item, it doesnt get reflected in the db, even that both delete & forceDelete return true.

using plain ids works as expected, but when used by optimus i have no luck ๐Ÿ˜ข .

have anyone encountered such an issue b4 ? or what might be the solution for it ?

Documentation

You explain very well on how to use it, but not what it actually does/how it gets done what it does. It's all relatively small, so I (think I) figured it out by glancing at the source, still would be nice if you could expand.

  • const MAX_INT = 2147483647; Does that include this Mersenne prime or one less?
  • why chmod 755? Everyone should be able to execute/recalculate the ids?

I'm getting this BindingResolutionException in Laravel

Hello,
This is probably not an issue in the optimus class itself. I must be doing something silly or forgetting to do something.

I have this controller called "VendorsControllers" where I inject Optimus in the constructor. When I try to go to the index page I get this:

BindingResolutionException in Container.php line 850:
Unresolvable dependency resolving [Parameter #0 [ <required> $prime ]] in class Jenssegers\Optimus\Optimus

Here's an snippet of my controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;

use App\Vendor;

use Auth;

use Jenssegers\Optimus\Optimus;

class VendorController extends Controller
{

    protected $optimus;

    public function __construct(Optimus $opt)
    {
        $this->optimus = $opt;
    }

    public function index()
    {
        $user = Auth::user();
        $vendors = Vendor::with('user')->get();

        foreach($vendors as $key => $vendor){
          $vendor->id = $this->optimus->encode($vendor->id);
          $vendors[$key] = $vendor;
        }

        return view('vendors/index', compact('vendors'));
    }

And here's my Optimus service provider:


<?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(2123809381, 1885413229, 146808189);
        });
    }
}

Should I register this service provider? I wasn't sure if it was really needed since Optimus documentation doesn't mention that.

Thanks in advance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.