【入門編】 AIシステムの可用性保護:DoS攻撃に対するリソース制限 – ガバナンス・リスク管理 & 生成AIセキュリティ防御ガイド

こんにちは!生成AIの活用がどんどん進む中、社内システムやWebサービスに最新のAI機能を組み込む開発が増えてきましたよね。「うちのサービスでもAIを使いたい!」というワクワク感の一方で、セキュリティ担当としては、ちょっと冷や汗が出るポイントでもあるんです。

特に、AI特有の「重たい処理」を狙ったサイバー攻撃については、これまでのWebサイトを守る方法とは少し頭を切り替える必要があります。

今回は、新人のIT担当者や、セキュリティに初めて触れる開発者の方に向けて、AIシステムの命綱である「可用性(サービスを止めないこと)」を守るためのリソース制限、中でも「DoS(サービス妨害)攻撃からGPUを守るキューイングとスロットリング」について、身近な例えを交えながら一歩ずつ学んでいきましょう!

—

1. 家の鍵だけでは防げない? AIを狙う「泥棒」の手口

まずは、私たちが普段使っているWebサイトと、生成AIが組み込まれたシステムの「違い」を考えてみますね。

普通のWebサイト(例えば、ブログ記事を表示するページなど)は、サーバーにとって「お茶を淹れるくらい簡単な作業」の連続です。だから、少しくらいアクセスが急増しても、サーバーはケロリとしています。

一方で、生成AIの「推論リクエスト」は、例えるなら「超難解な論文をその場で一から執筆してもらう」ようなものなんです。裏側では、高価でパワフルなGPU(画像処理半導体)がフル回転し、膨大な電力を消費しながら必死に言葉を紡ぎ出しています。

身近な例え:ラーメン屋の「一席占領おじさん」

ここで、街の人気ラーメン屋を想像してみてください。
お店には席(GPUの処理枠)が10席しかありません。通常なら、お客さんがサッと食べてサッと帰り、回転率よくみんなが美味しいラーメンを食べられますよね。

しかし、そこに悪意を持った「泥棒(攻撃者)」がやってきて、こんな嫌がらせをしたとします。

  • 「メニューに載っていない、めちゃくちゃ具だくさんで注文をつける特製ラーメンを、一晩中作り続けてくれ!」と、無限に注文票を出し続ける。
  • 仲間を何百人も動員して、一斉にその重たい注文を浴びせる。

結果はどうなるでしょうか?
お店の調理場(GPU)はパンクし、本当にラーメンを食べたい一般のお客さんは、外で何時間も待たされるか、お店に入れなくなってしまいますよね。これが、AIシステムにおける DoS攻撃(サービス妨害攻撃) の正体です。

—

2. 攻撃を防ぐための二大巨頭:「スロットリング」と「キューイング」

では、この悪意ある「重たい注文の嵐」からAIシステムを守るにはどうすればよいのでしょうか?
セキュリティの世界では、防犯の仕組みとして大きく2つのアプローチを用意します。それが 「スロットリング(制限)」 と 「キューイング(順番待ち)」 です。

スロットリング(来場制限・速度制限)

これは、ラーメン屋の入口に「お一人様1時間に1杯まで!」というルールを設けるようなものです。
短時間に何回もリクエストを送ってくる怪しい人や、あきらかに不自然な大量のアクセス(ボットなど)を検知したら、「ちょっとペースを落としてくださいね」と入り口でピシャリと遮断します。

キューイング(整理券システム・順番待ち)

スロットリングだけだと、アクセスが集中したときに正規のユーザーまで弾かれてしまいます。そこで登場するのが「整理券」です。
調理場(GPU)のキャパシティを超えるリクエストが来たら、すぐエラーにするのではなく、「順番待ちの列(キュー)」に並べてもらいます。「ただいま混み合っております。あなたの番号は42番です。順番が来るまで少々お待ちください」と優しく待機してもらうわけですね。これなら、時間は少しかかっても、システムがクラッシュするのを防げます。

—

3. 実践!コードで見るリソース制限の仕組み

