Skip to content

Commit

Permalink
Merge pull request #362 from uzulla/issue291/access-control-by-region
Browse files Browse the repository at this point in the history
国コード指定でアクセスブロック機能 #291
  • Loading branch information
fc2dev authored Aug 12, 2021
2 parents 96f239c + 5f84720 commit fa7b406
Show file tree
Hide file tree
Showing 12 changed files with 449 additions and 108 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/app/temp/debug_html/*
/app/temp/log/*
/app/temp/installed.lock
/app/temp/GeoLite2-Country.mmdb
/app/temp/github_release_cache.json
/app/version
/app/vendor/
Expand Down
4 changes: 4 additions & 0 deletions app/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
// ログイン時にメール認証を有効化するか
define("MFA_EMAIL", "0");

// 国コード(ISO Code)指定でアクセスブロック
//define("USER_BLOCK_COUNTRY_ISO_CODE_CSV", "JP");
//define("ADMIN_BLOCK_COUNTRY_ISO_CODE_CSV", "JP,US");

// If you want get error log on display.
// define('ERROR_ON_DISPLAY', "1");
// ini_set('display_errors', '1');
Expand Down
4 changes: 4 additions & 0 deletions app/config_read_from_env.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
define("EMERGENCY_PASSWORD_RESET_ENABLE", (string)getenv("FC2_EMERGENCY_PASSWORD_RESET_ENABLE"));
define("MFA_EMAIL", (string)getenv("FC2_MFA_EMAIL"));

// 国コード(ISO Code)指定でアクセスブロック
define("USER_BLOCK_COUNTRY_ISO_CODE_CSV", (string)getenv("FC2_USER_BLOCK_COUNTRY_ISO_CODE_CSV"));
define("ADMIN_BLOCK_COUNTRY_ISO_CODE_CSV", (string)getenv("FC2_ADMIN_BLOCK_COUNTRY_ISO_CODE_CSV"));

if (strlen((string)getenv("FC2_GITHUB_REPO")) > 0) {
define("GITHUB_REPO", (string)getenv("FC2_GITHUB_REPO"));
}
96 changes: 96 additions & 0 deletions app/src/Service/AccessBlock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);

namespace Fc2blog\Service;

use Exception;
use Fc2blog\App;
use Fc2blog\Web\Request;
use MaxMind\Db\Reader;

class AccessBlock
{
const MMDB_FILE_PATH = App::TEMP_DIR . "/GeoLite2-Country.mmdb";

private $user_block_country_iso_code_csv;
private $admin_block_country_iso_code_csv;

public function __construct(
string $user_block_country_iso_code_csv = "",
string $admin_block_country_iso_code_csv = ""
)
{
if (strlen($user_block_country_iso_code_csv) > 0) {
$this->user_block_country_iso_code_csv = $user_block_country_iso_code_csv;
} elseif (defined("USER_BLOCK_COUNTRY_ISO_CODE_CSV")) {
$this->user_block_country_iso_code_csv = USER_BLOCK_COUNTRY_ISO_CODE_CSV;
} else {
$this->user_block_country_iso_code_csv = "";
}

if (strlen($admin_block_country_iso_code_csv) > 0) {
$this->admin_block_country_iso_code_csv = $admin_block_country_iso_code_csv;
} elseif (defined("ADMIN_BLOCK_COUNTRY_ISO_CODE_CSV")) {
$this->admin_block_country_iso_code_csv = ADMIN_BLOCK_COUNTRY_ISO_CODE_CSV;
} else {
$this->admin_block_country_iso_code_csv = "";
}

}

public function isAdminBlockIp(Request $request): bool
{
if (strlen($this->admin_block_country_iso_code_csv) === 0) return false;
/** @noinspection PhpUnhandledExceptionInspection */ // エラーなら、アプリは停止で良い
return $this->isBlockIp($request, $this->admin_block_country_iso_code_csv);
}

public function isUserBlockIp(Request $request): bool
{
if (strlen($this->user_block_country_iso_code_csv) === 0) return false;
/** @noinspection PhpUnhandledExceptionInspection */ // エラーなら、アプリは停止で良い
return $this->isBlockIp($request, $this->user_block_country_iso_code_csv);
}

/**
* Check IP address that have to blocked with Read MaxMind Geo ip database.
* @param Request $request
* @param string $block_country_iso_code_csv
* @return bool
* @throws Reader\InvalidDatabaseException
* @throws Exception
*/
public function isBlockIp(Request $request, string $block_country_iso_code_csv): bool
{
if (
!file_exists(self::MMDB_FILE_PATH) ||
!is_file(self::MMDB_FILE_PATH) ||
!is_readable(self::MMDB_FILE_PATH)
) {
// mmdb file notfound. Not to be checking. Done.
return false;
}

$reader = new Reader(self::MMDB_FILE_PATH);
$result = $reader->get($request->getClientIpAddress());
$reader->close();
if (
!is_array($result) || // If undetermined, Result will be null.
!isset($result['country']) ||
!isset($result['country']['iso_code'])
) {
// Could not detect country information. So allow access.
return false;
}

$determined_country_iso_code = $result['country']['iso_code'];

return $this->isContainCsv($determined_country_iso_code, $block_country_iso_code_csv);
}

private function isContainCsv(string $country_iso_code, string $block_country_iso_code_csv): bool
{
$list = explode(',', $block_country_iso_code_csv);
return in_array($country_iso_code, $list);
}
}
19 changes: 15 additions & 4 deletions app/src/Web/Controller/Admin/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@
use Fc2blog\App;
use Fc2blog\Model\BlogsModel;
use Fc2blog\Model\UsersModel;
use Fc2blog\Service\AccessBlock;
use Fc2blog\Service\BlogService;
use Fc2blog\Web\Controller\Controller;
use Fc2blog\Web\Request;
use Fc2blog\Web\Session;

