I'm trying to create a randomized string in PHP, and I get absolutely no output with this:
<?php
function RandomString()
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < 10; $i++) {
$randstring = $characters[rand(0, strlen($characters))];
}
return $randstring;
}
RandomString();
echo $randstring;
What am I doing wrong?
To answer this question specifically, two problems:
$randstring is not in scope when you echo it.
The characters are not getting concatenated together in the loop.
Here's a code snippet with the corrections:
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[random_int(0, $charactersLength - 1)];
}
return $randomString;
}
Output the random string with the call below:
// Echo the random string.
// Optionally, you can give it a desired string length.
echo generateRandomString();
Please note that previous version of this answer used rand() instead of random_int() and therefore generated predictable random strings. So it was changed to be more secure, following advice from this answer.
Note: str_shuffle() internally uses rand(), which is unsuitable for cryptography purposes (e.g. generating random passwords). You want a secure random number generator instead. It also doesn't allow characters to repeat.
One more way.
UPDATED (now this generates any length of string):
function generateRandomString($length = 10) {
return substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length/strlen($x)) )),1,$length);
}
echo generateRandomString(); // OR: generateRandomString(24)
That's it. :)
There are a lot of answers to this question, but none of them leverage a Cryptographically Secure Pseudo-Random Number Generator (CSPRNG).
The simple, secure, and correct answer is to use RandomLib and don't reinvent the wheel.
For those of you who insist on inventing your own solution, PHP 7.0.0 will provide random_int() for this purpose; if you're still on PHP 5.x, we wrote a PHP 5 polyfill for random_int() so you can use the new API even before you upgrade to PHP 7.
Safely generating random integers in PHP isn't a trivial task. You should always check with your resident StackExchange cryptography experts before you deploy a home-grown algorithm in production.
With a secure integer generator in place, generating a random string with a CSPRNG is a walk in the park.
Creating a Secure, Random String
/**
* Generate a random string, using a cryptographically secure
* pseudorandom number generator (random_int)
*
* This function uses type hints now (PHP 7+ only), but it was originally
* written for PHP 5 as well.
*
* For PHP 7, random_int is a PHP core function
* For PHP 5.x, depends on https://github.com/paragonie/random_compat
*
* #param int $length How many characters do we want?
* #param string $keyspace A string of all possible characters
* to select from
* #return string
*/
function random_str(
int $length = 64,
string $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
): string {
if ($length < 1) {
throw new \RangeException("Length must be a positive integer");
}
$pieces = [];
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces []= $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
Usage:
$a = random_str(32);
$b = random_str(8, 'abcdefghijklmnopqrstuvwxyz');
$c = random_str();
Demo: https://3v4l.org/IMJGF (Ignore the PHP 5 failures; it needs random_compat)
This creates a 20 character long hexadecimal string:
$string = bin2hex(openssl_random_pseudo_bytes(10)); // 20 chars
In PHP 7 (random_bytes()):
$string = base64_encode(random_bytes(10)); // ~14 characters, includes /=+
// or
$string = substr(str_replace(['+', '/', '='], '', base64_encode(random_bytes(32))), 0, 32); // 32 characters, without /=+
// or
$string = bin2hex(random_bytes(10)); // 20 characters, only 0-9a-f
#tasmaniski: your answer worked for me. I had the same problem, and I would suggest it for those who are ever looking for the same answer. Here it is from #tasmaniski:
<?php
$random = substr(md5(mt_rand()), 0, 7);
echo $random;
?>
Here is a youtube video showing us how to create a random number
Depending on your application (I wanted to generate passwords), you could use
$string = base64_encode(openssl_random_pseudo_bytes(30));
Being base64, they may contain = or - as well as the requested characters. You could generate a longer string, then filter and trim it to remove those.
openssl_random_pseudo_bytes seems to be the recommended way way to generate a proper random number in php. Why rand doesn't use /dev/random I don't know.
PHP 7+ Generate cryptographically secure random bytes using random_bytes function.
$bytes = random_bytes(16);
echo bin2hex($bytes);
Possible output
da821217e61e33ed4b2dd96f8439056c
PHP 5.3+ Generate pseudo-random bytes using openssl_random_pseudo_bytes function.
$bytes = openssl_random_pseudo_bytes(16);
echo bin2hex($bytes);
Possible output
e2d1254506fbb6cd842cd640333214ad
The best use case could be
function getRandomBytes($length = 16)
{
if (function_exists('random_bytes')) {
$bytes = random_bytes($length / 2);
} else {
$bytes = openssl_random_pseudo_bytes($length / 2);
}
return bin2hex($bytes);
}
echo getRandomBytes();
Possible output
ba8cc342bdf91143
Here is a simple one-liner that generates a true random string without any script level looping or use of OpenSSL libraries.
echo substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', mt_rand(1,10))), 1, 10);
To break it down so the parameters are clear
// Character List to Pick from
$chrList = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
// Minimum/Maximum times to repeat character List to seed from
$chrRepeatMin = 1; // Minimum times to repeat the seed string
$chrRepeatMax = 10; // Maximum times to repeat the seed string
// Length of Random String returned
$chrRandomLength = 10;
// The ONE LINE random command with the above variables.
echo substr(str_shuffle(str_repeat($chrList, mt_rand($chrRepeatMin,$chrRepeatMax))), 1, $chrRandomLength);
This method works by randomly repeating the character list, then shuffles the combined string, and returns the number of characters specified.
You can further randomize this, by randomizing the length of the returned string, replacing $chrRandomLength with mt_rand(8, 15) (for a random string between 8 and 15 characters).
A better way to implement this function is:
function RandomString($length) {
$keys = array_merge(range(0,9), range('a', 'z'));
$key = "";
for($i=0; $i < $length; $i++) {
$key .= $keys[mt_rand(0, count($keys) - 1)];
}
return $key;
}
echo RandomString(20);
mt_rand is more random according to this and this in PHP 7. The rand function is an alias of mt_rand.
function generateRandomString($length = 15)
{
return substr(sha1(rand()), 0, $length);
}
Tada!
$randstring in the function scope is not the same as the scope where you call it. You have to assign the return value to a variable.
$randstring = RandomString();
echo $randstring;
Or just directly echo the return value:
echo RandomString();
Also, in your function you have a little mistake. Within the for loop, you need to use .= so each character gets appended to the string. By using = you are overwriting it with each new character instead of appending.
$randstring .= $characters[rand(0, strlen($characters))];
First, you define the alphabet you want to use:
$alphanum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$special = '~!##$%^&*(){}[],./?';
$alphabet = $alphanum . $special;
Then, use openssl_random_pseudo_bytes() to generate proper random data:
$len = 12; // length of password
$random = openssl_random_pseudo_bytes($len);
Finally, you use this random data to create the password. Because each character in $random can be chr(0) until chr(255), the code uses the remainder after division of its ordinal value with $alphabet_length to make sure only characters from the alphabet are picked (note that doing so biases the randomness):
$alphabet_length = strlen($alphabet);
$password = '';
for ($i = 0; $i < $len; ++$i) {
$password .= $alphabet[ord($random[$i]) % $alphabet_length];
}
Alternatively, and generally better, is to use RandomLib and SecurityLib:
use SecurityLib\Strength;
$factory = new RandomLib\Factory;
$generator = $factory->getGenerator(new Strength(Strength::MEDIUM));
$password = $generator->generateString(12, $alphabet);
I've tested performance of most popular functions there, the time which is needed to generate 1'000'000 strings of 32 symbols on my box is:
2.5 $s = substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length/strlen($x)) )),1,32);
1.9 $s = base64_encode(openssl_random_pseudo_bytes(24));
1.68 $s = bin2hex(openssl_random_pseudo_bytes(16));
0.63 $s = base64_encode(random_bytes(24));
0.62 $s = bin2hex(random_bytes(16));
0.37 $s = substr(md5(rand()), 0, 32);
0.37 $s = substr(md5(mt_rand()), 0, 32);
Please note it is not important how long it really was but which is slower and which one is faster so you can select according to your requirements including cryptography-readiness etc.
substr() around MD5 was added for sake of accuracy if you need string which is shorter than 32 symbols.
For sake of answer: the string was not concatenated but overwritten and result of the function was not stored.
Here's my simple one line solution to generate a use friendly random password, excluding the characters that lookalike such as "1" and "l", "O" and "0", etc... here it is 5 characters but you can easily change it of course:
$user_password = substr(str_shuffle('abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789'),0,5);
One very quick way is to do something like:
substr(md5(rand()),0,10);
This will generate a random string with the length of 10 chars. Of course, some might say it's a bit more heavy on the computation side, but nowadays processors are optimized to run md5 or sha256 algorithm very quickly. And of course, if the rand() function returns the same value, the result will be the same, having a 1 / 32767 chance of being the same. If security's the issue, then just change rand() to mt_rand()
function gen_uid($l=5){
return substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 10, $l);
}
echo gen_uid();
Default Value[5]: WvPJz
echo gen_uid(30);
Value[30]: cAiGgtf1lDpFWoVwjykNKXxv6SC4Q2
Short Methods..
Here are some shortest method to generate the random string
<?php
echo $my_rand_strng = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), -15);
echo substr(md5(rand()), 0, 7);
echo str_shuffle(MD5(microtime()));
?>
Helper function from Laravel 5 framework
/**
* Generate a "random" alpha-numeric string.
*
* Should not be considered sufficient for cryptography, etc.
*
* #param int $length
* #return string
*/
function str_random($length = 16)
{
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($pool, $length)), 0, $length);
}
Since php7, there is the random_bytes functions.
https://www.php.net/manual/ru/function.random-bytes.php
So you can generate a random string like that
<?php
$bytes = random_bytes(5);
var_dump(bin2hex($bytes));
?>
from the yii2 framework
/**
* Generates a random string of specified length.
* The string generated matches [A-Za-z0-9_-]+ and is transparent to URL-encoding.
*
* #param int $length the length of the key in characters
* #return string the generated random key
*/
function generateRandomString($length = 10) {
$bytes = random_bytes($length);
return substr(strtr(base64_encode($bytes), '+/', '-_'), 0, $length);
}
function rndStr($len = 64) {
$randomData = file_get_contents('/dev/urandom', false, null, 0, $len) . uniqid(mt_rand(), true);
$str = substr(str_replace(array('/','=','+'),'', base64_encode($randomData)),0,$len);
return $str;
}
This one was taken from adminer sources:
/** Get a random string
* #return string 32 hexadecimal characters
*/
function rand_string() {
return md5(uniqid(mt_rand(), true));
}
Adminer, database management tool written in PHP.
/**
* #param int $length
* #param string $abc
* #return string
*/
function generateRandomString($length = 10, $abc = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
{
return substr(str_shuffle($abc), 0, $length);
}
Source from http://www.xeweb.net/2011/02/11/generate-a-random-string-a-z-0-9-in-php/
Another one-liner, which generates a random string of 10 characters with letters and numbers. It will create an array with range (adjust the second parameter to set the size), loops over this array and assigns a random ASCII character (range 0-9 or a-z), then implodes the array to get a string.
$str = implode('', array_map(function () { return chr(rand(0, 1) ? rand(48, 57) : rand(97, 122)); }, range(0, 9)));
Note: this only works in PHP 5.3 and later
One liner.
It is fast for huge strings with some uniqueness.
function random_string($length){
return substr(str_repeat(md5(rand()), ceil($length/32)), 0, $length);
}
function randomString($length = 5) {
return substr(str_shuffle(implode(array_merge(range('A','Z'), range('a','z'), range(0,9)))), 0, $length);
}
Here is how I am doing it to get a true unique random key:
$Length = 10;
$RandomString = substr(str_shuffle(md5(time())), 0, $Length);
echo $RandomString;
You can use time() since it is a Unix timestamp and is always unique compared to other random mentioned above. You can then generate the md5sum of that and take the desired length you need from the generated MD5 string. In this case I am using 10 characters, and I could use a longer string if I would want to make it more unique.
I hope this helps.
The edited version of the function works fine, but there is just one issue I found: You used the wrong character to enclose $characters, so the ’ character is sometimes part of the random string that is generated.
To fix this, change:
$characters = ’0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’;
to:
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
This way only the enclosed characters are used, and the ’ character will never be a part of the random string that is generated.
function generateRandomString($length = 10, $hasNumber = true, $hasLowercase = true, $hasUppercase = true): string
{
$string = '';
if ($hasNumber)
$string .= '0123456789';
if ($hasLowercase)
$string .= 'abcdefghijklmnopqrstuvwxyz';
if ($hasUppercase)
$string .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return substr(str_shuffle(str_repeat($x = $string, ceil($length / strlen($x)))), 1, $length);
}
and use:
echo generateRandomString(32);
I liked the last comment which used openssl_random_pseudo_bytes, but it wasn't a solution for me as I still had to remove the characters I didn't want, and I wasn't able to get a set length string. Here is my solution...
function rndStr($len = 20) {
$rnd='';
for($i=0;$i<$len;$i++) {
do {
$byte = openssl_random_pseudo_bytes(1);
$asc = chr(base_convert(substr(bin2hex($byte),0,2),16,10));
} while(!ctype_alnum($asc));
$rnd .= $asc;
}
return $rnd;
}
Given a N-bit hash (e.g. output of md5()), I have 2 situations I need solutions for:
Based on the hash, return an integer value in a given range.
Based on the hash, return an array value from a given array.
Same hash, should always return same number or array key within that range or from that same input array. If the input array changes but hash remains the same, then i would get a different selection.
So for example i would have code like this:
echo intFromHash(1, 100, 'abcd'); // 15
echo intFromHash(1, 100, 'defg'); // 90
echo arrayValueFromHash(['moe', 'joe', 'pike'], 'abcd'); // 'joe'
echo arrayValueFromHash(['pike', 'dolly']); // pike
You can write intFromHash() in 1 line of code using the crc32() PHP function:
function intFromHash($min, $max, $hash {
return $min + crc32($hash) % ($max - $min + 1);
}
Use abs(crc32($hash)) if you are running it on a 32-bit system (read the documentation for details).
Then you can use it to implement arrayValueFromHash() (in another line of code):
function arrayValueFromHash(array $array, $hash) {
return $array[intFromHash(0, count($array) - 1, $hash)];
}
Use return $array[array_keys($array)[intFromHash(...)]]; if $array is an associative array (the expression presented in the code works only for numerically indexed arrays, as those listed in the question.)
Figured it out I think. Here's the code for whoever needs it:
/**
* Return a key from an array based on a given 4-bit hash.
*
* #param array $array Array to return a key from.
* #param string $hash 4-bit hash. If hash is longer than 4-bit only first 4 bits will be used.
* #return mixed
*/
function getArrayValueByHash($array, $hash)
{
$arrayKeys = array_keys($array);
$index = getIntFromHash(0, sizeof($arrayKeys)-1, $hash);
return $array[$arrayKeys[$index]];
}
/**
* Return an integer in range, based on a hash.
*
* #param int $start
* #param int $end
* #param string $hash 4-bit hash. If hash is longer than 4-bit only first 4 bits will be used.
* #return int
*/
function getIntFromHash($start, $end, $hash)
{
$size = $end-$start;
$hash = str_split($hash);
$intHash = ord($hash[0]) * 16777216 + ord($hash[1]) * 65536 + ord($hash[2]) * 256 + ord($hash[3]);
$fits = $intHash / $size;
$decimals = $fits - floor($fits);
$index = floor($decimals * $size);
return $start+$index;
}
I have a list of IP ranges like
$whitelist=array('50*','202.16*','123.168*',');
I want to block all other traffic from seeing the page.
I've tried
if(in_array($_SERVER['REMOTE_ADDR'],$whitelist)){
//display page
}
in_array doesn't use regexs to compare. Also your regex is incorrect the * is a quantifier. That allows zero or more of the previous character.
Try:
$whitelist=array('50\..*','202\.16\..*','123\.168\..*');
if(preg_match('/^(' . implode('|', $whitelist) . ')/', $_SERVER['REMOTE_ADDR'])){
The .* is allowing anything (pretty much(see s modifier http://php.net/manual/en/reference.pcre.pattern.modifiers.php), . is any character and then paired with that quantifier previously mentioned). The ^ is the start of the string. \. is a literal .. The | is an or.
Demo: https://eval.in/571019
Regex Demo: https://regex101.com/r/dC5uI0/1
This should work for you:
you can loop through white list ip and trim space and * (if found from right).
There after using substr you can cut the IP address of the same length of whitelist ip in loop, and compare both.
$whitelists = array('50*','202.16*','123.168*');
foreach($whitelists as $whitelist){
$whitelist = rtrim($whitelist, "*\r\n\t\0 ");
if(substr($_SERVER['REMOTE_ADDR'], 0, strlen($whitelist)) == $whitelist) {
$match = true;
break;
}
}
echo $match ? 'Match' : 'Not Match';
as stated by #chris85, in_array doesn't use regexs.
To do such a thing, you could simply use a loop like this:
if(preg_match('/^(' . implode('|', $whitelist) . ')/i', $_SERVER['REMOTE_ADDR'])){
// Do your stuff
}
Your '*' doesn't work as you thought.. That's ok:
$whitelist=array('50\.*','202.16\.*','123.168\.*');
Hope this helps someone. Seeing as how you didn't specifically ask about regular expressions, and since the topic is about matching IP addresses, I thought I'd put this out there so that it may help someone with a similar issue.
Server software usually strives to be as quick and efficient as possible; matching IP addresses is usually done arithmetically. That being said, I'll go over a fast way to do exactly what you're trying to do before going over a possible alternative.
If you're simply performing a wild card match on IP address strings, I would suggest that you use this method. It has been tailored to your use case, but I will include the simple matching function by itself.
For contrast, I've also included the sample output with execution times using this method compared to using PHP's RegEx functions (which are the way to go for more complex pattern matching)
NOTES:
I use the functions referenced herein for very specific purposes. They are applicable to this scenario because IP addresses don't have '*' characters in them. As they are written, if the variable you're testing has a '*' character in it, it will only match against a wild card character so there is a slight possibility for loss of information there.
If you're writing a command-line daemon or your process will use the same list of IPs to check against multiple times during its lifetime, it is beneficial to use the RegEx library. The small speed benefit to using my method here is only won when having to initially load and prepare the IP list RegEx for first use.
You can move the code from "wildcard_match()" to inside of "match_ip() for further benefit, avoiding the overhead of another function call.
Code (you can copy and paste):
<?php
/**
* This function compares a string ("$test") to see if it is
* equal to another string ("$wild"). An '*' in "$wild" will
* match any characters in "$test".
*
* #param string $wild - The string to compare against. This may
* be either an exact character string to match, or a string
* with a wild card ('*') character that will match any character(s)
* found in "$test".
*
* #param string $test - A character string we're comparing against
* "$wild" to determine if there is a match.
*
* #return bool Returns TRUE if "$test" is either an exact match to
* "$wild", or it fits the bill taking any wild card characters into
* consideration.
*
**/
function wildcard_match( $pattern, $test ) {
$p = 0;
$a_name = explode("*", $pattern);
$segs = count($a_name);
$max_seg = ($segs-1);
$plen = 0;
$test_len = strlen($test);
for ($i = 0; $i < $segs; $i++) {
$part = $a_name[$i];
$plen = strlen($part);
if ($plen === 0) {
if ($i === $max_seg) return true;
continue;
}
$p = strpos($test, $part, $p);
if ($p === false) {
return false;
}
$p+=$plen;
}
if ($p===$test_len) {
return true;
}
return false;
}
/**
* Function to quickly traverse an array of whole, or
* wild card IPv4 addresses given in "$whitelist" and
* determine if they match the given whole IPv4
* address in "$test".
*
* #param array $whitelist - An array of IPv4 addresses, either
* whole, or containing an '*' character wherein any character(s)
* in "$test" will match.
*
* #param string $test - A complete string (dot-decimal) IPv4
* address to compare against the contents of the array given in
* parameter one ("$whitelist").
*
* #return bool Returns TRUE, if the IPv4 address given in "$test" was
* matched to an IPv4 address or IPv4 wild card pattern in the array
* given in parameter one ("$whitelist").
*
**/
function match_ip( $whitelist, $test ) {
foreach ($whitelist as $w) {
if (wildcard_match($w, $test)) return true;
}
return false;
}
/* The array of IP addresses we're going to validate */
$check_array = array("50.245.1.9", "35.125.25.255", "202.16.15.25");
/* The array as given in your example (minus the extra ' at the end) */
$whitelist1=array('50*','202.16*','123.168*');
/* An array for RegEx matching */
$whitelist2=array('50\..*','202\.16\..*','123\.168\..*');
microtime(true); /* Execute this once to make sure its module is loaded */
echo "Giving PHP a second to get its ducks in a row...\n";
usleep(1000000); /** Give PHP a second to load and prepare */
$st = microtime(true);
foreach ($check_array as $c) {
if (match_ip($whitelist1, $c)) {
echo "$c....Match!\n";
} else {
echo "$c....No match!\n";
}
}
$dt = microtime(true)-$st;
echo "Time: $dt\n\n\n\n";
$st = microtime(true);
foreach ($check_array as $c) {
if(preg_match('/^(' . implode('|', $whitelist2) . ')/', $c)){
echo "$c....Match!\n";
} else {
echo "$c....No Match!\n";
}
}
$dt = microtime(true)-$st;
echo "Time: $dt\n\n";
The output from this is:
Giving PHP a second to get its ducks in a row...
50.245.1.9....Match!
35.125.25.255....No match!
202.16.15.25....Match!
Time 1: 0.00027704238891602
50.245.1.9....Match!
35.125.25.255....No Match!
202.16.15.25....Match!
Time 2: 0.00040698051452637
The first result set is from the function "match_ip()", the second result set is from firing up the RegEx library.
Now, a possible better solution to wild card matching IPs would be to employ an array of IP addresses in CIDR notation in your array. Often times, you want IP traffic to be allowed from a specific network or range of IP addresses.
There are quite a few assumptions here, but for example (using your "$whitelist" array):
'50*' might be construed as "I want all IP addressess from 50.xxx.xxx.xxx to be allowed access.
In that case, you'd specify the format "50.0.0.0/8". (The "0"s after the "50." can be any number. They will be completely ignored because of the "/8".)
xxx.xxx.xxx.xxx
| | | |
8 16 24 32
An IPv4 address is computationally 32 bits, so above, you're saying that all you care about is the first 8 bits matching.
'123.168*' would be "123.168.0.0/16"
"101.23.54.0/24" would allow all IP addresses starting with "101.23.54" access.
"44.32.240.10/32" would only allow the IP address "44.32.240.10" access. No range.
So you could do something like this:
<?php
/**
* Determines if the two given IPv4 addresses
* are equal, or are on the same network using
* the given number of "$mask" bits.
*
* #param string $ip1 - The first string dot-decimal IPv4
* address.
*
* #param string $ip1 - The second string dot-decimal IPv4
* address.
*
* #param int $mask - The number of bits in the mask.
*
* #return bool Returns TRUE if they match after being
* masked, or FALSE if not.
*
*/
function ip_match( $ip1, $ip2, $mask) {
$mask = (int)$mask;
$ip1 = ip2long($ip1);
$ip2 = ip2long($ip2);
if ($ip1 === false || $ip2 === false) return false;
if ($mask < 1 || $mask > 32) return false;
$mask = (0x00000000FFFFFFFF & 0x00000000FFFFFFFF << (32-$mask));
if ( ($ip1 & $mask) === ($ip2 & $mask) ) {
return true;
}
return false;
}
/**
* Takes an array of string (CIDR) network representations and
* sorts them into an array used later for checking against IP
* addresses.
*
* #param array $cidr_array - An array of IP addressess in
* CIDR notation e.g. 192.168.1.1/24
*
* #return array Returns an array of objects with the following
* properties:
* 'ip' - The string (dot-decimal) IP address that
* has been numerically verified for use
* in comparisons later.
*
* 'mask' - The number of bits used for creating
* the subnet mask against which IP
* addresses will be compared.
*
**/
function make_whitelist( $cidr_array ) {
$wl = array();
$lip = 0;
$bm = 0;
$spos = 0;
if (!is_array($cidr_array)) return false;
foreach ($cidr_array as $ip) {
$spos = strpos($ip, "/");
if ($spos === false) {
$bm = 32; /* If there's no "/", assume
* that we want an EXACT IP
* address. Hence the 32 bit
* mask
**/
} else {
$bm = (int)substr($ip, ($spos+1));
$ip = substr($ip, 0, $spos++);
}
$lip = ip2long($ip); /* Using this here to check IP validity
* before storing it in the array...
* We use ip2long() later for comparisons.
*
* You can store it this way - as a long -
* instead of as a string (I do) to
* use less memory if you wish.
*
**/
if ($bm === 0) continue; /* A bit mask of ZERO will block
* ALL IP addresses, skip it
* for the example.
**/
if ($lip === false) continue; /* If it's an invalid IP, skip it,
* you could optionally try to
* resolve it as a hostname using
* gethostbyname() or gethostbynamel()
* here...
**/
array_push($wl, (object)array('ip'=>$ip, 'mask'=>$bm));
}
return $wl;
}
$whitelist = make_whitelist(array("50.0.0.0/8", "202.16.0.0/16", "123.168.0.0/16", "1.1.1.1"));
$ips_to_check = array("50.1.174.41", "42.123.100.23", "123.168.4.79", "1.1.1.2", "1.1.1.1");
foreach ($ips_to_check as $ip) {
foreach ($whitelist as $w) {
if (ip_match($ip, $w->ip, $w->mask)) {
echo "\"$ip\" is allowed!\n";
continue 2;
}
}
echo "\"$ip\" is NOT allowed!\n";
}
I know this is a lot, but there is plenty here for people to think about, find while searching, and hopefully be able to use to make their lives easier!