SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

生成AI時代のセキュリティ入門

AIが生成したコードに潜む古典的セキュリティホールを解説!セキュリティ設計の基本を知ろう

生成AI時代のセキュリティ入門 第2回

AIが陥りやすい古典的な罠

A03:2021-インジェクション

 インジェクションは、信頼できないデータをコマンドやクエリの一部として送信することにより、意図しないコマンドを実行させてしまう攻撃を指します。AIは、Web上の無数の古いサンプルコードを学習しているため、この種の脆弱性を持つコードを生成する可能性を含んでいます。

 文字列連結によるSQLクエリ構築や、ユーザー入力をサニタイズせずにHTMLに描画するコード(XSS)を生成する傾向があります。これは、開発者がAIの出力を無条件に信頼してしまうことで、アプリケーションに脆弱性が持ち込まれる典型的なパターンです。

解説:SQLインジェクション

脆弱なコード例(Node.js/PostgreSQL)

 AIに「ユーザー名で検索するSQLクエリを」と依頼すると、次のような文字列連結を用いたコードが生成される可能性があります。

import { Client } from 'pg';
const client = new Client();
await client.connect();

async function findUsersByName(name: string) {
  // 警告:文字列連結はSQLインジェクションに対して非常に脆弱
  const query = `SELECT * FROM users WHERE name = '${name}'`;
  const res = await client.query(query);
  return res.rows;
}

 このコードでは、nameに 'OR'1'='1といった文字列を入力すると、全てのユーザー情報が漏洩してしまいます。

安全な実装例(ORMの利用)

 ORM(Object-Relational Mapper)を利用すると、開発者は生のSQLクエリを直接記述する必要がなくなり、SQLインジェクションのリスクを大幅に低減できます。

 PrismaのようなORMライブラリは、メソッドチェーンを通じてクエリを構築し、内部で安全にパラメータ化を実行します。

import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

async function findUsersByNameSafe(name: string) {
  // Prismaのクエリビルダーが自動的にSQLインジェクション対策を行う
  const users = await prisma.user.findMany({
    where: {
      name: name,
    },
  });
  return users;
}

解説:クロスサイトスクリプティング(XSS)

脆弱なコード例(React)

 「ReactでHTML文字列を描画したい」とAIに尋ねると、サニタイズしていないコードが提示される可能性があります。

import React from 'react';

interface UserProfileProps {
  bio: string; // ユーザーが入力したHTMLを含む自己紹介
}

const UserProfile: React.FC<UserProfileProps> = ({ bio }) => {
  return (
    <div>
      <h2>User Bio</h2>
      <div dangerouslySetInnerHTML={{ __html: bio }} />
    </div>
  );
};

 bio に <img src=x onerror="alert('XSS!')"> のようなスクリプトを埋め込まれると、それが実行されてしまいます。

安全な実装例

 最も安全で根本的な解決策は、ユーザーの入力をHTMLとして解釈せず、単なるテキストとして扱うことです。Reactでは、JSXの波括弧 {} 内に文字列を直接記述するだけで、自動的にエスケープ処理が行われ、XSS攻撃を防止できます。

 dangerouslySetInnerHTMLの使用は可能な限り避けるべきです。基本的にdangerouslySetInnerHTMLの使用はリンター(静的解析ツール)によって、警告が出ます。

 しかし開発スピードを優先するあまり、この警告を無視(または見落とし)してしまうケースもあり、結果として脆弱性が生まれてしまいます。

import React from 'react';

interface UserProfileProps {
  bio: string; // ユーザーが入力した自己紹介
}

const UserProfile: React.FC<UserProfileProps> = ({ bio }) => {
  return (
    <div>
      <h2>User Bio</h2>
      {/* Reactが自動的にテキストをエスケープするため安全 */}
      <p>{bio}</p>
    </div>
  );
};

 アプリケーションの要件として、ユーザーにリッチテキスト(太字、リストなど)の入力を許可する必要がある場合は、HTMLを描画する際にDOMPurifyなどのライブラリでサニタイズ処理を検討することを推奨します。

 さらに最近はアプリケーション内でLLMによって生成された出力をシステムで使う場合、その出力がXSSなどの脆弱性になる可能性があります。

 例えば、LLMによってJavaScriptまたはMarkdownが生成され、ユーザーに返されます。その後、ブラウザによってコードが解釈され、XSSやCSRFが発生するケースが考えられます。

 これはWebブラウザだけでなく、バックエンドシステムでSSRF、権限昇格、またはリモートコード実行が発生する可能性があります。

 この問題はOWASP Top 10 for LLM Applicationsの「LLM05:不適切な出力処理」で指摘されています。

