tomoyotitle.png

環境変数を用いた SSH サービスのブルートフォース対策

概要

このページでは、SSHサーバからログインシェルが実行される時に環境変数を用いた追加のユーザ認証を行うことにより、ブルートフォース対策を行う手順について紹介します。

システムのユーザ認証で使うパスワードがブルートフォース攻撃により割り出されてしまっても、もう1つのパスワードとして環境変数名とその値の両方が割り出されない限り、ログインシェルが実行されることがありません。

従来のブルートフォース対策は、認証が失敗したIPアドレスからの接続を一定時間拒否するという方法のため、多数のIPアドレスに分散されると防ぎきれない場合があります。この方法は2段階に分けているので、第1段階が突破されたとしても被害を防ぐことが可能です。また、第2段階での認証の自由度が高く認証方式も秘密にできるのでブルートフォース攻撃を仕掛けることが困難です。

ステップ1:ソースコードのコンパイル

パスワードとして使用する環境変数の名前と値を決めます。このページでは、環境変数名として CERBERUS を、値として sftp を使用します。

以下のソースコードをコンパイルします。コンパイルされたプログラムの名前を /bin/env_chk とします。また、SSHサーバのパス名を /usr/sbin/sshd とします。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SECRET_ENVIRONMENT "CERBERUS=sftp"
#define SSHD_EXECUTABLE_PATH "/usr/sbin/sshd"

int main(int raw_argc, char *raw_argv[])
{
	int i;
	int argc;
	int envc;
	char *filename;
	char **argv;
	char **envp;
	{ /* Check that I'm an execute handler process.  */
		int fd = open("/proc/ccs/.execute_handler", O_RDONLY);
		close(fd);
		if (fd == EOF) {
			fprintf(stderr, "FATAL: I'm not execute_handler.\n");
			return 1;
		}
	}
	if (raw_argc < 7)
		return 1;
	filename = raw_argv[4];
	argc = atoi(raw_argv[5]);
	envc = atoi(raw_argv[6]);
	if (raw_argc != argc + envc + 7)
		return 1;
	for (i = 5; i < argc + 5; i++)
		raw_argv[i] = raw_argv[i + 2];
	raw_argv[argc + 5] = NULL;
	for (i = argc + 6; i < argc + envc + 6; i++)
		raw_argv[i] = raw_argv[i + 1];
	raw_argv[argc + envc + 6] = NULL;
	argv = raw_argv + 5;
	envp = raw_argv + argc + 6;
	/* "/usr/sbin/sshd" executes "/usr/sbin/sshd" with "-R" option. */
	if (argc == 2 && !strcmp(argv[1], "-R") &&
	    !strcmp(raw_argv[2], SSHD_EXECUTABLE_PATH) &&
	    !strcmp(filename, SSHD_EXECUTABLE_PATH)) {
		execve(filename, argv, envp);
		return 1;
	}
	/*
	 * Check environment variables passed to execve() request
	 * and execute the program if it has SECRET_ENVIRONMENT environment.
	 */
	for (i = 0; i < envc; i++) {
		if (strcmp(envp[i], SECRET_ENVIRONMENT))
			continue;
		while (i < envc) {
			envp[i] = envp[i + 1];
			i++;
		}
		execve(filename, argv, envp);
		break;
	}
	return 1;
}

ステップ2:環境変数を渡すための設定

サーバ側の /etc/ssh/sshd_config の末尾に環境変数名を指定します。

AcceptEnv CERBERUS

クライアント側の /etc/ssh/ssh_config の末尾に環境変数名を指定します。

SendEnv CERBERUS

ステップ3: TOMOYO Linux のインストールと初期化

TOMOYO Linux をインストールしてから、以下のコマンドを実行して初期設定を行ってください。

# /usr/lib/ccs/init_policy

その後、 TOMOYO Linux カーネルで再起動する前に以下の操作を行ってください。

/usr/sbin/sshd が実行された場合にはドメイン遷移が初期化されるようにするために、 /etc/ccs/exception_policy.conf に以下の内容を追加します。

initialize_domain /usr/sbin/sshd from any

/usr/sbin/sshd からのプログラムの実行要求が /bin/env_chk に渡されるようにするために、 /etc/ccs/domain_policy.conf に以下の内容を追加します。

<kernel> /usr/sbin/sshd
task auto_execute_handler /bin/env_chk

ステップ4:運用

以上で設定は完了です。 TOMOYO Linux カーネルで再起動してください。

クライアント側から sftp でサーバ側にアクセスし、すぐに切断されることを確認してください。

クライアント側で環境変数 CERBERUS=sftp を設定してください。

クライアント側から sftp でサーバ側にアクセスし、操作が行えることを確認してください。

説明

TOMOYO Linux の execute_handler 機能が /usr/sbin/sshd からのプログラムの実行要求を横取りして、 /bin/env_chk に渡しています。 /bin/env_chk がプログラムの実行要求に渡されたパラメータの検査を行い、適切であると判断した場合には要求されたプログラムを実行します。

システムのユーザ認証を通過後なので、シェルの実行要求を伴わないSSHの機能は許可されています。ポートフォワーディングの制限やシェルセッションの制限なども行いたい場合は、通常通り学習モードで動作を学習させて、強制モードで動作を制限してやる必要があります。

応用

冒頭の動画では sftp を実行するために ssh ログインをしているため、パスワードとして sftp を指定しています。パスワードは固定されている必要は無く、パスワードの内容に応じて異なる動作をさせることもできます。例えば、 ssh_type=ro-sftp の場合はダウンロード専用の sftp サービス、 ssh_type=rw-sftp の場合はアップロードも可能な sftp サービスとして動作させることができます。

また、プログラム実行時のパラメータが渡されるので、その内容を記録に残すことができます。例えば、SSHセッションで実行されたプログラムと引数や環境変数などの情報を取得することができます。