「考え方は分かったけれど、具体的にどうやってコードを書けばいいの?」という声が聞こえてきそうですね。
ここからは、実際の開発現場で使える簡単な防御のアイデアを、Python(FastAPIなど)をベースにした疑似コードで見ていきましょう。

今回は、リクエストが来すぎたときに「順番待ちの列(キュー)」に入れ、さらに「1人あたりの速度制限(スロットリング)」をかけるイメージです。

import time
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
import asyncio

app = FastAPI()

# 【スロットリング用】簡易的なIPアドレスごとのアクセス記録(本番ではRedisなどの外部キャッシュを使います)
request_counts = {}

# 【キューイング用】同時に処理できるAI推論の最大数(GPUのメモリや性能に合わせて調整!)
MAX_CONCURRENT_REQUESTS = 5
semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)

# 1分間に許可する最大リクエスト数(スパム防止)
RATE_LIMIT_PER_MINUTE = 10

@app.post("/api/v1/generate")
async def generate_text(request: Request, prompt_data: dict):
    client_ip = request.client.host
    current_time = time.time()
    
    # 1. 【スロットリングのチェック】
    # クライアントごとのリクエスト履歴を管理し、短期間の連打をブロックする
    if client_ip not in request_counts:
        request_counts[client_ip] = []
    
    # 1分以上前の古い履歴を削除
    request_counts[client_ip] = [t for t in request_counts[client_ip] if current_time - t < 60]
    
    if len(request_counts[client_ip]) >= RATE_LIMIT_PER_MINUTE:
        # 制限回数を超えている場合は、HTTP 429 (Too Many Requests)を返す
        return JSONResponse(
            status_code=429,
            content={"error": "リクエストが多すぎます。1分後に再度お試しください。"}
        )
    
    # 今回のアクセス時刻を記録
    request_counts[client_ip].append(current_time)

    # 2. 【キューイングの適用】
    # 同時実行数が上限に達している場合、ここで順番待ち(セマフォによるブロック)が発生します
    async with semaphore:
        try:
            # ログ出力(現場での監視用)
            print(f"[{client_ip}] AI推論処理を開始します...")
            
            # ここに実際の重たいAI推論処理(LLMの呼び出しなど)が入ります
            # 例: response = await heavy_ai_model.generate(prompt_data["prompt"])
            await asyncio.sleep(2) # 重たい処理のシミュレーション
            
            return {"status": "success", "result": "AIからの生成テキストがここに入ります。"}
            
        except Exception as e:
            # 万が一、処理中にエラーが起きた場合のハンドリング
            raise HTTPException(status_code=500, detail="AIの処理中にエラーが発生しました。")

コードの解説と現場の知見

このコードでは、以下の2重の防壁を作っています。
1. RATE_LIMIT_PER_MINUTE で、悪意あるスクリプトが秒速で大量の負荷をかけるのを防ぎます(スロットリング)。
2. asyncio.Semaphore を使うことで、どれだけたくさんのリクエストが来ても、サーバーが同時に受け持つ作業を強制的に 5 件までに絞り込んでいます(キューイング)。

現場のインフラエンジニアとしては、このコードを直接アプリに書くだけでなく、手前のリバースプロキシ(Nginx や Cloudflare などのCDN)の段階でレートリミットを設定しておくのが、サーバーのCPU負荷すら逃がせる鉄則のテクニックになります。

—

4. セキュリティ担当からのメッセージ

AIシステムの可用性保護は、一朝一夕には完璧になりません。「どのくらいコストがかかる処理なのか」「正規のユーザーは1日に何回くらい使うものなのか」をビジネス部門やデータサイエンティストとしっかり話し合い、適切なバランスを見つけることが大切です。

「難しそうだな…」と感じたかもしれませんが、まずは小さな制限(スロットリング)をかけることから一歩ずつ始めてみましょう。あなたのその丁寧な実装が、会社の高価なGPUと、サービスを待っている大切なお客さんを守る盾になります。

それでは、次のセキュリティの冒険でお会いしましょう!安全で快適な開発ライフを!

コメント

タイトルとURLをコピーしました