2月から取り沙汰されてきたネット証券への不正アクセス・不正取引を巡り、新たな騒動が巻き起こっている。4月30日にSBI証券が、多要素認証なしにログイン可能だった「バックアップサイト」の閉鎖を発表した他、5月1日、SNSで人気の投資家・テスタさんが証券口座を乗っ取られたと投稿。一連の出来事をきっかけとして、大手2社を中心に、ネット証券のセキュリティ的な危うさに批判が集中している。
一連の騒ぎを受けてか、日本証券業協会も投資家への呼びかけを始めた。2日には、多要素認証の設定必須化を決定した証券会社69社の一覧を公表。「多要素認証の必須化は、投資家のセキュリティ対策として大変重要な措置となり、万が一、ログインID・パスワードを窃取されても、単要素認証と比べ被害を防止できる可能性が高まる」と注意喚起した。
SMBC日興証券、SBI証券、楽天証券は5月2日、フィッシング詐欺などによる証券口座への不正アクセスと不正取引(第三者による売買)の被害について、一定の補償を行う方針をそれぞれ発表した。日本証券業協会(日証協)が同日公表した、証券会社10社による申し合わせを受けた対応となる。
“不正取引”被害への補償、各社の対応は? SBI証券・楽天証券は「対象顧客には月内に案内開始」 - ITmedia NEWS
SMBC日興証券は同日、被害状況を精査した上で、個別の事情を踏まえて「被害補償の可否や内容を検討する」と発表。案内時期については明示していない。ID・パスワードの管理状況やセキュリティ設定などによっては「補償できない場合もある」として、ログイン時のワンタイムパスワードの設定といった対策を改めて呼び掛けている。
“不正取引”被害への補償、各社の対応は? SBI証券・楽天証券は「対象顧客には月内に案内開始」 - ITmedia NEWS
⇧ とりあえず、「全額補償」しないとか有り得ないんだが...
何と言うか、
仮想通貨取引所コインチェックで1月26日に発生した約580億円相当の仮想通貨流出事件を受け、SBIホールディングス代表の北尾吉孝社長が30日、運営元コインチェック社を「カス中のカス」と声を荒げて批判した。同日に開催した同社2018年3月期第3四半期決算説明会の中で。
北尾代表は、同件の問題点はウォレットに関して基本的なセキュリティー対策さえ講じていなかった「本当に初歩的な問題」であると指摘。同社がCMを出していたことにふれ「お金をかけなければいけないところ(セキュリティー)にお金をかけず客を集めるためにお金を使った。こういう輩はカス中のカス」と喝破した。
⇧ 過去の発言を見るに、グループ企業であろう「SBI証券」については、「全額補償」すべきな気がしますな...
syslogとは?
Wikipediaによると、
"syslog" という用語は、その通信プロトコルを指すだけでなく、syslog メッセージを送信するシステム(アプリケーションやライブラリ)syslog メッセージを受信し報告・分析するシステムに対しても使われる。
syslogの各メッセージには、そのメッセージを生成したシステムの種類を示すファシリティコードが付与され、重大度が設定される。
⇧ 用語の意味合いがファジーで辛い...
一応、ほとんどの「OS(Operation System)」で実装されているらしいが、実装に差異があるのかが分からない...
syslogの実装は、多くのオペレーティングシステムで行われている。
ネットワーク上で動作する場合、syslogはクライアントサーバモデルを採用している。受信側(サーバ)は一般に"syslogd"、"syslogデーモン"、"syslogサーバ" などと呼ばれる。クライアントは1024バイト以下の短いテキストメッセージをサーバに送信する。syslogメッセージはUDPまたはTCP上で送信される。
⇧ ネットワーク経由の送受信にも対応しているらしく、「クライアントサーバーモデル」になるらしい。
「syslog」の送受信については、「TCP/IP モデル(TCP/IP参照モデル)」で考えた場合、
⇧ 4層(レイヤー)が関係してくることになるのだが、
blog.smartbuildingsacademy.com
Working :
Syslog standard defines three layers i.e., the Syslog transport layer, Syslog application layer, and Syslog content layer.
- Syslog content layer –
It is the actual data contained in the event message. It contains some informational elements such as the facility codes and severity levels. - Syslog Application layer –
This layer generates, interprets, routes, and stores the message. - Syslog Transport layer –
This layer transmits the message over a network.
https://www.geeksforgeeks.org/what-is-syslog-server-and-its-working/
⇧ 主に、
- Application Layer
- Transport Layer
が「アプリケーション」側で意識する必要がありそうということみたい。
送信するデータのフォーマットについては、
メッセージ
RFC 3164では、メッセージ・コンポーネント(measge component、MSGと略称される)は、メッセージを生成したプログラムやプロセスの名前であるTAGと、メッセージの詳細を含むCONTENTの2つのフィールドで構成されると規定されている。
RFC 5424には、「MSGは、RFC 3164でCONTENTと呼ばれていたものである。TAGはヘッダの一部になったが、単一のフィールドではない。TAGはAPP-NAME、PROCID、MSGIDに分割されている。これはTAGの使い方と完全には似ていないが、ほとんどの場合同じ機能を提供する」と書かれている。rsyslogなどの一般的なシスログツールは、この新規格に準拠している。
⇧ RFCが存在するらしく、
の2つが定義されているらしいのだが、
制限
プロセス、アプリケーション、オペレーティングシステムはそれぞれ独立して記述されているため、ログメッセージの内容にはほとんど統一性がない。フォーマットや内容については、何の想定もされていない。
syslogメッセージはフォーマットされているが(RFC 5424にはABNF(拡張バッカス・ナウア記法)の定義がある)、そのMSGフィールドはフォーマットされていない。
syslogのプロトコルは片方向通信であり、受信側が受信できたことを発信側が確認する手段はない。
⇧ 実際には「フォーマット」が統一されていないらしく、「RFC」 の定義がほぼ意味ない...
ちなみに、「通信プロトコル」については、
ネットワーク上で動作する場合、syslogはクライアントサーバモデルを採用しており、サーバはクライアントからのプロトコル要求をwell-knownポートまたは予約済みポートで待ち受ける。歴史的には、ネットワークログの用途で使用される最も一般的なトランスポート層プロトコルはUDPであり、サーバの待受けポートは514である。
⇧ とあることから、「クライアント」と「サーバー」間で「OS(Operation System)」による差異は無いということかと。
とりあえず、「メッセージ」の「フォーマット」が統一されていないということで、「クライアント」側と「サーバー」側で、どんなデータを許容するかについて認識を合わせておかないと、「サーバー」側で受信した時に何某かの処理を行う時に対処しようが無い気がする...
RFC 3164とRFC 5424の送信項目を整理してみる
ネットの情報を漁っていたところ、
⇧ 上記サイト様によりますと、上記のような構造になっているらしく、
- syslogメッセージ
- Syslog header
- Message
という形になるそうな。
■Syslog header
■Message
⇧ といった感じらしいのだが、実際には、送受信はUDPになるようなので、
syslog のパケットフォーマットは 2 種類あります。
1 つがBSD 形式 (RFC3164)、もう 1 つはIETF 形式 (RFC5424) です。
【図解】syslogプロトコル再入門 ~フォーマット(BSD/IETF形式),Facility/Severity一覧,Ciscoの設定~ | SEの道標
その後 2012 年に RFC6587 により TCP による規格化も為されました。
【図解】syslogプロトコル再入門 ~フォーマット(BSD/IETF形式),Facility/Severity一覧,Ciscoの設定~ | SEの道標
■RFC 3164
■RFC 5424
⇧ といった感じで、「パケット」の「データ」部分に「syslog メッセージ」が格納される感じになりますと。
データ長はUDPヘッダの必須フィールドである。UDPヘッダが8バイト固定であるため最小値は 8 である。理論上の上限は65,535バイト(ヘッダ8バイト + データ65,527バイト)である。下位層がIPv4の場合、実際の上限は65,507バイト(IPヘッダ20バイトとUDPヘッダ8バイトを差し引いた値)となる。
IPv6のジャンボグラム機能では、65,535バイトを越えるサイズのUDPパケットを扱える。この場合、IPv6のオプションヘッダでサイズを指定し、最大4,294,967,295バイト(232 - 1)を指定できるので、ヘッダ部の8バイトを差し引くと最大4,294,967,287バイトのデータを扱える。
⇧「データ」部分の最大が、
No | Internet Protocol | データの最大値 | syslog メッセージ | データの最大値 |
---|---|---|---|---|
1 | IPv4 | 65,507バイト | RFC 3164 | 1024 byte未満 |
2 | RFC 5424 | 2048 byte未満 | ||
2 | IPv6 | 4,294,967,287バイト | RFC 3164 | 1024 byte未満 |
4 | RFC 5424 | 2048 byte未満 |
となるようなので、IPv4のケースで考えた場合、以下のようになるらしい。
■RFC 3164
構成 | user datagram | syslogメッセージ | ||||||
---|---|---|---|---|---|---|---|---|
UDP | byte | 構造(論理) | byte | 構造 | byte | 項目 | byte | |
UDP header | Source Port | 2 | - | - | - | - | - | - |
Destination Port | 2 | - | - | - | - | - | - | |
Length | 2 | - | - | - | - | - | - | |
Checksum | 2 | - | - | - | - | - | - | |
data | 65507 | syslog header | ※1 | PRI ※2 | - | - | ||
Header ※3 | - | TIMESTAMP ※4 | - | |||||
HOSTNAME | - | |||||||
Message | ※1 | MSG ※5 | - | TAG ※6 | - | |||
CONTENT ※7 | - |
※1 「syslog header」と「Message」合わせて、1024 byte 以下。
※2 以下のフォーマットのASCII文字列。詳細については、RFC 3164を参照。
< + Facility × 8 + Seventy + >
※3 以下のフォーマットのASCII文字列。詳細については、RFC 3164を参照。
TIMESTAMP + 半角スペース + HOSTNAME
※4 以下のフォーマットのASCII文字列。詳細については、RFC 3164を参照。
MMM DD HH:MM:SS
※5 以下のフォーマットのASCII文字列になることが多いらしい。詳細については、RFC 3164を参照。
[TAG]: CONTENT
※6 プロセス名やプログラム名。
※7 ログの内容。
定義がファジー過ぎて非常に困るのだが...
とりあえず、「RFC 3164」の「syslog」のフォーマットにおいては「TIMESTAMP」については、「タイムゾーン」を設定できないので、「syslog」の送信先のマシンの「タイムゾーン」と異なる場合が起こり得るので、送信先のマシン(「syslog」を受信する側)で何某かのチェック処理を行う必要がありますと。
まぁ、「RFC 3164」に準拠していなかったとしても、「UDP」の「data」として不正な形になっていないのであれば、普通に送受信できてしまう気がするので、チェック処理が重要そうな気はするのだが、言及されている情報が少ないんよね...
PythonでRFC 5424のsyslogのフォーマットを作成する
「Python」の公式のドキュメントの「サンプル」で、
⇧「RFC 5424」についての見本があったので、ChatGPT氏に「型」を付与してもらった。
「Python」の標準の「モジュール」である「logging」は、「RFC 3164」をデフォルトでサポートしていると考えて良いのかが分からないのだが...
■syslog部分のフォーマット作成
■■RFC 5424
■■D:\work-soft\python\auto_translate\src\syslog_rfc5424_formatter.py
import datetime import logging import logging.handlers import re import socket import time from typing import Any, Optional, Dict, Callable class SysLogHandler5424(logging.handlers.SysLogHandler): tz_offset: re.Pattern[str] = re.compile(r'([+-]\d{2})(\d{2})$') escaped: re.Pattern[str] = re.compile(r'([\]"\\])') def __init__( self, *args: Any, msgid: Optional[str] = None, appname: Optional[str] = None, **kwargs: Any ) -> None: self.msgid = msgid self.appname = appname super().__init__(*args, **kwargs) def format(self, record: logging.LogRecord) -> str: version: int = 1 asctime: str = datetime.datetime.fromtimestamp(record.created).isoformat() m: Optional[re.Match[str]] = self.tz_offset.match(time.strftime('%z')) has_offset: bool = False if m and time.timezone: hrs, mins = m.groups() if int(hrs) or int(mins): has_offset = True if not has_offset: asctime += 'Z' else: asctime += f'{hrs}:{mins}' try: hostname: str = socket.gethostname() except Exception: hostname = '-' appname: str = self.appname or '-' procid: int = record.process msgid: str = '-' msg: str = super().format(record) sdata: str = '-' if hasattr(record, 'structured_data'): sd: Dict[str, Dict[str, Any]] = record.structured_data parts: list[str] = [] def replacer(m: re.Match[str]) -> str: g = m.groups() return '\\' + g[0] for sdid, dv in sd.items(): part: str = f'[{sdid}' for k, v in dv.items(): s: str = str(v) s = self.escaped.sub(replacer, s) part += f' {k}="{s}"' part += ']' parts.append(part) sdata = ''.join(parts) return f'{version} {asctime} {hostname} {appname} {procid} {msgid} {sdata} {msg}'
■■RFC 3164
■■D:\work-soft\python\auto_translate\src\syslog_rfc3164_formatter.py
import logging import socket import time from typing import Optional class RFC3164Formatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: severity: int = getattr(record, 'severity', record.levelno % 8) facility: int = getattr(record, 'facility', 1) # default facility: user-level messages pri: int = facility * 8 + severity timestamp: str = time.strftime('%b %d %H:%M:%S', time.localtime(record.created)) hostname: str = socket.gethostname() tag: str = getattr(record, 'tag', record.name) pid: Optional[int] = getattr(record, 'process', None) pid_part: str = f"[{pid}]" if pid is not None else "" message: str = super().format(record) return f"<{pri}>{timestamp} {hostname} {tag}{pid_part}: {message}"
⇧ といった感じ。
■syslog部分のフォーマット確認
■■RFC 5424
■■D:\work-soft\python\auto_translate\src\create_rfc5424.py
import logging import time from syslog_rfc5424_formatter import SysLogHandler5424 # ハンドラを生成(送信先は仮でOK) handler = SysLogHandler5424(address=('localhost', 514), appname='myapp', msgid='TESTID') # structured_data を含める extra = { 'structured_data': { 'exampleSDID@32473': { 'eventID': '1011', 'eventSource': 'application', 'user': 'alice' } } } # ダミーの LogRecord を作成 record = logging.LogRecord( name="mylogger", level=logging.INFO, pathname=__file__, lineno=123, msg="This is a test log for RFC5424.", args=(), exc_info=None ) # structured_data を LogRecord に追加 record.structured_data = extra['structured_data'] record.process = 4321 record.created = time.time() # フォーマットだけ確認 formatted = handler.format(record) print("🧾 RFC5424 Formatted Log:\n", formatted)
■■RFC 3164
■■D:\work-soft\python\auto_translate\src\create_rfc3164.py
import logging from logging import Logger from syslog_rfc3164_formatter import RFC3164Formatter def setup_logger() -> Logger: logger: Logger = logging.getLogger("test_formatter") logger.setLevel(logging.DEBUG) stream_handler: logging.StreamHandler = logging.StreamHandler() stream_handler.setFormatter(RFC3164Formatter('%(message)s')) logger.addHandler(stream_handler) return logger def main() -> None: logger: Logger = setup_logger() for severity in range(8): # type: int for facility in range(8): # type: int extra: dict[str, int | str] = { 'severity': severity, 'facility': facility, 'tag': 'test_app' } logger.info( f"Test log: severity={severity}, facility={facility}", extra=extra ) if __name__ == "__main__": main()
⇧ といった感じで、
- syslog メッセージ
- Syslog header
- Message
はできたと。
Linux VM間でsyslog メッセージの送受信をPythonで実現するのに必要な情報を整理してみる
本当は、
■syslog送受信(Cisco Router → Linux VM)
Cisco Router → Linux VM
⇧ を検証したいのだが、「Cisco Router」は用意できないので、
■syslog送受信(Linux VM → Linux VM)
Linux VM → Linux VM
⇧ で検討してみることに。
送受信部分の処理についても、ChatGPT氏に質問したところ、
■送信側
# src/syslog_sender.py import socket def send_syslog_message(message: str, host: str = "127.0.0.1", port: int = 514, use_tcp: bool = False) -> None: """Syslog メッセージを送信(RFC3164 / RFC5424 対応)""" if use_tcp: with socket.create_connection((host, port)) as sock: sock.sendall(message.encode("utf-8")) else: with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.sendto(message.encode("utf-8"), (host, port))
■受信側
# src/syslog_receiver.py import socket from typing import Tuple def run_syslog_udp_receiver(host: str = "0.0.0.0", port: int = 514) -> None: """UDPでSyslogメッセージを受信""" with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.bind((host, port)) print(f"🟢 Syslog UDP server running on {host}:{port}\n") while True: data: bytes addr: Tuple[str, int] data, addr = sock.recvfrom(4096) print(f"📩 From {addr}: {data.decode('utf-8').strip()}")
⇧ 上記のような回答が返って来ましたと。
とりあえず、
⇧ ChatGPT氏には、 セキュリティという概念は無いらしい...
By the way、
UnixDatagramServer
は UDPServer
から派生していて、 UnixStreamServer
からではないことに注意してください --- IP と Unix サーバの唯一の違いはアドレスファミリーです。
⇧ 上記のページの内容によると、処理に時間のかからない「同期処理」を実現するケースにおいては、「TCP」や「UDP」を利用した通信で受信を担う「サーバー」用のクラスが「Python」の標準の「モジュール」で用意されているそうな。
「syslog」のIF(InterFace)仕様書と正確なサンプルが欲しいところですな...
そもそも、「syslog」の用語の定義がファジー過ぎる問題を何とかして欲しいのだが...
毎度モヤモヤ感が半端ない…
今回はこのへんで。