⏱️ 暗号学アルゴリズム解説 ✓ 2026年最新検証済み

TOTPとHOTPの違いを徹底解説:仕組みとセキュリティ比較

2段階認証(2FA)の基盤技術であるワンタイムパスワード。2005年のRFC 4226(HOTP)から2011年のRFC 6238(TOTP)へ、なぜ世界中の認証システムはシフトしたのか?HMAC-SHA1ハッシュから動的切り捨て(Dynamic Truncation)によって6桁の数字を抽出する数学的アルゴリズム、ステートレス設計の利点、および時刻ズレと非同期への対処を分かりやすく技術的に深掘りします。

2FA
2FA-Tool セキュリティ技術チーム
2026年10月更新
読了目安:約10分(約4,800文字)
RFC 6238 RFC 4226 HMAC-SHA1
💡 暗号技術の重要ポイント(TL;DR)
  • 入力変数の本質的な違い: HOTPは単調増加する整数カウンター C を入力とし、TOTPはCをエポック秒に基づく時間ステップ T(Unix秒 / 30) に置き換えます。
  • 完全な継承関係: 数学的にTOTPはHOTPの直接の派生形です:TOTP(K, Time) = HOTP(K, T)。両アルゴリズムは全く同一の動的切り捨て(Dynamic Truncation)関数を共有しています。
  • アーキテクチャの決定打: HOTPはステートフル(認証ごとにサーバーが最新カウンターをDBに排他書き込み保存する必要がある)であるのに対し、TOTPはステートレス(時刻同期のみでDB書き込み不要)であるため、現代の大規模分散クラウド基盤に最適です。
01

歴史的背景:専用ハードウェアトークンからオープンWeb標準へ

2000年代初頭、企業の2要素認証はRSA SecurIDに代表される専用ハードウェアキーフォブが独占していました。これらの独自トークンは高価であり、内部の水晶発振器の精度管理や専用認証サーバーの導入が必要だったため、中小企業や一般コンシューマーへの普及は極めて困難でした。

このベンダーロックインを打破し、世界中のエンジニアが自由に実装・監査できるオープンな標準を策定するため、業界団体OATH(Initiative for Open Authentication)が結成され、IETFにおいて2つの歴史的RFC標準が誕生しました:

📜 2005年:RFC 4226(HOTP)

HMAC-Based One-Time Password Algorithm。物理ボタンを備えた低電力マイクロコントローラー向けに策定。ボタンを押すたびに内部カウンターが増加し、LCD画面に6桁のコードが表示されます。

📜 2011年:RFC 6238(TOTP)

Time-Based One-Time Password Algorithm。スマートフォンの爆発的普及に伴い登場。携帯電話網のNTP時刻同期を活用することで、ボタン操作やカウンターズレの課題を根底から解消しました。

02

HOTP(RFC 4226)と動的切り捨てのバイト計算メカニズム

HOTPの数学的定義は極めてシンプルです:

HOTP(K, C) = Truncate(HMAC-SHA-1(K, C)) mod 10DigitCount

構成パラメーターの解説:

  • K: 共有秘密鍵(Base32デコードされた160ビット / 20バイトのバイナリ列)。
  • C: 8バイト(64ビット)ビッグエンディアン整数のカウンター。
  • HMAC-SHA-1: 秘密鍵付きハッシュ関数。20バイト(160ビット)のダイジェストを出力。

🔍 動的切り捨て(Dynamic Truncation)が20バイトから6桁を抽出する手順:

HMAC-SHA1ハッシュ値(20バイト)から、暗号学的に偏りのない6桁の数字を決定論的に抽出するため、RFC 4226第5.3節で定義されたアルゴリズムを実行します:

// ステップ 1: 動的オフセットの決定
Offset = HS[19] & 0x0F
20バイトのダイジェストの末尾(インデックス19)のバイトを取り出し、下位4ビット(0x0F)をマスクします。これにより、必ず0から15(0x0〜0xF)の整数が得られます。最大値15に4バイトを足しても19となるため、配列外参照が数学的に防止されます。
// ステップ 2: 4バイトの連続抽出とMSBマスク
P = ( (HS[Offset] & 0x7F) << 24 ) | ( (HS[Offset+1] & 0xFF) << 16 ) | ( (HS[Offset+2] & 0xFF) << 8 ) | ( HS[Offset+3] & 0xFF )
Offsetの位置から連続する4バイトをビッグエンディアン整数として結合します。最上位ビット(MSB)を0x7Fでマスクすることで符号付き整数のオーバーフローを防ぎ、31ビットの正の整数Pを生成します。
// ステップ 3: 剰余算による6桁コードの確定
OTP = P mod 106 = P mod 1,000,000
1,000,000で割った余りを求め、6桁に満たない場合は左側をゼロ埋め(ゼロパディング)します(例:748 ➔ "000748")。
🧪 RFC 4226 Appendix D 公式テストベクターの計算例:

秘密鍵をASCII文字列 "12345678901234567890"、カウンター C = 0 とした場合のHMAC-SHA1ダイジェスト(16進数):

cc 93 cf 18 50 8d 94 93 4c 64 b6 6d 8b fb 87 fb 23 cb 4b 71

1. 末尾バイトは 0x71 です。0x71 & 0x0F = 0x01 となり、Offset = 1 です。
2. インデックス1からの4バイトは 93 cf 18 50 です。MSBをクリアすると 0x13cf1850、10進数で 332,339,280 です。
3. 332339280 mod 1000000 = 339280。最終的な6桁ワンタイムパスワードは 339280 となります。