abstract class AdminController extends Controller
{
protected function beforeFilter(Request $request)
protected function beforeFilter(Request $request): string
{
// 親のフィルター呼び出し
parent::beforeFilter($request);
$template_path = parent::beforeFilter($request);
if (strlen($template_path) > 0) {
return $template_path;
}

// install.lockファイルがなければインストーラーへ
if (!$this->isInstalled() && (
Expand All @@ -26,6 +30,11 @@ protected function beforeFilter(Request $request)
$this->redirect($request, ['controller' => 'Common', 'action' => 'install']);
}

// IPアドレスからアクセス元の国を推定してのブロック
if ((new AccessBlock())->isAdminBlockIp($request)) {
return $this->error403();
}

if (!$this->isLogin()) {
// 未ログイン時でもアクセス許可するパターンリスト
$allows = array(
Expand All @@ -40,7 +49,7 @@ protected function beforeFilter(Request $request)
if (!isset($allows[$controller_name]) || !in_array($action_name, $allows[$controller_name])) {
$this->redirect($request, array('controller' => 'Session', 'action' => 'login'));
}
return;
return "";
}

if (!$this->isSelectedBlog()) {
Expand All @@ -57,14 +66,16 @@ protected function beforeFilter(Request $request)
$this->setWarnMessage(__('Please select a blog'));
$this->redirect($request, ['controller' => 'Blogs', 'action' => 'index']);
}
return;
return "";
}

// ログイン中でかつブログ選択中の場合ブログ情報を取得し時間設定を行う
$blog = BlogService::getById($this->getBlogIdFromSession());
if (is_array($blog) && isset($blog['timezone'])) {
date_default_timezone_set($blog['timezone']);
}

return "";
}

/**
Expand Down
8 changes: 6 additions & 2 deletions app/src/Web/Controller/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ public function execute($method): void
*/
public function prepare(string $method): string
{
$this->beforeFilter($this->request);
$template_path = $this->beforeFilter($this->request);
if (strlen($template_path) > 0) {
return $template_path;
}

$this->resolvedMethod = $method;

Expand Down Expand Up @@ -130,8 +133,9 @@ protected function isInvalidAjaxRequest(Request $request): bool
return false;
}

protected function beforeFilter(Request $request)
protected function beforeFilter(Request $request): string
{
return "";
}

public function set(string $key, $value)
Expand Down
10 changes: 8 additions & 2 deletions app/src/Web/Controller/User/EntriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ class EntriesController extends UserController
/**
* 記事系統の前処理
* @param Request $request
* @return string
*/
protected function beforeFilter(Request $request): void
protected function beforeFilter(Request $request): string
{
parent::beforeFilter($request);
$template_path = parent::beforeFilter($request);
if (strlen($template_path) > 0) {
return $template_path;
}

// ブログID指定があるかチェック
$blog_id = $request->getBlogId();
Expand Down Expand Up @@ -76,6 +80,8 @@ protected function beforeFilter(Request $request): void
$entries_model = new EntriesModel();
$entries_model->updateReservation($blog_id);
$entries_model->updateLimited($blog_id);

return "";
}

/**
Expand Down
21 changes: 17 additions & 4 deletions app/src/Web/Controller/User/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Fc2blog\Web\Controller\User;

use Fc2blog\Model\BlogsModel;
use Fc2blog\Service\AccessBlock;
use Fc2blog\Web\Controller\Controller;
use Fc2blog\Web\Fc2BlogTemplate;
use Fc2blog\Web\Request;
Expand All @@ -12,6 +13,22 @@

abstract class UserController extends Controller
{
protected function beforeFilter(Request $request): string
{
// 親のフィルター呼び出し
$template_path = parent::beforeFilter($request);
if (strlen($template_path) > 0) {
return $template_path;
}

// IPアドレスからアクセス元の国を推定してのブロック
if ((new AccessBlock())->isUserBlockIp($request)) {
return $this->error403();
}

return "";
}

/**
* 管理画面ログイン中のブログIDを取得する
*/
Expand Down Expand Up @@ -80,9 +97,6 @@ protected static function getEntryPasswordKey(string $blog_id, int $entry_id): s
*/
protected function renderByFc2Template(Request $request, string $template_file_path): string
{
if (is_null($template_file_path)) {
throw new InvalidArgumentException("undefined template");
}
if (!is_file($template_file_path)) {
throw new InvalidArgumentException("missing template");
}
Expand All @@ -94,7 +108,6 @@ protected function renderByFc2Template(Request $request, string $template_file_p

// テンプレートをレンダリングして返す
ob_start();
/** @noinspection PhpIncludeInspection */
include($template_file_path);
return ob_get_clean();
}
Expand Down
11 changes: 11 additions & 0 deletions app/src/Web/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ public function getReferer(): string
return $this->server['HTTP_REFERER'] ?? '';
}

/**
* アクセスIPアドレスを返却 取得できなかった場合は空文字を返却
* @return string
*/
public function getClientIpAddress(): string
{
// TODO support X_FORWARDED_FOR and other.
// TODO どの環境変数を「信用するか」を設定する項目が必要
return $this->server['REMOTE_ADDR'] ?? '';
}

public function getPath()
{
return $this->path;
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"nikic/php-parser": "^4.10",
"tuupola/base62": "^2.1",
"mibe/feedwriter": "^1.1",
"swiftmailer/swiftmailer": "^6.0"
"swiftmailer/swiftmailer": "^6.0",
"maxmind-db/reader": "~1.0"
},
"config": {
"vendor-dir": "app/vendor"
Expand Down
Loading

0 comments on commit fa7b406

Please sign in to comment.