こんにちは!最近は、社内システムやWebアプリに生成AI(LLM)を組み込むことが当たり前になってきましたよね。「AIが質問にサクッと答えてくれる機能を作ったら、ユーザーにすごく喜ばれた!」なんて話もよく耳にします。
でも、ちょっと待ってください。
その便利なAI機能、「悪い奴ら」から狙われているかもしれないってご存知でしたか?
今回は、セキュリティ初心者や新人の開発者の方に向けて、LLM特有のいやらしい攻撃手法である「トークン制限を悪用したDoS(ドス)攻撃」と、その現実的な対策について、身近な例えを交えながら優しく紐解いていきたいと思います。
—
1. 家の鍵で例える「LLMへのDoS攻撃」の正体
セキュリティの基本を考えるとき、よく「家の鍵と防犯」に例えられます。今回のお話も、まさにこれにそっくりなんです。
想像してみてください。あなたが経営するお店の入り口に、「何でも無料で相談に乗る優秀なコンシェルジュ(=LLM)」を置いたとします。このコンシェルジュは、どんなに長くて複雑な質問をされても、一生懸命、一言一句すべて聞いて、丁寧に回答してくれます。
さて、ここに意地の悪い泥棒(攻撃者)がやってきました。
泥棒は、こんな嫌がらせを仕掛けます。
1. 無限に長い呪文のような質問をコンシェルジュに投げつける。
2. コンシェルジュは、その長大な文章をすべて理解しようと脳みそをフル回転させ、メモ用紙を何万枚も消費する。
3. その結果、コンシェルジュは疲れ果ててしまい、本当に相談したい一般のお客様の対応ができなくなってしまう。
4. おまけに、コンシェルジュを雇っているあなた(企業)の元には、莫大な「メモ用紙代(APIの利用料金)」の請求書が届く。
これが、LLMのトークン制限を悪用したDoS(サービス妨害)攻撃とコスト枯渇攻撃のメカニズムです。
コンシェルジュが一度に処理できる「言葉の限界(トークン制限)」のギリギリを狙って、システムをパンクさせたり、財布を空っぽにさせたりする手口なんですよね。
—
2. そもそも「トークン」ってなに?
セキュリティ対策に入る前に、AIが言葉を理解する単位である「トークン」について、ざっくりとおさらいしておきましょう。
AIは、私たち人間のように言葉をそのまま「意味」として理解しているわけではありません。文章を細切れのピース(これがトークンです)に分解して、計算しています。
日本語の場合、大体こんな感じで分割されます。
- 「こんにちは」 = 約2〜3トークン
- 「セキュリティ対策は重要です」 = 約10トークン前後
AIのモデル(例えばOpenAIのGPTシリーズなど)には、1回のリクエストで受け取れる「入力トークン」と「出力トークン」の最大上限(トークン制限)がしっかり決まっています。
攻撃者は、この上限ギリギリの「超特大のゴミデータ」を大量に送りつけることで、サーバーの計算リソースを食い潰し、正常なユーザーの邪魔をするわけです。
—
3. 一歩ずつ学ぶ!三つの現実的な防御アプローチ
「うわ、なんだか怖そう……どうやって守ればいいの?」と思いますよね。でも、安心してください。現場のエンジニアが実践している基本的な対策は、大きく分けて次の3つだけです。
一歩ずつ、確実に対策を学んでいきましょう!
① レート制限(Rate Limiting)で「荒らし」を防ぐ
まずは、一番基本的な防犯カメラのような仕組みです。
「1人の人間が、1分間に質問できるのはせいぜい5回まで!」というように、回数制限を設けます。同じ人(IPアドレスやログインユーザー)からの異常な連打をブロックするわけです。
② 入力トークン数制限で「長すぎる質問」を門前払いにしちゃう
コンシェルジュに渡す前に、質問の長さを測る番人を置きましょう。
「すいません、あなたの質問は長すぎます。もう少し短くまとめてくださいね」と、AIに処理を渡す手前で弾くのです。これだけで、サーバーの負荷もAPIの無駄な出費も一気に防げます。
③ コストと利用枠の管理(クォータ設定)
クラウドサービス(AWS, Azure, OpenAI APIなど)の管理画面で、月あたりの利用料金の上限(予算アラートやハードリミット)を必ず設定しておきましょう。万が一攻撃を受けても、被害が「全財産の没収」ではなく「数千円の被害」で食い止められます。
—
4. 実装コード例:Pythonでのトークンチェックとレート制限のイメージ
百聞は一見にしかず。実際のアプリケーション開発(例えばバックエンドのAPIサーバー)で、どのようにこれを防ぐのか、簡単なPythonのコード例を見てみましょう。
ここでは、文字数をベースにした簡易的なトークンチェックと、レート制限の考え方をコードに落とし込んでいます。
import time
from functools import wraps
from flask import Flask, jsonify, request
app = Flask(__name__)
# シンプルなインメモリのレート制限管理用ストレージ
# 本番環境では Redis などのKVSを使用することを強く推奨します
request_counts = {}
# 制限設定
MAX_TOKENS_ALLOWED = 1000 # 1回のリクエストで許容する最大トークン数(目安)
RATE_LIMIT_WINDOW = 60 # 制限をかける時間窓(秒)
MAX_REQUESTS = 10 # 制限時間内に許可する最大リクエスト数
def count_approximate_tokens(text):
"""
【簡易トークン計算機】
実際のプロダクトでは tiktoken などの公式ライブラリを使用しますが、
ここでは日本語の文字数や長さを簡易的にチェックする例として示します。
"""
# 雑な見積もりとして、文字数をそのままトークン数の代わりにするか、
# あるいは専用の計算ロジックをここに挟みます。
return len(text)
def rate_limit_and_token_check(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# 簡易的にIPアドレスをクライアント識別子とする
client_ip = request.remote_addr
current_time = time.time()
# 1. レート制限のチェック
if client_ip not in request_counts:
request_counts[client_ip] = []
# 過去ログから時間窓内のリクエストだけを抽出
request_counts[client_ip] = [
t for t in request_counts[client_ip] if current_time - t < RATE_LIMIT_WINDOW
]
if len(request_counts[client_ip]) >= MAX_REQUESTS:
return jsonify({
"error": "リクエストが多すぎます。しばらく時間を置いてから再度お試しください。"
}), 429 # HTTP Status 429 Too Many Requests
# リクエストボディからプロンプト(ユーザーからの質問)を取得
data = request.get_json() or {}
prompt = data.get("prompt", "")
# 2. トークン数(文字数)の制限チェック
estimated_tokens = count_approximate_tokens(prompt)
if estimated_tokens > MAX_TOKENS_ALLOWED:
return jsonify({
"error": f"プロンプトが長すぎます(現在: {estimated_tokens}トークン)。上限は {MAX_TOKENS_ALLOWED} トークンです。"
}), 400 # HTTP Status 400 Bad Request
# 問題なければリクエスト時間を記録して通過
request_counts[client_ip].append(current_time)
return f(*args, **kwargs)
return decorated_function
@app.route("/api/chat", methods=["POST"])
@rate_limit_and_token_check
def chat_with_ai():
"""
安全対策をクリアした正常なリクエストだけがここに到達します
"""
data = request.get_json()
prompt = data.get("prompt")
# ここでLLMのAPI(OpenAI等)を呼び出す処理を記述する
# response = openai_client.chat.completions.create(...)
return jsonify({
"status": "success",
"message": "AIからの回答をここに返します"
}), 200
if __name__ == "__main__":
app.run(debug=True)
コードのポイント解説
count_approximate_tokens関数: 悪意あるユーザーが送り込んでくる「長すぎる文章」をAIに渡す前に検知します。本番環境では、OpenAIが提供しているtiktokenなどの正確なトークナイザーライブラリを組み込みましょう。429 Too Many Requestsや400 Bad Request: セキュリティの鉄則として、エラーメッセージで「なぜ怒られたのか」を親切に伝えつつも、サーバーの内部構造を漏らさない適切なステータスコードを返します。- Redis等の活用: サンプルでは簡易的にメモリ上で管理していますが、実際のクラウドインフラでは
Redisなどの高速なインメモリデータベースを使って複数台のサーバー間でレート制限の情報を共有するのが実務の定番です。
—
5. まとめ:生成AIセキュリティは「入り口の門番」から始まる
生成AIは魔法の道具ではありません。裏側では巨大なサーバーが動き、一回一回のリクエストに莫大な計算コストがかかっています。
「動くものを作る」だけであれば、プロンプトをそのままAIに丸投げするコードを書くだけで完了してしまいます。しかし、セキュリティの視点を持つエンジニアは、その「動くもの」の裏側に潜むリスク(リソース枯渇や想定外のコスト請求)にしっかり目を向けなければなりません。
- レート制限で連打を防ぐ。
- トークン制限(入力文字数の制限)で過大なプロンプトを弾く。
- コストのクォータ設定で万が一の被害を最小限にする。
どれも今日から設計に取り入れられる現実的な対策です。
「安全で、誰にとっても快適なAIサービス」を一緒に作っていくために、まずは身近な防犯対策から一歩ずつ始めていきましょう!
コメント