Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- namespace Lsa\Components\Impl;
- class IPBlock implements \Lsa\Components\IRegisterable {
- const DEFAULT_ALLOWED_COUNTRIES = ['SK', 'CZ', 'DE', 'UK', 'FR', 'AT', 'AU', 'PL', 'HU', 'NL', 'DK', 'NO', 'FI', 'SW'];
- const OPTION_ALLOWED = 'lsa_ipblock_countries';
- const OPTION_API_KEY = 'lsa_ipblock_apikey';
- const OPTION_THRESHOLD = 'lsa_ipblock_threshold';
- const OPTION_MODE = 'lsa_ipblock_mode';
- const OPTION_FREEGEOIP_URL = 'lsa_freegeoip_url';
- const SECTION_ANTISPAM = 'lsa_antispam';
- const SECTION_ANTISPAM_PAGE = 'writing';
- public static $MODES = [
- 'wl-c' => 'Whitelist krajiny',
- 'bl-c' => 'Blacklist krajiny',
- 'wl-c_p' => 'Whitelist krajiny, potom kontrola proxy',
- 'bl-c_p' => 'Blacklist krajiny, potom kontrola proxy',
- 'p_wl-c' => 'Kontrola proxy, nad prah whitelist krajiny', // pri označení ako proxy
- 'p_bl-c' => 'Kontrola proxy, nad prah blacklist krajiny',
- 'p' => 'Len kontrola proxy, nad prah zamietnuť'
- ];
- /** @var self */
- private static $instance = null;
- /** @var string[] ProxyCheck.io cache, array IP => API result */
- private $ipCache = [];
- /** @var string[] GeoIP cache, array IP => country */
- protected $geoIPCache = [];
- public function __construct() {
- self::$instance = $this;
- }
- public function register(): void {
- add_filter('registration_errors', [$this, 'registration_errors'], 10, 3);
- add_filter('pre_comment_approved', [$this, 'pre_comment_approved'], 10, 2);
- if (is_admin()) {
- add_action('admin_init', [$this, 'admin_init']);
- }
- }
- public function getClientCountry(string $ip = null): string {
- $ourGeo = $this->getGeoLoc($ip);
- // TODO: Probably should be moved to getGeoLoc?
- if (empty($ourGeo)) {
- $res = $this->doProxyCheck($ip);
- if ($res && isset($res[$ip]) && isset($res[$ip]['isocode'])) {
- $ourGeo = strtoupper($res[$ip]['isocode']);
- $this->geoIPCache[$ip] = $ourGeo;
- }
- }
- return $ourGeo;
- }
- public function isClientAllowed(string $ip = null): bool {
- $ip = $ip ?: $_SERVER['REMOTE_ADDR'];
- $mode = $this->getMode();
- $inList = in_array($this->getClientCountry($ip), $this->getAllowedCountries());
- switch ($this->getMode()) {
- case 'wl-c': // Whitelist krajiny
- return $inList;
- case 'bl-c': // Blacklist krajiny
- return !$inList;
- case 'wl-c_p': // Whitelist krajiny, potom kontrola proxy
- if (!$inList)
- return false;
- $res = $this->doProxyCheck($ip);
- if (!$res)
- return true;
- $entry = $res[$ip];
- if ($entry['risk'] > $this->getThreshold())
- return false;
- return $entry['proxy'] == 'no';
- case 'bl-c_p': // Blacklist krajiny, potom kontrola proxy
- if ($inList)
- return false;
- $res = $this->doProxyCheck($ip);
- if (!$res)
- return true;
- $entry = $res[$ip];
- if ($entry['risk'] > $this->getThreshold())
- return false;
- return $entry['proxy'] == 'no';
- case 'p_wl-c': // Kontrola proxy, pri neistom označení whitelist krajiny
- $res = $this->doProxyCheck($ip);
- if (!$res)
- return true;
- $entry = $res[$ip];
- if ($entry['risk'] > $this->getThreshold())
- return $inList;
- return $entry['proxy'] == 'no';
- case 'p_bl-c': // Kontrola proxy, pri neistom označení blacklist krajiny
- $res = $this->doProxyCheck($ip);
- if (!$res)
- return true;
- $entry = $res[$ip];
- if ($entry['risk'] > $this->getThreshold())
- return !$inList;
- return $entry['proxy'] == 'no';
- case 'p': // Len kontrola proxy, nad prah zamietnuť
- $res = $this->doProxyCheck($ip);
- if (!$res)
- return true;
- $entry = $res[$ip];
- if ($entry['risk'] > $this->getThreshold())
- return false;
- return $entry['proxy'] == 'no';
- default:
- // TODO: NotImplementedException
- error_log('IPBlock: Unknown mode: ' . $mode . ' while checking ' . $ip);
- return false;
- }
- }
- public function pre_comment_approved($approved, array $comment) {
- if (!is_user_logged_in() && !$this->isClientAllowed() && $approved === 1) {
- return 'spam';
- }
- return $approved;
- }
- public function registration_errors(\WP_Error $errors, string $sanitizedUserLogin, string $userMail): \WP_Error {
- if (!$this->isClientAllowed()) {
- $errors->add('banned_country', 'You are not allowed to register from this country. Contact the admin for an exception.');
- }
- return $errors;
- }
- public function admin_init(): void {
- if (!current_user_can('administrator'))
- return;
- add_settings_section(self::SECTION_ANTISPAM, 'Nastavenia antispamu', [$this, 'doSectionHeader'], self::SECTION_ANTISPAM_PAGE);
- add_settings_field(
- self::OPTION_MODE,
- 'Režim detekcie',
- [$this, 'doModeField'],
- self::SECTION_ANTISPAM_PAGE,
- self::SECTION_ANTISPAM
- );
- add_settings_field(
- self::OPTION_ALLOWED,
- 'Povolené krajiny prispievateľov',
- [$this, 'doWhitelistField'],
- self::SECTION_ANTISPAM_PAGE,
- self::SECTION_ANTISPAM
- );
- add_settings_field(
- self::OPTION_API_KEY,
- 'API kľúč Proxycheck.io',
- [$this, 'doApiKeyField'],
- self::SECTION_ANTISPAM_PAGE,
- self::SECTION_ANTISPAM
- );
- add_settings_field(
- self::OPTION_THRESHOLD,
- 'Maximálna hranica rizika',
- [$this, 'doThresholdField'],
- self::SECTION_ANTISPAM_PAGE,
- self::SECTION_ANTISPAM
- );
- add_settings_field(
- self::OPTION_FREEGEOIP_URL,
- 'URL FreeGeoIP endpointu',
- [$this, 'doEndpointUrlField'],
- self::SECTION_ANTISPAM_PAGE,
- self::SECTION_ANTISPAM
- );
- register_setting(self::SECTION_ANTISPAM_PAGE, self::OPTION_MODE);
- register_setting(self::SECTION_ANTISPAM_PAGE, self::OPTION_ALLOWED);
- register_setting(self::SECTION_ANTISPAM_PAGE, self::OPTION_API_KEY);
- register_setting(self::SECTION_ANTISPAM_PAGE, self::OPTION_THRESHOLD);
- register_setting(self::SECTION_ANTISPAM_PAGE, self::OPTION_FREEGEOIP_URL);
- }
- public function doModeField(): void {
- ?>
- <select name="<?=self::OPTION_MODE?>" id="<?=self::OPTION_MODE?>">
- <?php foreach (self::$MODES as $key => $value) { ?>
- <option value="<?=esc_attr($key)?>" <?php selected($key, $this->getMode()); ?>><?=esc_html($value)?></option>
- <?php } ?>
- </select>
- <?php
- }
- public function doWhitelistField(): void {
- ?>
- <input type="text" name="<?=self::OPTION_ALLOWED?>" id="<?=self::OPTION_ALLOWED?>" value="<?=esc_attr(implode(',', $this->getAllowedCountries()))?>" class="regular-text" />
- <p class="description" id="tagline-description">Krajiny, z ktorých sa používatelia môžu registrovať alebo anonymne pridávať príspevky. Podľa ISO 3166-1 alpha-2.</p>
- <?php
- }
- public function doApiKeyField(): void {
- ?>
- <input type="text" name="<?=self::OPTION_API_KEY?>" id="<?=self::OPTION_API_KEY?>" value="<?=esc_attr($this->getProxyCheckApiKey())?>" class="regular-text" />
- <p class="description" id="tagline-description">API kľúč pre službu <a href="https://proxycheck.io/">ProxyCheck.io</a></p>
- <?php
- }
- public function doThresholdField(): void {
- ?>
- <input type="number" name="<?=self::OPTION_THRESHOLD?>" id="<?=self::OPTION_THRESHOLD?>" value="<?=esc_attr($this->getThreshold())?>" class="regular-text" />
- <p class="description" id="tagline-description">Maximálna hodnota <a href="https://proxycheck.io/api/#risk_score" title="Dokumentácia API ProxyCheck.io" target="_blank">"skóre rizika"</a>, pri ktorej príspevok prejde. Zadané číslo 0 = 0 %, čiže nástroj si je stopercentne istý, že klient <b>nie je</b> proxy/VPN. pod 33 % je nízka miera rizika.</p>
- <?php
- }
- public function doEndpointUrlField(): void {
- ?>
- <input type="text" name="<?=self::OPTION_FREEGEOIP_URL?>" id="<?=self::OPTION_FREEGEOIP_URL?>" value="<?=esc_attr($this->getEndpointUrl())?>" class="regular-text" />
- <p class="description" id="tagline-description">Placeholder <i>%IP%</i> bude nahradený urlencode-nutou IP klienta.</p>
- <?php
- }
- public function doSectionHeader(): void {
- ?>
- <p>Kontrola krajiny je vždy vykonávaná cez FreeGeoIP. Ak toto nebude správne fungovať, treba povedať Erikovi. :)</p>
- <p>V prípade nedostupnosti alebo nesprávnej konfigurácie služby bude považovaná proxy kontrola za <b>úspešnú</b>, teda s výsledkom <i>"nie je proxy/VPN"</i>.</p>
- <?php
- }
- public function getMode(): string {
- return get_option(self::OPTION_MODE, 'p');
- }
- public function getProxyCheckApiKey(): ?string {
- return get_option(self::OPTION_API_KEY);
- }
- public function getThreshold(): int {
- return (int) get_option(self::OPTION_THRESHOLD, 33);
- }
- public function getEndpointUrl(): string {
- return get_option(self::OPTION_FREEGEOIP_URL, 'https://www.letemsvetemapplem.eu/freegeoip.php?ip=%IP%');
- }
- public function getAllowedCountries(): array {
- $res = get_option(self::OPTION_ALLOWED, implode(',', self::DEFAULT_ALLOWED_COUNTRIES));
- $res = preg_replace('/\s+/', '', $res);
- $res = mb_strtoupper($res);
- return explode(',', $res);;
- }
- public function doProxyCheck(string $ip): ?array {
- $cachedKey = 'lsa_proxycheck_' . md5($ip);
- if (( $cached = get_transient($cachedKey) ))
- return $cached;
- // https://proxycheck.io/api/#query_flags
- $url = 'http://proxycheck.io/v2/' . urlencode($ip);
- $url .= '?key=' . ($this->getProxyCheckApiKey() ?: '');
- $url .= '&vpn=1'; // Check for both VPN's and Proxies instead of just Proxies.
- $url .= '&asn=1'; // Enable ASN data response.
- $url .= '&risk=1'; // 0 = Off, 1 = Risk Score (0-100), 2 = Risk Score & Attack History.
- $url .= '&days=14'; // Restrict checking to proxies seen in the past # of days.
- $ch = curl_init($url);
- curl_setopt_array($ch, [
- CURLOPT_CONNECTTIMEOUT => 30,
- CURLOPT_RETURNTRANSFER => true
- ]);
- $res = curl_exec($ch);
- $resCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- if (curl_errno($ch)) {
- error_log('ProxyCheck for ' . $ip . ' (HTTP ' . $resCode . ') failed with cURL error ' . curl_error($ch));
- curl_close($ch);
- return null;
- }
- curl_close($ch);
- if (empty($res))
- return null;
- $dec = json_decode($res, true);
- if (isset($dec['status']) && !in_array($dec['status'], ['ok', 'warning'])) {
- error_log('ProxyCheck.io error returned by API: ' . $dec['message']);
- return null;
- }
- set_transient($cachedKey, $dec, 86400);
- return $dec;
- }
- public function getGeoLoc(string $ip = ""): string {
- $ip = empty($ip) ? $_SERVER['REMOTE_ADDR'] : $ip;
- if (isset($this->geoIPCache[$ip]))
- return $this->geoIPCache[$ip];
- $ch = curl_init(str_replace('%IP%', urlencode($ip), $this->getEndpointUrl()));
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
- curl_setopt($ch, CURLOPT_TIMEOUT, 5);
- $c = '';
- $suc = !( ($c = curl_exec($ch)) === false);
- curl_close($ch);
- $c = strtoupper($c);
- $this->geoIPCache[$ip] = $c;
- return $c;
- }
- public static function get() : self {
- if (self::$instance == null) {
- self::$instance = new self();
- }
- return self::$instance;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment