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-revalidateやPragma: no-cacheといったヘッダーを付与し、CDNやブラウザにコンテンツをキャッシュさせないよう指示することが不可欠となります。