03

TOTP(RFC 6238)の進化:時刻ステップの魔法

HOTPでは、ユーザーが端末のボタンを誤って複数回押したり、ログインを途中で中断したりすると、端末側とサーバー側でカウンターCがズレて認証不能になる問題がありました。

RFC 6238は、この動的にズレるカウンターCを、Unixエポック時刻に基づく確定的な時間ステップ T に置き換えました:

T = ⌊ (CurrentUnixTime - T0) / X ⌋
  • CurrentUnixTime: 1970年1月1日00:00:00 UTCからの経過秒数。
  • T0: 開始エポック秒(現代の実装ではすべて0)。
  • X(タイムステップ間隔): 標準では30秒。
  • 床関数 ⌊ ⌋(切り捨て): 30秒間の枠内ではTの値は全く変動しません。例えばエポック秒 1728211200 から 1728211229 までの間、Tは同一の64ビット整数を保ちます。

バイナリ変換とHOTPへの引き渡し: 算出された整数Tは、HOTPのカウンターCと全く同じ8バイトのビッグエンディアン形式にパックされ、HOTPアルゴリズムに入力されます:
TOTP(K, Time) = HOTP(K, T) この計算の美しさにより、認証アプリはネットワーク通信を一切行わず、時計の進みだけを頼りに30秒ごとにコードを同期更新できるのです。

SHA-256およびSHA-512への対応: RFC 4226はHMAC-SHA-1のみを規定していましたが、RFC 6238ではHMAC-SHA-256やHMAC-SHA-512も規格化されました。SHA-256(32バイト)ではオフセットをバイト31から、SHA-512(64バイト)ではバイト63から取得します。ただし現在でも、互換性重視のためGoogle Authenticatorをはじめとする99%のサービスがSHA-1を採用しています。
04

徹底比較マトリックス(HOTP vs TOTP)

比較軸 HOTP(RFC 4226) TOTP(RFC 6238)
入力変数 イベントカウンター C(単調増加) 時間ステップ T(30秒刻みのUnixエポック)
コードの有効期間 無期限(次の認証で消費されるまで有効) 厳格に30秒間(自動失効)
サーバー構造 ステートフル(認証ごとにDBのカウンター更新が必須) ステートレス(NTP同期だけで独立検証可能)
リプレイ攻撃耐性 中程度(盗聴されたコードは使用されるまで有効) 極めて高い(30秒経過で自動的に無効化)
主な障害要因 カウンターの非同期ズレ(空押し) 端末の時刻ズレ(クロックドリフト)
対応ハッシュ関数 HMAC-SHA-1 のみ SHA-1、SHA-256、SHA-512
実社会での採用状況 旧式銀行トークン、専用電子錠 現代Webサービスの99.9%(デファクトスタンダード)
05

カウンター非同期ズレ vs 時刻ズレの技術的対策

実際のシステム運用において、クライアントとサーバーのズレは不可避です。各アルゴリズムはエンジニアリングによって信頼性を担保しています:

⚙️ HOTPの対策:ルックアヘッド探索ウィンドウ(s)

ユーザーがログインせずにトークンのボタンを誤って5回押した場合、端末は C+5 に進みますがサーバーは C のままです。
RFC 4226では、サーバーが探索枠 [C+1, C+s](通常 s=20)の範囲で総当たり計算を行い、一致した時点でサーバーのカウンターをジャンプさせて再同期します。

⏱️ TOTPの対策:時刻ズレ許容ウィンドウ(±1枠)

ネットワーク遅延やスマホの内蔵時計のズレに対応するため、本番サーバーは [T-1, T, T+1] の3つのウィンドウ(合計90秒間)のいずれかに一致すれば正当とみなします。

リプレイ攻撃防止の必須ルール: 90秒の許容枠があるため、一度使用されたコードが再利用されるのを防ぐ目的で、サーバーは消費済みトークンをメモリ(Redis等)に90秒間キャッシュし、同一コードの連続送信を弾く必要があります。
🛡️ 定数時間文字列比較(タイミング攻撃対策):

サーバー側でユーザー入力コードと計算結果を比較する際、通常の文字列比較演算子(===)を用いると、先頭文字が不一致になった瞬間に処理が打ち切られます。攻撃者はナノ秒単位の応答時間の差を計測することで1文字ずつ正解コードを特定できてしまいます。セキュアなシステムでは必ず定数時間比較(例:Node.jsの crypto.timingSafeEqual)を実装してサイドチャネル攻撃を防御します。

06

現代のインターネットでTOTPが勝利した理由

HOTPが衰退し、Web全体でTOTPが圧倒的な標準となった背景には、3つの決定的な理由があります:

☁️ クラウドのスケーラビリティ

世界中に分散したWebサーバー群において、ステートレスなTOTPはDBアクセスなしにNTP時計だけで検証可能。高トラフィック時もDBのロック競合を起こしません。

📱 スマホの高精度自動時刻同期

基地局電波やGPSによりスマホの時計はミリ秒単位で常時自動補正されるため、専用ハードウェアトークンが不要になりました。

🛡️ 30秒という狭い攻撃受付時間

盗聴されても30秒で無効化されるため、ログ漏洩や通信パケット傍受からの安全性が劇的に向上しました。

🧮 純粋なクライアントサイド RFC 6238 エンジン

ブラウザ上でTOTP動的計算をリアルタイム体験

Base32シークレット文字列がどのように30秒刻みで6桁コードへ変換されるかを直接確認できます。W3C Web Crypto APIにより完全オフラインで動作します。

オンライン生成ツールを開く