Limit Laravel 5 Encryption length - php

I have a Laravel5 web application of Business directory.
When I Encrypting a value like
$cryptval = Crypt::encrypt(1);
result = eyJpdiI6IndhaFZFNlhIRDlURzdXanJVMEhBM0E9PSIsInZhbHVlIjoidWF3VzRFZDhyRHltUlwveDdyV0VVWnc9PSIsIm1hYyI6IjE5YjA2YWIyN2Q0MTBlYjdhNDJiNDE5ZjY2OGQ2MDA2NzQ3ZTA4ODc4NzY0ZTIwMjBiMzQxN2RjNmM5ZDg3ZjYifQ==
its giving a long string about 250 length.
Is there any way to limit the length of this string in laravel?
My Client needs to add the URL with encrypted value in a mail function.
eg:
www.example.com/varify/eyJpdiI6IndhaFZFNlhIRDlURzdXanJVMEhBM0E9PSIsInZhbHVlIjoidWF3VzRFZDhyRHltUlwveDdyV0VVWnc9PSIsIm1hYyI6IjE5YjA2YWIyN2Q0MTBlYjdhNDJiNDE5ZjY2OGQ2MDA2NzQ3ZTA4ODc4NzY0ZTIwMjBiMzQxN2RjNmM5ZDg3ZjYifQ==
But the mail function only allow some length of URL :(

One solution is to store the hashed values in a table, and then reference the hash by the auto-incrementing ID of the hash value.
| id | hash | timestamp | random_key |
| 1 | some-hash | 125346164 | 21415 |
| 2 | some-other-hash | 123513515 | 25151 |
So now, instead of using:
www.example.com/verify/some-hash
You can use:
www.example.com/verify/1
The id should really be obfuscated, and not used just as an integer - which is where the timestamp and random_key can help.
$id = 1;
$timestamp = 125346164;
$randomKey = 21415;
$key = base64_encode($timestamp . $randomKey . $id);
echo 'http://www.domain.com/verify/' . $key;
// http://www.domain.com/verify/MTI1MzQ2MTY0MjE0MTUx
All that being said, my recommendation would be to try to work around the limitation put in place by the e-mail delivery platform as URL's can support an address length of around 2000 characters. The example you gave only had a length of 32 and falls well within the lengths acceptable by any modern browser.
Edit: Just generate a uuid using a package like this rather than trying to create your own random id. This will produce a string such as d3d29d70-1d25-11e3-8591-034165a3a613.

I think dont need to store nothing in database, that is a hard work,
In my case a use base64_encode in blase and use base64_decode in controller to show the real value to method and continue the process.

I just faced the same problem. I simply added a column 'hash' in my database table. Then I filled it with a md5(encrypt($model->id))
The md5 value is much shorter, and because it also uses Laravel's crypt, it can't be guessed.

Related

PHP/MySQL table update issue while checking condition in column

I have the above table: tblCompInfo, the product_id value is not 100% accurate and I need to fix it. I have total of 543847 total row with 25 different company and 12 different products.
now, The URL is 100% accurate and as you can see from the image I have highlighted with RED which means they are wrong and GREEN which is what it should be updated to.
TASK:
I need to update Product_id by parsing through URL and getting the INTEGER and checking it with product table, if its a product, assign the value else assign 0.
SOLUTION:
I got two solution in my head:
1. EXPORT the entire DATA to EXCEL CVS, change it and UPLOAD it to DATABASE. which means my entire week will be working with EXCEL only.
2. Since I have laravel framework: I can make a function in PHP and get the DATA company wise and UPDATE the table in a foreach loop with condition.
PROBLEM:
So, to make my life easy, I made the PHP function with a simple solution and it works BUT I get MEMORY ALLOCATION PROBLEM.
$companyID = ??;
$tblCompInfos = tblCompInfo::where('company_id', '=', $companyID)->get();
foreach($tblCompInfos as $tblCompInfo)
{
$actual_link = $tblCompInfo->url;
$pathlink = parse_url($actual_link, PHP_URL_PATH);
$product_id_from_url = preg_replace("/[^0-9]/", "" , $pathlink);
$FindIfItsInProductTable = Product::find($product_id_from_url);
$real_product_id = $FindIfItsInProductTable == null ? 0 : $product_id_from_url;
DB::table('tblCompInfo')->where('company_id', '=', $companyID)->where('url', '=', $tblCompInfo->url)->update(array(
'product_id' => $real_product_id,
));
echo $actual_link."-".$real_product_id."=".$tblCompInfo->product_id."<br>";
}
if it was a local server, I would have update my PHP.ini with more memory and do the job.
However, I have a LIVE server and it has to be done in the live server and I have no control or power over PHP.ini.
What to do? How can I do it easily that I will not get a memory issue?
Please help if anyone?
Try this :
UPDATE [table_name] SET product_id = CONVERT(SUBSTR(url, LOCATE('products/', url)+9, LOCATE('/compare',url)-LOCATE('products/', url)+9),UNSIGNED INTEGER)
But this will only works if every url field has suffix as /compare
if you use MariaDB you can use REGEXP_REPLACE to do the changes like
UPDATE your_table
SET url = REGEXP_REPLACE(url,'[0-9]+',Product_id)
WHERE Product_id > 0;
sample
MariaDB [your_schema]> SELECT REGEXP_REPLACE('http://example.com/products/12/compare','[0-9]+','99');
+--------------------------------------------------------------------+
| REGEXP_REPLACE('http://example.com/products/12/compare','[0-9]+','99') |
+--------------------------------------------------------------------+
| http://example.com/products/99/compare |
+--------------------------------------------------------------------+
1 row in set (0.00 sec)
MariaDB [your_schema]>
I have a pretty odd idea but it can work.
Look at that query :
SELECT
'http://example.com/products/12/compare' as url,
'http://example.com/products/' as check1,
'http://example.com/termsets/' as check2,
'http://example.com/products/12/compare' REGEXP 'http://example.com/products/' as regexp_check1, -- check 1
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) as test1,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) REGEXP "^[0-9]+$" as test1_only_num,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),2 ) as test11,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) REGEXP "^[0-9]+$" as test11_only_num,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),3 ) as test111,
SUBSTRING('http://example.com/products/12/compare', LOCATE('http://example.com/products/','http://example.com/products/12/compare')+LENGTH('http://example.com/products/'),1 ) REGEXP "^[0-9]+$" as test111_only_num;
Result :
+----------------------------------------+------------------------------+------------------------------+---------------+-------+----------------+--------+-----------------+---------+------------------+
| url | check1 | check2 | regexp_check1 | test1 | test1_only_num | test11 | test11_only_num | test111 | test111_only_num |
+----------------------------------------+------------------------------+------------------------------+---------------+-------+----------------+--------+-----------------+---------+------------------+
| http://example.com/products/12/compare | http://example.com/products/ | http://example.com/termsets/ | 1 | 1 | 1 | 12 | 1 | 12/ | 0 |
+----------------------------------------+------------------------------+------------------------------+---------------+-------+----------------+--------+-----------------+---------+------------------+
Url, check1 and check2 are just to display the variables I'm using. It's a main ID, the query is not usable that way of course.
Logic with check1
You check with a REGEX if check1 is present in your URL. If yes, regexp_check1 is 1, else it's 0.
ONLY if regexp_check1 is 1, then you SUBSTRING your URL to take the part that is located AFTER the check1 sentence. You take the first character AFTER (test1), then the two characters AFTER (test11), the three characters AFTER (test111) etc.. until the max length your ID_PRODUCT can be (6 or 7 for example).
You REGEX the SUBSTR you isolated to check if they are numeric only (test1 is numeric, test11 is numeric only, test111 is not numeric only.
Then you know that the content of test11 is your ID
Then you do the same thing with check2 if regexp_check1 was 0, and with an eventual check3 (which would contain http://www.comadso.dk/products/ for example), and for every beginning you can have.
Maybe my idea is a shitty one, but hey if it's seem dumb but works, it's not dumb !

Unique, unpredictable, 12 digit, integer id

How would I go about generating this... I want to keep my primary key sequential and have a 12 digit unique pin generated for each new object added to the database.
The reason it cant just be autoincrement is i don't want the sequential numbers to be easily guessable.
It needs to be integer numbers, because I'm going to have verification codes that need to be dialed on a phone pad.
Use a concatenation of a unique incremented number and a randomly generated number.
The unique incremented number ensures that the result is unique, and the randomly generated number makes it hardly guessable.
This is simple and guaranteed to have no collision (1). The result is incremental, partly random, and non-predictable (provided that the random number part is generated with a good PRNG).
(1): You have to either pad id and random with zeros, or to separate them with some non-digit character.
With a MySQL db, this translates to:
CREATE TABLE foo (
id int not null auto_increment,
random int not null,
...
primary key (id)
);
Maybe you can use UUID_SHORT(). Not 12 digits long, but still could be a viable option:
mysql> select uuid_short();
+-------------------+
| uuid_short() |
+-------------------+
| 22048742962102272 |
+-------------------+
So:
INSERT INTO `table` (`id`, `text`) VALUES (UUID_SHORT(), 'hello world!');
Note: If you really want to have exactly 12 digits, then don't even try to substring the result, if would not ensure the uniqueness of the identifier and may cause collisions.
<?php
$allowed_characters = array(1,2,3,4,5,6,7,8,9,0);
for($i = 1;$i <= 12; $i++){
$pass .= $allowed_characters[rand(0, count($allowed_characters) - 1)];
}
echo $pass;
?>
demo: http://sandbox.phpcode.eu/g/c0190/4
Generally, I will prefer to do something a little bit more low tech. I obscure the values in PHP and leave them as auto-incrementing in JS.
$seeds = array( /*series 100 of very large >= 10-digit numbers*/ );
$seedID = rand( count( $seeds ) ); // randomly choose one of those.
// a string combination which represents the ID + some hash.
$id = bcadd( $seeds[ $seedID ], /* id retrieved from database */ );
// make sure we've not accidentally passed the 10^12 point
$id = bcmod( $id, 1000000000000 );
// make sure to pad
$id = str_pad('' . $id, 3, "0", STR_PAD_LEFT);
$outID = substr( $id, 0, 5 ) . $seedID . substr( $id, 6 );
Then, when receiving the ID from the user:
$seedID = substr( $outID, 6, 2 );
$tmpID = substr( $outID, 0, 5 ) . substr( $outID, 8 );
$id = bcsub( $tmpID, $seeds[ $seedID ] );
// we passed the modulus se we need to add this back in.
if( $id < 0 ) $id = bcmod( bcadd( $id, 1000000000000 ), 1000000000000 );
This will basically mean that you're simply obscuring whatever number you want -- you can use auto_increment with impunity!
One method would be to take your primary key value, salt it with a few other random-ish bits of data (username, current time, process ID, fixed string, etc...) and hash it with md5 or sha1. You then take the hash string and convert it into digits via basic string operations. That'll give you a relatively unique numeric code.
of course, with only 12 digits, you're far more likely to end up with a collision than by using the raw string hash - but since you're requiring this to be dialed on a keypad, it's an acceptable tradeoff.
If the pins are invalidated/deleted after usage, then the collision chances will be much reduced.
You want two things
Uniqueness
Incremental
If you want both the things from same sequence you will run out of luck (literally)
Uniqueness is guaranteed by having large sample space + random + check-unique. Which means, the actual number could be anywhere in between the sample space.
But if you want unique + incremental property, you are dividing sample space by 2. In 64 tries you would have reduced a 64 bit int sample space to 1 bit sample space.
Good luck !
All solutions so far lack one thing essential to your application: Security!
You said you will be using these numbers as a (product) verification code - so you really, really want this to be unpredictable, otherwise it will get exploited.
Neither MySQL's built-in RANDOM function nor any of the random functions PHP provides today are secure random functions. They behave pseudo-randomly, alright, but they all are predictable!
Your only chance is to whip up something of your own using /dev/urandom on a *nix machine or leveraging the Crypto API on Windows. OpenSSL does provide secure random numbers based on these mechanisms - you could reuse this either in a C extension for PHP or by reading the output from a command line script called from PHP. See also this answer.
About your requirement for the numbers to be sequential - is this really so important? It does complicate things enormously. Otherwise you would be good to go with a simple secure 6 byte random number encoded to a string using hex encoding (yielding a 12 character string). Although I would recommend making it 10 bytes and 20 characters to be safer.
But if you want to be sequential, which I interpret as monotonously increasing (because a simple +1 would be trivially predictable), this makes things just so much more complicated. And you don't gain anything from this complexity, the only thing that might happen is that you break the security by inventing some obscure scheme that is easily exploitable.
My suggestion: Add another column that acts as a plain old auto-incremented ID and add the code as a random number constructed as above as a separate column. As far as I see, there's no need to require the product activation code to be the ID at the same time.

SQL string match numbers, varying length

I am looking up which exchange services which telephone numbers, from a table of fragmentary numbers that show which exchange services them.
So my table contains, for example:
id |exchcode |exchname |easting|northin|leadin |
-----------------------------------------------------------------
12122 |SNL/UC |SANDAL |43430 |41306 |1924240 |
12123 |SNL/UC |SANDAL |43430 |41306 |1924241 |
881 |SNL/UD |SANDAL |43430 |41306 |1924249 |
2456 |BD/BCC/1 |BRADFORD CABLE |41627 |43262 |192421 |
4313 |NEY/UB |NORMANTON |43847 |42289 |192422 |
12124 |SNL/UC |SANDAL |43430 |41306 |192425 |
9949 |OBE/UB |HORBURY OSSETT |42857 |41971 |192428 |
9987 |OBE/UB |WAKEFIELD |42857 |41971 |1924 |
(sorry, formatting a bit rubbish)
leadin is the leading part of the phone number I have to match (stored as a VARCHAR, not a number)
And I am supplied with a phone number 1924283777 (not real)
how do I query to get the best match from the above table (It should pick exchange id 9949), or do I deal with it in code after I've done the query (php)
tl;dr: variable length for values of leadin column, want best match with a number longer than leadin.
I would think something like
WHERE ? LIKE concat(leadin, '%') order by length(leadin) desc limit 1
(I haven't checked the function names, and I'm not certain that this will work in MYSQL - I'm pretty sure it will work in one of the SQL's I've used).

PHP TZ setting length

I was wonder what is the maximum length of the timezone settings in PHP? I'm storing the string in a database, and would like to keep the length as short as possible, but i see timezones like "Canada/East-Saskatchewan", which goes beyond our current limit.
If I could just get a list of all the supported timezone string, I can sort them, but they are currently split on to several different pages.
linky: http://www.php.net/manual/en/timezones.php
Edit June 2021 Answer is 64. Why? That's the width of the column used in MySQL to store those timezone name strings.
The zoneinfo database behind those time zone strings just added new prefixes. To America/Argentina/ComodRivadavia, formerly the longest timezone string, they added posix/America/Argentina/ComodRivadavia and right/America/Argentina/ComodRivadavia, both with a length of 38. This is up from 32, the previous longest string.
And here is the complete PHP code to find that:
<?php
$timezone_identifiers = DateTimeZone::listIdentifiers();
$maxlen = 0;
foreach($timezone_identifiers as $id)
{
if(strlen($id) > $maxlen)
$maxlen = strlen($id);
}
echo "Max Length: $maxlen";
/*
Output:
Max Length: 32
*/
?>
The Olson database — available from ftp://ftp.iana.org/tz/releases/ or http://www.iana.org/time-zones (but see also http://www.twinsun.com/tz/tz-link.htm* and http://en.wikipedia.org/wiki/Tz_database) — is the source of these names. The documentation in the file Theory includes a description of how the zone names are formed. This would help you establish how long names can be.
The longest 'current' names are 30 characters (America/Argentina/Buenos_Aires,
America/Argentina/Rio_Gallegos, America/North_Dakota/New_Salem); the longest 'backwards compatibility' name is 32 characters (America/Argentina/ComodRivadavia).
* Note that the TwinSun site has not been updated for some time and has some outdated links (such as suggesting that the Olson database is available from ftp://ftp.elsie.nci.nih.gov — it is now available from IANA instead).
From the manual:
<?php
$timezone_identifiers = DateTimeZone::listIdentifiers();
for ($i=0; $i < 5; $i++) {
echo "$timezone_identifiers[$i]\n";
}
?>
If you are using MySQL with PHP, consider that MySQL already stores Olson timezone names in the "mysql" system database, in a table called "time_zone_name", in a column called "name". One option is to choose the length of your column to match the length that MySQL uses. In MySQL 5.7, the timezone name length is 64 characters. To determine the length in use on your MySQL installation, follow the example below:
mysql> use mysql
Database changed
mysql> describe time_zone_name;
+--------------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------------+------+-----+---------+-------+
| Name | char(64) | NO | PRI | NULL | |
| Time_zone_id | int(10) unsigned | NO | | NULL | |
+--------------+------------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

INET_ATON() and INET_NTOA() in PHP?

I want to store IP addresses in my database, but I also need to use them throughout my application. I read about using INET_ATON() and INET_NTOA() in my MySQL queries to get a 32-bit unsigned integer out of an IP address, which is exactly what I want as it will make searching through the database faster than using char(15).
The thing is, I can't find a function that does the same sort of thing in PHP. The only thing I came across is:
http://php.net/manual/en/function.ip2long.php
So I tested it:
$ip = $_SERVER['REMOTE_ADDR'];
echo ip2long($ip);
And it outputs nothing. In the example they gave it seems to work, but then again I'm not exactly sure if ip2long() does the same thing as INET_ATON().
Does someone know a PHP function that will do this? Or even a completely new solution to storing an IP address in a database?
Thanks.
There is an important distinction between ip2long, long2ip and the MySQL functions.
PHP's ip2long and long2ip deal with signed integers.
See http://php.net/manual/en/function.ip2long.php
"Because PHP's integer type is signed, and many IP addresses will result in negative integers on 32-bit architectures, you need to use the '%u' formatter of sprintf() or printf() to get the string representation of the unsigned IP address."
MySQL's INET_ATON() and INET_NTOA() deal with unsigned integers
See http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton
"To store values generated by INET_ATON(), use an INT UNSIGNED column rather than INT, which is signed. If you use a signed column, values corresponding to IP addresses for which the first octet is greater than 127 cannot be stored correctly."
Here are some functions you can use to work between the two.
If you inserted into the MySQL database, an IP using INET_ATON(), you can convert it back in PHP using the following:
long2ip(sprintf("%d", $ip_address));
And you can convert it to save it in the database from PHP using this:
sprintf("%u", ip2long($ip_address));
(Also important, don't type-cast the $ip_address to int as this might cause problems by wrapping the number if it's bigger than MAX_INT. If you must cast it, cast it to a long or float)
The ip2long() and long2ip() functions should work just fine.
Note : you should use those for IPv4 addresses -- make sure that, in your case, $_SERVER['REMOTE_ADDR'] actually contains a valid IPv4 address (and not some IPv6-stuff).
Trying on a google IP address :
var_dump(ip2long('209.85.227.147'));
var_dump(long2ip(3512066963));
I get the following output :
int(3512066963)
string(14) "209.85.227.147"
You should not have to deal with that inside PHP, that's what MySQL native funcions are for. See this example:
create table iptable (
ip int(32) unsigned not null,
comment varchar(32) not null
);
insert into iptable (ip, comment) values (inet_aton('10.0.0.3'), 'This is 10.0.0.3');
select * from iptable;
+-----------+------------------+
| ip | comment |
+-----------+------------------+
| 167772163 | This is 10.0.0.3 |
+-----------+------------------+
select inet_ntoa(ip) as ip, comment from iptable;
+----------+------------------+
| ip | comment |
+----------+------------------+
| 10.0.0.3 | This is 10.0.0.3 |
+----------+------------------+
If you want to deal with both ipv4 and ipv6 in the same field, and you are using Mysql 5.6 or higher, you can use varbinary(16) and the functions inet6_aton and inet6_ntoa. This is a better example of why you should use MySQL functions and not deal with binary data inside PHP:
create table iptable2 (
ip varbinary(16) not null,
comment varchar(32) not null
);
insert into iptable2 (ip, comment) values
(inet6_aton('192.168.1.254'), 'This is router 192.168.1.254'),
(inet6_aton('::1'), 'This is ipv6 localhost ::1'),
(inet6_aton('FE80:0000:0000:0000:0202:B3FF:FE1E:8329'), 'This is some large ipv6 example')
;
select * from iptable2;
+------------------+---------------------------------+
| ip | comment |
+------------------+---------------------------------+
| +¿?¦ | This is router 192.168.1.254 |
| ? | This is ipv6 localhost ::1 |
| ¦Ç ??¦ ¦?â) | This is some large ipv6 example |
+------------------+---------------------------------+
select inet6_ntoa(ip) as ip, comment from iptable2;
+--------------------------+---------------------------------+
| ip | comment |
+--------------------------+---------------------------------+
| 192.168.1.254 | This is router 192.168.1.254 |
| ::1 | This is ipv6 localhost ::1 |
| fe80::202:b3ff:fe1e:8329 | This is some large ipv6 example |
+--------------------------+---------------------------------+
You can see that by doing this, you can actually avoid having to evaluate ipv6 addresses in different formats, since MySQL converts them to binary and back to their simpliest expression.
I know this question has more than 2 years already, but I want to let this information be useful for others that come across.
HTH
Francisco Zarabozo
For IPv4 and IPv6 support use inet_pton() and inet_ntop(), these are availiable since PHP 5.1+ and mimic exactly the equivalent MySQL functions.
Otherwise just use ip2long() and long2ip().
Here PHP alternative functions (simple copy/paste in your program) -
function inet_aton($ip)
{
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return 0;
return sprintf("%u", ip2long($ip));
}
function inet_ntoa($num)
{
$num = trim($num);
if ($num == "0") return "0.0.0.0";
return long2ip(-(4294967295 - ($num - 1)));
}
ip2long is equivalent to inet_aton().
ip2long only works with IPv4. I suspect your system is using IPv6 for loopback. Try to print REMOTE_ADDR.

Categories