Skip to content

Instantly share code, notes, and snippets.

@typhonius
Last active May 9, 2017 18:18

Revisions

  1. typhonius revised this gist Mar 12, 2014. 1 changed file with 20 additions and 27 deletions.
    47 changes: 20 additions & 27 deletions acquia.inc
    Original file line number Diff line number Diff line change
    @@ -25,35 +25,27 @@ function ac_protect_this_site() {
    }
    }

    // Check to see whether the page requested is specifically restricted
    // to users on the whitelist.
    if (!empty($conf['ah_restricted_paths'])) {
    ah_paths_restrict($on_vpn);
    }

    // Should we skip the auth check? Default to FALSE.
    $skip_auth_check = FALSE;

    // Check if we should skip auth check for this page.
    if (ah_path_skip_auth()) {
    $skip_auth_check = TRUE;
    }

    // Check if we should disable cache for this page.
    if (ah_path_no_cache()) {
    $conf['page_cache_maximum_age'] = 0;
    }

    // Check if we should skip auth check for this page.
    if (ah_path_skip_auth()) {
    $skip_auth_check = TRUE;
    // Is the page restricted to whitelist only? Default to FALSE.
    $restricted_page = FALSE;

    // Check to see whether this page is restricted.
    if (!empty($conf['ah_restricted_paths']) && ah_paths_restrict()) {
    $restricted_page = TRUE;
    }

    // Determine if we should password protect the site.
    //
    // Current business rules:
    // 1) Dev, stage, and prod are only accessible from whitelisted IPs.
    // 2) Users on whitelisted IPs must pass basic auth challenge to access.
    // 3) Users on blacklist and not on whitelist are denied access.
    // 4) Specific paths are restricted to IPs on the whitelist.
    //
    // Always start by assuming we're protecting the site if the relevant variables
    // are set. Some rule below has to pass before we turn it off.
    $protect_ip = !empty($conf['ah_whitelist']);
    $protect_password = !empty($conf['ah_basic_auth_credentials']);

    @@ -67,8 +59,8 @@ function ac_protect_this_site() {
    // $skip_auth_check = TRUE;
    // $on_vpn = TRUE;

    // If not on whitelisted IP prevent all access.
    if ($protect_ip && !$on_vpn) {
    // If not on whitelisted IP prevent access to protected pages.
    if ($protect_ip && !$on_vpn && $restricted_page) {
    ah_page_403();
    }

    @@ -158,13 +150,13 @@ function ah_path_skip_auth() {
    }

    /**
    * Forbid access to user or admin paths if the user is not on an approved IP range.
    * Check whether a path has been restricted.
    *
    */
    function ah_paths_restrict($on_vpn) {
    function ah_paths_restrict() {
    global $conf;

    if (isset($_GET['q']) && !$on_vpn) {
    if (isset($_GET['q'])) {

    // Borrow some code from drupal_match_path()
    foreach ($conf['ah_restricted_paths'] as &$path) {
    @@ -174,11 +166,12 @@ function ah_paths_restrict($on_vpn) {
    $paths = preg_replace('/\\\\\*/', '.*', $conf['ah_restricted_paths']);
    $paths = '/^(' . join('|', $paths) . ')$/';

    // Deny access if the user is on a restricted path.
    // If this is a restricted path, return TRUE.
    if (preg_match($paths, $_GET['q'])) {
    ah_page_403();
    return TRUE;
    }
    }
    return FALSE;
    }

    /**
    @@ -227,4 +220,4 @@ function ah_ip_in_list($ip, $list) {
    }
    }
    return FALSE;
    }
    }
  2. typhonius revised this gist Mar 1, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion acquia.inc
    Original file line number Diff line number Diff line change
    @@ -82,8 +82,8 @@ function ac_protect_this_site() {
    * Output a 403 (forbidden access) response.
    */
    function ah_page_403() {
    print "403 Forbidden: Access denied (". $_SERVER['AH_Client_IP'] .")";
    header('HTTP/1.0 403 Forbidden');
    print "403 Forbidden: Access denied (". $_SERVER['AH_Client_IP'] .")";
    exit;
    }

  3. typhonius revised this gist Dec 23, 2013. 1 changed file with 69 additions and 42 deletions.
    111 changes: 69 additions & 42 deletions acquia.inc
    Original file line number Diff line number Diff line change
    @@ -13,8 +13,22 @@ function ac_protect_this_site() {

    // Is the user on the VPN? Default to FALSE.
    $on_vpn = $cli ? TRUE : FALSE;

    if (!empty($_SERVER['AH_Client_IP']) && !empty($conf['ah_whitelist'])) {
    $on_vpn = ah_ip_in_whitelist($_SERVER['AH_Client_IP'], $conf['ah_whitelist']);
    $on_vpn = ah_ip_in_list($_SERVER['AH_Client_IP'], $conf['ah_whitelist']);
    }

    // If the IP is not explicitly whitelisted check to see if the IP is blacklisted.
    if (!$on_vpn && !empty($_SERVER['AH_Client_IP']) && !empty($conf['ah_blacklist'])) {
    if (ah_ip_in_list($_SERVER['AH_Client_IP'], $conf['ah_blacklist'])) {
    ah_page_403();
    }
    }

    // Check to see whether the page requested is specifically restricted
    // to users on the whitelist.
    if (!empty($conf['ah_restricted_paths'])) {
    ah_paths_restrict($on_vpn);
    }

    // Should we skip the auth check? Default to FALSE.
    @@ -35,6 +49,8 @@ function ac_protect_this_site() {
    // Current business rules:
    // 1) Dev, stage, and prod are only accessible from whitelisted IPs.
    // 2) Users on whitelisted IPs must pass basic auth challenge to access.
    // 3) Users on blacklist and not on whitelist are denied access.
    // 4) Specific paths are restricted to IPs on the whitelist.
    //
    // Always start by assuming we're protecting the site if the relevant variables
    // are set. Some rule below has to pass before we turn it off.
    @@ -144,60 +160,71 @@ function ah_path_skip_auth() {
    /**
    * Forbid access to user or admin paths if the user is not on an approved IP range.
    *
    * @todo Use a $conf variable for this.
    */
    function ah_paths_restrict($on_vpn) {
    if (isset($_GET['q'])) {
    $restricted_path = ($_GET['q'] == 'user' || $_GET['q'] == 'admin' ||
    strpos($_GET['q'], 'user/') === 0 ||
    strpos($_GET['q'], 'admin/') === 0);
    // @TODO - remove debug
    // header( 'X-AH-debug-restricted-path: ' . ($restricted_path ? "restricted" : "not restricted") );
    if ($restricted_path && !$on_vpn) {
    ah_page_forbidden();
    global $conf;

    if (isset($_GET['q']) && !$on_vpn) {

    // Borrow some code from drupal_match_path()
    foreach ($conf['ah_restricted_paths'] as &$path) {
    $path = preg_quote($path, '/');
    }

    $paths = preg_replace('/\\\\\*/', '.*', $conf['ah_restricted_paths']);
    $paths = '/^(' . join('|', $paths) . ')$/';

    // Deny access if the user is on a restricted path.
    if (preg_match($paths, $_GET['q'])) {
    ah_page_403();
    }
    }
    }

    /**
    * Determine if the IP is within the ranges defined in the whitelist.
    * Determine if the IP is within the ranges defined in the white/black list.
    */
    function ah_ip_in_whitelist($ip, $whitelist) {
    foreach ($whitelist as $cidr) {
    $range = ah_ip_cidr_range($cidr);
    if ($range && count($range) == 2) {
    $ip_long = ip2long($ip);
    $ip_one_long = $range[0];
    $ip_two_long = $range[1];
    $lte = $ip_one_long <= $ip_long;
    $gte = $ip_two_long >= $ip_long;
    if ($lte && $gte) {
    function ah_ip_in_list($ip, $list) {
    foreach ($list as $item) {

    // Match IPs in CIDR format.
    if (strpos($item, '/') !== false) {
    list($range, $mask) = explode('/', $item);

    // Take the binary form of the IP and range.
    $ip_dec = ip2long($ip);
    $range_dec = ip2long($range);

    // Create the binary form of netmask.
    $mask_dec = ~ (pow(2, (32 - $mask)) - 1);

    // Run a bitwise AND to determine whether the IP and range exist
    // within the same netmask.
    if (($mask_dec & $ip_dec) == ($mask_dec & $range_dec)) {
    return TRUE;
    }
    }
    }
    }

    /**
    * From http://www.php.net/manual/en/function.ip2long.php#109298
    */
    function ah_ip_cidr_range($cidr) {
    list($ip, $mask) = explode('/', $cidr);
    // Match against wildcard IPs or IP ranges.
    elseif (strpos($item, '*') !== false || strpos($item, '-') !== false) {

    // net mask binary string
    $maskBinStr = str_repeat("1", $mask ) . str_repeat("0", 32-$mask );
    // inverse mask
    $inverseMaskBinStr = str_repeat("0", $mask ) . str_repeat("1", 32-$mask );
    // Construct a range from wildcard IPs
    if (strpos($item, '*') !== false) {
    $item = str_replace('*', 0, $item) . '-' . str_replace('*', 255, $item);
    }

    $ipLong = ip2long($ip);
    $ipMaskLong = bindec($maskBinStr);
    $inverseIpMaskLong = bindec($inverseMaskBinStr);
    $netWork = $ipLong & $ipMaskLong;
    // Match against ranges by converting to long IPs.
    list($start, $end) = explode('-', $item);

    // ignore network ID (eg: 192.168.1.0)
    $start = $netWork + 1;
    // ignore brocast IP(eg: 192.168.1.255)
    $end = ($netWork | $inverseIpMaskLong) - 1;
    if (ip2long($start) <= ip2long($ip) && ip2long($ip) <= ip2long($end)) {
    return TRUE;
    }
    }

    return array($start, $end);
    }
    // Match against single IPs
    elseif ($ip === $item) {
    return TRUE;
    }
    }
    return FALSE;
    }
  4. typhonius created this gist Nov 18, 2013.
    203 changes: 203 additions & 0 deletions acquia.inc
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,203 @@
    <?php

    /**
    * @file
    * Utilities for use in protecting an environment via basic auth or IP whitelist.
    */

    function ac_protect_this_site() {
    global $conf;

    // Test if we are using drush (command-line interface)
    $cli = drupal_is_cli();

    // Is the user on the VPN? Default to FALSE.
    $on_vpn = $cli ? TRUE : FALSE;
    if (!empty($_SERVER['AH_Client_IP']) && !empty($conf['ah_whitelist'])) {
    $on_vpn = ah_ip_in_whitelist($_SERVER['AH_Client_IP'], $conf['ah_whitelist']);
    }

    // Should we skip the auth check? Default to FALSE.
    $skip_auth_check = FALSE;

    // Check if we should disable cache for this page.
    if (ah_path_no_cache()) {
    $conf['page_cache_maximum_age'] = 0;
    }

    // Check if we should skip auth check for this page.
    if (ah_path_skip_auth()) {
    $skip_auth_check = TRUE;
    }

    // Determine if we should password protect the site.
    //
    // Current business rules:
    // 1) Dev, stage, and prod are only accessible from whitelisted IPs.
    // 2) Users on whitelisted IPs must pass basic auth challenge to access.
    //
    // Always start by assuming we're protecting the site if the relevant variables
    // are set. Some rule below has to pass before we turn it off.
    $protect_ip = !empty($conf['ah_whitelist']);
    $protect_password = !empty($conf['ah_basic_auth_credentials']);

    // Do not protect command line requests, e.g. Drush.
    if ($cli) {
    $protect_ip = FALSE;
    $protect_password = FALSE;
    }

    // Un-comment to disable protection, e.g. for load tests.
    // $skip_auth_check = TRUE;
    // $on_vpn = TRUE;

    // If not on whitelisted IP prevent all access.
    if ($protect_ip && !$on_vpn) {
    ah_page_403();
    }

    // If not skipping auth, check basic auth.
    if ($protect_password && !$skip_auth_check) {
    ah_check_basic_auth();
    }
    }

    /**
    * Output a 403 (forbidden access) response.
    */
    function ah_page_403() {
    print "403 Forbidden: Access denied (". $_SERVER['AH_Client_IP'] .")";
    header('HTTP/1.0 403 Forbidden');
    exit;
    }

    /**
    * Output a 401 (unauthorized) response.
    */
    function ah_page_401() {
    header('WWW-Authenticate: Basic realm="This site is protected"');
    header('HTTP/1.0 401 Unauthorized');
    print "401 Unauthorized: Access denied (". $_SERVER['AH_Client_IP'] .")";
    exit;
    }

    /**
    * Check basic auth against allowed values.
    */
    function ah_check_basic_auth() {
    global $conf;

    $authorized = FALSE;
    $php_auth_user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : NULL;
    $php_auth_pw = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : NULL;
    $credentials = isset($conf['ah_basic_auth_credentials']) ? $conf['ah_basic_auth_credentials'] : NULL;

    if ($php_auth_user && $php_auth_pw && !empty($credentials)) {
    if (isset($credentials[$php_auth_user]) && $credentials[$php_auth_user] == $php_auth_pw) {
    $authorized = TRUE;
    }
    }

    if ($authorized) {
    return;
    }

    // Always fall back to 401.
    ah_page_401();
    }

    /**
    * Determine if the current path is in the list of paths to not cache.
    */
    function ah_path_no_cache() {
    global $conf;

    $q = isset($_GET['q']) ? $_GET['q'] : NULL;
    $paths = isset($conf['ah_paths_no_cache']) ? $conf['ah_paths_no_cache'] : NULL;
    if (!empty($q) && !empty($paths)) {
    foreach ($paths as $path) {
    if ($q == $path || strpos($q, $path) === 0) {
    return TRUE;
    }
    }
    }
    }

    /**
    * Determine if the current path is in the list of paths on which to not check
    * auth.
    */
    function ah_path_skip_auth() {
    global $conf;

    $q = isset($_GET['q']) ? $_GET['q'] : NULL;
    $paths = isset($conf['ah_paths_skip_auth']) ? $conf['ah_paths_skip_auth'] : NULL;
    if (!empty($q) && !empty($paths)) {
    foreach ($paths as $path) {
    if ($q == $path || strpos($q, $path) === 0) {
    return TRUE;
    }
    }
    }
    }

    /**
    * Forbid access to user or admin paths if the user is not on an approved IP range.
    *
    * @todo Use a $conf variable for this.
    */
    function ah_paths_restrict($on_vpn) {
    if (isset($_GET['q'])) {
    $restricted_path = ($_GET['q'] == 'user' || $_GET['q'] == 'admin' ||
    strpos($_GET['q'], 'user/') === 0 ||
    strpos($_GET['q'], 'admin/') === 0);
    // @TODO - remove debug
    // header( 'X-AH-debug-restricted-path: ' . ($restricted_path ? "restricted" : "not restricted") );
    if ($restricted_path && !$on_vpn) {
    ah_page_forbidden();
    }
    }
    }

    /**
    * Determine if the IP is within the ranges defined in the whitelist.
    */
    function ah_ip_in_whitelist($ip, $whitelist) {
    foreach ($whitelist as $cidr) {
    $range = ah_ip_cidr_range($cidr);
    if ($range && count($range) == 2) {
    $ip_long = ip2long($ip);
    $ip_one_long = $range[0];
    $ip_two_long = $range[1];
    $lte = $ip_one_long <= $ip_long;
    $gte = $ip_two_long >= $ip_long;
    if ($lte && $gte) {
    return TRUE;
    }
    }
    }
    }

    /**
    * From http://www.php.net/manual/en/function.ip2long.php#109298
    */
    function ah_ip_cidr_range($cidr) {
    list($ip, $mask) = explode('/', $cidr);

    // net mask binary string
    $maskBinStr = str_repeat("1", $mask ) . str_repeat("0", 32-$mask );
    // inverse mask
    $inverseMaskBinStr = str_repeat("0", $mask ) . str_repeat("1", 32-$mask );

    $ipLong = ip2long($ip);
    $ipMaskLong = bindec($maskBinStr);
    $inverseIpMaskLong = bindec($inverseMaskBinStr);
    $netWork = $ipLong & $ipMaskLong;

    // ignore network ID (eg: 192.168.1.0)
    $start = $netWork + 1;
    // ignore brocast IP(eg: 192.168.1.255)
    $end = ($netWork | $inverseIpMaskLong) - 1;

    return array($start, $end);
    }