Skip to content

Instantly share code, notes, and snippets.

@rordi
Last active June 23, 2021 15:59
Show Gist options
  • Save rordi/ec162a200faba81c8c2999df7455b470 to your computer and use it in GitHub Desktop.
Save rordi/ec162a200faba81c8c2999df7455b470 to your computer and use it in GitHub Desktop.
Configuration hardening for PhpMyAdmin
<?php
/**
* Configuration hardening for PhpMyAdmin
*
* This file is included at the end of the main PhpMyAdmin configuration file. We thus here override
* any potential config value that was previously set.
*
* @author Dietrich Rordorf
* @date 2018-11-05
* @see https://docs.phpmyadmin.net/en/latest/config.html
*/
const LOCALHOST = 'localhost';
// allowed IP addresses in CIDR notation
const ALLOWED_CIDRS = [
'127.0.0.0/8', // local
'192.168.0.0/16', // local
// add additional IPs hereh
];
/**
* function to check if a single IP is in a single CIDR range
*
* @param string $ip
* @param string $cidr
* @return bool
*/
function ipInCidr($ip, $cidr) {
list ($net, $mask) = mb_split("/", $cidr);
$ip_net = ip2long ($net);
$ip_mask = ~((1 << (32 - $mask)) - 1);
$ip_ip = ip2long ($ip);
$ip_ip_net = $ip_ip & $ip_mask;
return ($ip_ip_net == $ip_net);
}
function cidr_match($ip, $range)
{
list ($subnet, $bits) = explode('/', $range);
if ($bits === null) {
$bits = 32;
}
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = (-1 << (32 - $bits)) & ip2long('255.255.255.255');
$subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
return ($ip & $mask) == $subnet;
}
/**
* function to check if a single ip is in the allowed CIDR ranges
*
* @param string $ip
* @return bool
*/
function ipInAllowedCidrs($ip) {
foreach (ALLOWED_CIDRS as $cidr) {
if (cidr_match($ip, $cidr)) {
return true;
}
}
return false;
}
// restrict access to PMA to specified IP addresses
$accessAllowed = false;
$localDevelopment = false;
// local development: allow connections locally
if (isset($_SERVER['VIRTUAL_HOST']) && $_SERVER['VIRTUAL_HOST'] === LOCALHOST || isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] === LOCALHOST) {
$accessAllowed = true;
$localDevelopment = true;
$sessionDuration = 7/*d*/ * 24/*h*/ * 60/*m*/ * 60/*s*/;
ini_set('session.gc_maxlifetime', $sessionDuration);
$cfg['LoginCookieValidity'] = $sessionDuration;
}
// regular web server
if (isset($_SERVER['REMOTE_ADDR']) && ipInAllowedCidrs($_SERVER['REMOTE_ADDR'])) {
$accessAllowed = true;
}
// proxied web server
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && ipInAllowedCidrs($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$accessAllowed = true;
}
if (!$accessAllowed) {
http_response_code(403);
die('Forbidden');
}
// additional hardening of the PMA login process
for ($i = 1; isset($hosts[$i - 1]); $i++) {
// force that user logs in without potentially revealing the username
$cfg['Servers'][$i]['auth_type'] = 'cookie';
if (isset($cfg['Servers'][$i]['user'])) {
unset($cfg['Servers'][$i]['user']);
}
if (isset($cfg['Servers'][$i]['password'])) {
unset($cfg['Servers'][$i]['password']);
}
// disable root login unless developping locally
$cfg['Servers'][$i]['AllowRoot'] = $localDevelopment;
// disable login without password
$cfg['Servers'][$i]['AllowNoPassword'] = false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment