
問題
古いWS_FTP サーバーWindows アップデート後に職場で起動しなくなりました。対応する管理者はもういません。私が持っている唯一の情報は次のとおりです。
- ファイル構造
- ユーザー名
- 各ユーザーのソルトなしSHA256ハッシュ
それらのハッシュの 1 つは次のとおりです。
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
これは に対応します"password"
。 で確認できます: echo -n password | sha256sum
。
FTP クライアントがどこにあるのかさえわかりません。そのうちのいくつかは、アクセスできないリモート センサーです。サーバーが停止するまで、データを送信し続けました。データは機密ではありませんが、それでも私たちにとっては重要です。
試み
走ってみたジョン・ザ・リッパーハッシュで、30 個中 4 個のパスワードが見つかりました。
ソルトなしの SHA256 ハッシュを使用する Linux FTP サーバーを探しました。安全性が低すぎるため、どのサーバーもこれを提案しませんでした。少なくともデフォルトでは提案しませんでした。
一部のサーバー(例:VSFTPD の) は htpasswd に委任します。ただし、ソルトなしの SHA256 ハッシュを保存する方法は見つかりませんでした。
質問
SHA256 で、ソルトなしで 1 ラウンドのみのハッシュを作成することは可能ですかhtpasswd
? 最小値は 8 バイトのソルトと 1000 ラウンドのようですmkpasswd
。
これらのハッシュで動作するように設定できる別の Linux FTP サーバーはありますか?
セキュリティについてはあまり気にしていません。センサーからの着信接続を受け入れる FTP サーバーをセットアップしたいだけです。
答え1
@Brocoが提案したpyftpdlib、私のニーズにぴったりでした!
手順は次のとおりです。
- Linuxサーバーをインストールする
- インストールアナコンダ
- インストールpyftpdlib
- ユーザー名、ハッシュ、フォルダの辞書を含むJSONファイルを作成する
- SHA256ハッシュを比較するFTPサーバー用のpyftpdlibスクリプトを作成する
- リダイレクトポート21からポート8021へ
- 実行するにはシステムユニット非特権ユーザーとして
- 再起動するFTPサーバーを自動的にjson ファイルが変更された場合。
json ファイルのテンプレートは次のとおりです。
{
"user1": {
"folder": "users/user1",
"sha256": "DFB0CE07EDF923F1F40BA56CC9BA9C396B53E3399E3164D60E35050BAA2BE9C9"
},
"user2": {
"folder": "users/user2",
"sha256": "5E884898DA28047151D0E56F8DC6292773603D0D6AABBDD62A11EF721D1542D8"
}
}
スクリプトは次のとおりです。
#!/opt/anaconda3/bin/python -u
#encoding: UTF-8
import re
import os
import hashlib
import json
from pathlib import Path
from pyftpdlib.authorizers import DummyAuthorizer, AuthenticationFailed
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.filesystems import AbstractedFS
FTP_FOLDER = Path("/media/ftp_data")
with open('input/ftp_users.json') as users_json:
USERS = json.loads(users_json.read())
current_folder = Path.cwd().resolve()
def login_dump(username, password, success):
if success:
subfolder = 'logins'
attr = 'w'
else:
subfolder = 'logins/failed'
attr = 'a'
logins_folder = current_folder.joinpath(subfolder)
logins_folder.mkdir(parents=True, exist_ok=True)
with open(str(logins_folder.joinpath(username)), attr) as user_file:
# NOTE: Could write better hash directly, e.g. with `mkpasswd -m sha-512`
user_file.write(password + "\n")
class SHA256Authorizer(DummyAuthorizer):
def validate_authentication(self, username, password, handler):
sha256_hash = hashlib.sha256(password.encode('ascii')).hexdigest().upper()
try:
# NOTE: Case sensitive!
if self.user_table[username]['pwd'] != sha256_hash:
login_dump(username, password, False)
raise AuthenticationFailed
except KeyError:
login_dump(username, password, False)
raise AuthenticationFailed
login_dump(username, password, True)
authorizer = SHA256Authorizer()
for user, params in USERS.items():
print("Adding user %r" % user)
folder = FTP_FOLDER.joinpath(params['folder'])
folder.mkdir(parents=True, exist_ok=True)
authorizer.add_user(user,
params['sha256'].upper(),
str(folder),
perm="elradfmw",
msg_login="Welcome, %s!" % user)
handler = FTPHandler
handler.authorizer = authorizer
handler.banner = "FTP server"
class WindowsOrUnixPathFS(AbstractedFS):
def ftpnorm(self, ftppath):
# NOTE: Some old clients still think they talk to a Windows Server
return super().ftpnorm(ftppath.replace("\\", "/"))
handler.abstracted_fs = WindowsOrUnixPathFS
handler.passive_ports = range(40000, 40500)
# NOTE: Port forwarding is needed because this script shouldn't be run as root.
# See https://serverfault.com/a/238565/442344
server = FTPServer((IP_ADDRESS, 8021), handler)
server.serve_forever()
スクリプトはパスワードをプレーンテキストで記録しますが、データは機密ではないため、これは許容範囲です。数か月後には、vsftpd に切り替える予定です。