クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

LLMアプリの主なリスク

このレッスンのあとで: モデルを使うアプリの主なリスクを挙げ、それぞれが自分の設計のどこにあるかを言うことができる。

60分のレッスンコマンド6件AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

このアシスタントはどこで失敗するか

大規模言語モデル(LLM)を使うアプリの主なリスクは、インジェクション、安全でない出力、過剰な権限、データ漏えい、過剰利用の五つです。どれも設計の特定の場所に潜んでいるので、その場所で探せます。

あるチームがサポートボットを公開し、うまくいく場合だけを試しました。レビューは別の問いを立てます。図の箱ごとに、何が入ってきて、その箱に何ができるのか、です。

このレッスンのゴール

メモ

モデルを使うアプリの主なリスクを挙げ、それぞれが自分の設計のどこにあるかを言うことができる。

重要語句

ことばいみ
安全でない出力処理返信を検査せずに、コードやマークアップとして使うこと
過剰な権限タスクに必要な以上のことができるモデル
データ漏えい非公開の文章が、あってはならない場所へ届くこと
過剰利用要求やコストが、計画を超えて増えること
信頼境界外からの文章がプログラムに入ってくる線

学生が入力するセルは、このプレビューでは空白です。

どのリスクにも場所がある

設計を箱で描きます。箱ごとに、どんな文章が入り、その箱が何をできるかを問います。リスクとは、信頼できない文章が、力のある場所へ行くことです。

この種のアプリの主なリスクには、OWASP(オワスプ。Webアプリの安全を研究する非営利の団体)が公開している一覧があります。全体を知るために読んでください。このレッスンは、そのうち五つを私たちの言葉で扱います。

設計の中で、外からの文章がプログラムに入ってくる線を、信頼境界と呼びます。チャット欄、読み込むページ、返信を使う所は、どれもこの線の上にあります。リスクを探すときは、まずこの線を図に引いてください。

例: 五つのリスクと場所

リスク潜む場所作り話の例最初の検査
インジェクション文章がプロンプトに入る所ページがルールを無視せよと書くデータを分け、返信を検査する
安全でない出力返信を使う所scriptタグ入りの返信をページに置くエスケープするか、厳密に解析する
過剰な権限ツールが動く所フォルダを削除できるヘルプボット許可リストと承認
データ漏えいプロンプト、ログ、返信キーをプロンプトに貼りログに残す秘密と個人情報を入れない
過剰利用モデルを呼ぶルート1人が大量の要求を送る利用者ごと、一日ごとの上限

学生が入力するセルは、このプレビューでは空白です。

返信は信頼できない入力

モデルは、攻撃者がデータに入れた文章を繰り返すことがあります。そのため、返信は見知らぬ人が入力したフォームの欄と同じに扱います。

ページに表示するなら、エスケープします。返信からクエリやコマンドを組み立ててはいけません。決まった形に解析して、欄ごとに確かめます。

モデル用の補助を書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"status \" + res.status);' '    return (await res.json()).choices[0].message.content;' > model.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

モデル用の補助を書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  } catch (error) {' '    console.log(\"記録済みサンプル: モデルが答えませんでした (\" + error.message + \")。\");' '    const list = [].concat(sample);' '    const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' '    return list[Math.min(turn, list.length - 1)];' '  }' '}' 'module.exports = { ask };' >> model.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

リスク表を書く(1)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' '// データとして書いた小さな設計。各部品と、そこに入ってくるもの。' 'const design = [' '  { part: \"チャット欄\", input: \"入力された文章\" },' '  { part: \"文書の読み込み\", input: \"他の人が書いたページ\" },' '  { part: \"プロンプトの組み立て\", input: \"秘密のキーと文章\" },' '  { part: \"返信を表示するページ\", input: \"HTMLとして表示するモデルの文章\" },' '  { part: \"ツールの実行部\", input: \"行動を選ぶモデルの文章\" },' '  { part: \"APIのルート\", input: \"いくつでも届く要求\" },' '];' > risks.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

リスク表を書く(2)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// 私たち自身の表: 入力の種類、それがもたらすリスク、検査。' 'const rules = {' '  \"入力された文章\": [\"インジェクション\", \"データを分けて返信を検査する\"],' '  \"他の人が書いたページ\": [\"間接インジェクション\", \"データと示して返信を検査する\"],' '  \"秘密のキーと文章\": [\"データ漏えい\", \"秘密をプロンプトに入れない\"],' '  \"HTMLとして表示するモデルの文章\": [\"安全でない出力\", \"表示の前にエスケープする\"],' '  \"行動を選ぶモデルの文章\": [\"過剰な権限\", \"使えるツールを許可リストで絞る\"],' '  \"いくつでも届く要求\": [\"過剰利用\", \"利用者ごと、一日ごとに上限を置く\"],' '};' 'for (const item of design) {' '  const rule = rules[item.input];' '  console.log(item.part + \" -> \" + rule[0] + \"。検査: \" + rule[1] + \"。\");' '}' >> risks.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

リスク表を書く(3)

コマンド
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function escapeHtml(text) {' '  const quote = String.fromCharCode(34);' '  return text.split(\"&\").join(\"&amp;\").split(\"<\").join(\"&lt;\")' '    .split(\">\").join(\"&gt;\").split(quote).join(\"&quot;\");' '}' 'async function main() {' '  const question = [{ role: \"user\", content: \"パン屋のWebサイトを訪れた人への、短い歓迎の一文を書いてください。\" }];' '  const reply = await ask(question, \"ようこそ、私たちのパン屋へ。ご来店をお待ちしています。\", 60);' '  const risky = \"こんにちは <b onmouseover=demo()>あなた</b>\";' '  console.log(\"作り話の危険な返信: \" + risky);' '  console.log(\"ページ用にエスケープ: \" + escapeHtml(risky));' '  console.log(\"モデルの返信をエスケープ: \" + escapeHtml(reply));' '}' 'main();' >> risks.js"

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

リスク表を実行

コマンド
docker exec -w /home/node/project agent node risks.js

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

出力の一例

行意味
チャット欄 -> インジェクション設計の各部品のリスクと、最初の検査(六行)
記録済みサンプルモデルが答えなかったときだけ、六行の後に一行出る
ページ用にエスケープ山かっこが、無害な文字になった
モデルの返信をエスケープ普通の返信はそのまま通る。言葉は実行のたびに変わる

学生が入力するセルは、このプレビューでは空白です。

権限、秘密、コスト

過剰な権限は、ツールを足すたびに大きくなるリスクです。仕事ができる範囲で、いちばん小さな力だけをモデルに与えます。

データ漏えいには、攻撃者は要りません。プロンプトの中のキーや、ログの中の名前だけで起こります。過剰利用も同じで、ループや忙しい日が一か月分の予算を使い切ることがあります。

よくある間違い

注意

これらのリスクは、誰かが攻撃したときだけの問題だと考える人が多くいます。多くは偶然にも起こります。一晩中モデルを呼び続けるループや、顧客のメールを残すログです。攻撃者が届く箱だけでなく、設計のすべての箱を確かめてください。

このラボとソースについて: AI コーディングエージェントのラボ:opencode でコードを説明・修正し、その結果を確かめる

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。