mirror of
https://gitlab.com/ArkHost/WP-Security-Pack.git
synced 2026-09-19 17:37:30 +02:00
313 lines
7.5 KiB
PHP
313 lines
7.5 KiB
PHP
<?php
|
|
/**
|
|
* Geo-blocking for WP Security Pack.
|
|
*
|
|
* @package WP_Security_Pack
|
|
*/
|
|
|
|
// Prevent direct access.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Geo-blocking class using IP2Location Lite database.
|
|
*/
|
|
class WPSP_Geo_Blocking {
|
|
|
|
/**
|
|
* IP2Location database file path.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $db_path = '';
|
|
|
|
/**
|
|
* IP2Location database object.
|
|
*
|
|
* @var object|null
|
|
*/
|
|
private $db = null;
|
|
|
|
/**
|
|
* Country lookup cache.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $cache = array();
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function __construct() {
|
|
$this->db_path = WP_Security_Pack::get_setting( 'geo_database_path', '' );
|
|
|
|
if ( empty( $this->db_path ) ) {
|
|
// Default path in plugin directory.
|
|
$this->db_path = WPSP_PLUGIN_DIR . 'data/IP2LOCATION-LITE-DB1.BIN';
|
|
}
|
|
|
|
// Check geo-blocking immediately (constructor runs during init).
|
|
// This blocks access to the entire website for blocked countries.
|
|
if ( WP_Security_Pack::get_setting( 'geo_blocking_enabled', false ) ) {
|
|
$this->check_geo_access();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if geo-blocking should apply.
|
|
*/
|
|
public function check_geo_access() {
|
|
$ip = WPSP_Helper::get_client_ip();
|
|
|
|
if ( ! $ip ) {
|
|
return;
|
|
}
|
|
|
|
// Check if IP is whitelisted.
|
|
$ip_control = wpsp()->get_component( 'ip_control' );
|
|
if ( $ip_control && $ip_control->is_whitelisted( $ip ) ) {
|
|
return;
|
|
}
|
|
|
|
// Get country code.
|
|
$country_code = $this->get_country_code( $ip );
|
|
|
|
if ( ! $country_code ) {
|
|
return; // Can't determine country, allow access.
|
|
}
|
|
|
|
// Check if country is blocked.
|
|
$blocked_countries = WP_Security_Pack::get_setting( 'geo_blocked_countries', array() );
|
|
|
|
if ( ! empty( $blocked_countries ) && in_array( $country_code, $blocked_countries, true ) ) {
|
|
WPSP_Activity_Log::log(
|
|
WPSP_Activity_Log::EVENT_GEO_BLOCKED,
|
|
$ip,
|
|
null,
|
|
sprintf(
|
|
/* translators: %s: Country code */
|
|
__( 'Blocked country: %s', 'wp-security-pack' ),
|
|
$country_code
|
|
)
|
|
);
|
|
|
|
$this->block_access( $country_code );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get country code for an IP address.
|
|
*
|
|
* @param string $ip IP address.
|
|
* @return string|null Two-letter country code or null.
|
|
*/
|
|
public function get_country_code( $ip ) {
|
|
// Check cache first.
|
|
if ( isset( $this->cache[ $ip ] ) ) {
|
|
return $this->cache[ $ip ];
|
|
}
|
|
|
|
// Check transient cache.
|
|
$cache_key = 'wpsp_geo_' . md5( $ip );
|
|
$cached = get_transient( $cache_key );
|
|
|
|
if ( false !== $cached ) {
|
|
$this->cache[ $ip ] = $cached;
|
|
return $cached;
|
|
}
|
|
|
|
$country_code = null;
|
|
|
|
// Try IP2Location database first.
|
|
$country_code = $this->lookup_ip2location( $ip );
|
|
|
|
// Fallback to PHP geoip extension.
|
|
if ( ! $country_code && function_exists( 'geoip_country_code_by_name' ) ) {
|
|
// phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.geoip_country_code_by_nameDeprecated
|
|
$country_code = @geoip_country_code_by_name( $ip );
|
|
}
|
|
|
|
// Cache the result.
|
|
if ( $country_code ) {
|
|
$country_code = strtoupper( $country_code );
|
|
$this->cache[ $ip ] = $country_code;
|
|
set_transient( $cache_key, $country_code, HOUR_IN_SECONDS );
|
|
}
|
|
|
|
return $country_code;
|
|
}
|
|
|
|
/**
|
|
* Lookup IP using IP2Location database.
|
|
*
|
|
* @param string $ip IP address.
|
|
* @return string|null
|
|
*/
|
|
private function lookup_ip2location( $ip ) {
|
|
if ( ! file_exists( $this->db_path ) ) {
|
|
return null;
|
|
}
|
|
|
|
// Load the database reader.
|
|
if ( null === $this->db ) {
|
|
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-ip2location.php';
|
|
$this->db = new WPSP_IP2Location( $this->db_path );
|
|
}
|
|
|
|
if ( ! $this->db ) {
|
|
return null;
|
|
}
|
|
|
|
$record = $this->db->lookup( $ip );
|
|
|
|
if ( $record && ! empty( $record['country_code'] ) && '-' !== $record['country_code'] ) {
|
|
return $record['country_code'];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Block access for geo-blocked countries.
|
|
*
|
|
* @param string $country_code Country code.
|
|
*/
|
|
private function block_access( $country_code ) {
|
|
status_header( 403 );
|
|
nocache_headers();
|
|
|
|
$countries = WPSP_Helper::get_countries();
|
|
$country_name = isset( $countries[ $country_code ] ) ? $countries[ $country_code ] : $country_code;
|
|
|
|
$message = sprintf(
|
|
/* translators: %s: Country name */
|
|
__( 'Access from %s is not permitted.', 'wp-security-pack' ),
|
|
$country_name
|
|
);
|
|
|
|
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
|
|
wp_send_json_error( array( 'message' => $message ), 403 );
|
|
}
|
|
|
|
wp_die(
|
|
esc_html( $message ),
|
|
esc_html__( 'Access Denied', 'wp-security-pack' ),
|
|
array(
|
|
'response' => 403,
|
|
'back_link' => false,
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if geo-database exists.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function database_exists() {
|
|
return file_exists( $this->db_path );
|
|
}
|
|
|
|
/**
|
|
* Get database info.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_database_info() {
|
|
$info = array(
|
|
'exists' => false,
|
|
'path' => $this->db_path,
|
|
'size' => 0,
|
|
'modified' => null,
|
|
'type' => '',
|
|
);
|
|
|
|
if ( file_exists( $this->db_path ) ) {
|
|
$info['exists'] = true;
|
|
$info['size'] = filesize( $this->db_path );
|
|
$info['modified'] = filemtime( $this->db_path );
|
|
|
|
// Try to get database type.
|
|
if ( null === $this->db ) {
|
|
require_once WPSP_PLUGIN_DIR . 'includes/class-wpsp-ip2location.php';
|
|
$this->db = new WPSP_IP2Location( $this->db_path );
|
|
}
|
|
|
|
if ( $this->db ) {
|
|
$info['type'] = $this->db->get_database_type();
|
|
}
|
|
}
|
|
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Download IP2Location Lite database.
|
|
*
|
|
* @param string $download_token IP2Location download token (optional for LITE).
|
|
* @return bool|WP_Error
|
|
*/
|
|
public function download_database( $download_token = '' ) {
|
|
// IP2Location LITE DB1 (Country only) - free, no token required.
|
|
$download_url = 'https://download.ip2location.com/lite/IP2LOCATION-LITE-DB1.BIN.ZIP';
|
|
|
|
// If token provided, use the authenticated endpoint.
|
|
if ( ! empty( $download_token ) ) {
|
|
$download_url = 'https://www.ip2location.com/download/?token=' . urlencode( $download_token ) . '&file=DB1LITEBIN';
|
|
}
|
|
|
|
// Create data directory.
|
|
$data_dir = WPSP_PLUGIN_DIR . 'data';
|
|
if ( ! file_exists( $data_dir ) ) {
|
|
wp_mkdir_p( $data_dir );
|
|
}
|
|
|
|
// Download the file.
|
|
$tmp_file = download_url( $download_url, 300 );
|
|
|
|
if ( is_wp_error( $tmp_file ) ) {
|
|
return $tmp_file;
|
|
}
|
|
|
|
// Check if it's a ZIP file.
|
|
$finfo = finfo_open( FILEINFO_MIME_TYPE );
|
|
$mime = finfo_file( $finfo, $tmp_file );
|
|
finfo_close( $finfo );
|
|
|
|
$target_path = $data_dir . '/IP2LOCATION-LITE-DB1.BIN';
|
|
|
|
if ( 'application/zip' === $mime || 'application/x-zip-compressed' === $mime ) {
|
|
// Extract ZIP.
|
|
$zip = new ZipArchive();
|
|
if ( true === $zip->open( $tmp_file ) ) {
|
|
// Find the BIN file.
|
|
for ( $i = 0; $i < $zip->numFiles; $i++ ) {
|
|
$filename = $zip->getNameIndex( $i );
|
|
if ( preg_match( '/\.BIN$/i', $filename ) ) {
|
|
$content = $zip->getFromIndex( $i );
|
|
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents
|
|
file_put_contents( $target_path, $content );
|
|
break;
|
|
}
|
|
}
|
|
$zip->close();
|
|
}
|
|
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
|
|
unlink( $tmp_file );
|
|
} else {
|
|
// Direct BIN file.
|
|
// phpcs:ignore WordPress.WP.AlternativeFunctions.rename_rename
|
|
rename( $tmp_file, $target_path );
|
|
}
|
|
|
|
if ( file_exists( $target_path ) ) {
|
|
// Update database path setting.
|
|
WP_Security_Pack::update_setting( 'geo_database_path', $target_path );
|
|
return true;
|
|
}
|
|
|
|
return new WP_Error( 'download_failed', __( 'Failed to download or extract database.', 'wp-security-pack' ) );
|
|
}
|
|
}
|