環境がもたらす危険な近道

A05:2021-セキュリティ設定の不備

 これは、サーバーやクラウド環境などのバックエンドの構成(コンフィグレーション)ミスに起因する脆弱性を指します。開発を急ぐあまり、安全でないデフォルト設定や、エラー解決のための場当たり的な設定が本番環境に残ってしまうケースが多く見られます。

 「A01:2021-アクセス制御の不備」がアプリケーションの認可ロジックの問題であるのに対し、「A05:2021-セキュリティ設定の不備」はシステムがどのように構成されているかという環境の問題です。

 AIは、開発者が直面している特定のエラー(CORSエラーなど)を解決することに長けていますが、その場しのぎの解決策が持つセキュリティ上の副作用までは考慮できない場合があります。そのため、過剰にアクセスを許可するサーバー設定や、デバッグに便利な詳細エラーメッセージを本番環境でも表示するような危険な設定を生成してしまっているケースがあります。

解説:CORSのワイルドカード指定

 Access-Control-Allow-Origin: *は、「どのウェブサイトからでもこのAPIの応答を読み取れる」ことを許可する危険な設定です。認証情報(Cookieなど)と共にこの設定が使われると、悪意のあるサイトがユーザーになりすましてAPIを呼び出し、個人情報を窃取する可能性があります。

対策

 信頼できる特定のオリジン(フロントエンドアプリケーションのドメインなど)をホワイトリスト形式で明示的に指定し、アクセス元を厳格に制御する必要があります。

解説:ディレクトリリスティングの無効化忘れ

  Webサーバーの設定によっては、特定のURLパスにindexファイル(index.htmlなど)が存在しない場合、そのディレクトリに含まれるファイルやフォルダの一覧を自動的に表示してしまうことがあります。また利用しているBaaS(上記で例に挙げたFirebase Storageなど)に関しても、ディレクトリリスティングが無効になっていないケースが見受けられます。

対策

 WebサーバーやBaaSなどクラウドサービスの設定でディレクトリリスティングを明示的に無効化します。

解説:CDNによる意図しない個人情報のキャッシュ

 Webサイトの表示速度を向上させるCDN(コンテンツデリバリネットワーク)の設定ミスにより、ユーザー固有の個人情報を含むべき動的なページがキャッシュされてしまうことがあります。その結果、全く別のユーザーが同じURLにアクセスした際に、キャッシュされていた他人の個人情報(例:マイページ、注文履歴など)が閲覧できてしまうという深刻な情報漏洩に繋がります。

対策

 個人情報を含むページやAPIのレスポンスには、CDNをバイパスするようにCDN側で設定したり、Webサーバー側からキャッシュを明確に禁止するHTTPヘッダーを正しく設定する必要があります。具体的には、Cache-Control: private, no-cache, no-store, must-revalidatePragma: no-cacheといったヘッダーを付与し、CDNやブラウザにコンテンツをキャッシュさせないよう指示することが不可欠となります。

次のページ
加速する開発が見落とすセキュリティの土台

この記事は参考になりましたか?

生成AI時代のセキュリティ入門連載記事一覧
この記事の著者

Kyohei Fukuda(キョウヘイ フクダ)

 広島県出身、東京在住のWebエンジニア。 国内IT企業数社でフロントエンド開発に従事した後、現在は外資IT企業でSolution Engineer兼Developer Advocateを務めている。OSSのPDF生成ライブラリ「pdfme」を開発し、関連する書類作成サービスを個人開発し運営。 JavaSc...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

CodeZine編集部(コードジンヘンシュウブ)

CodeZineは、株式会社翔泳社が運営するソフトウェア開発者向けのWebメディアです。「デベロッパーの成長と課題解決に貢献するメディア」をコンセプトに、現場で役立つ最新情報を日々お届けします。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/22465 2025/11/17 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング