動いたコードが、安全とは限らない。AIの「正解」をどう見抜く?
テストは緑。画面も動く。それでも、名前の一文字や利用者の違いで壊れることがあります。確かめる条件を一つ増やすと、「動いた」の先を調べられます。

この記事の目次
「動いた」の中に、何を確かめたかが隠れている
名前を入れると会員情報が出る。自分の名前では問題なく動いたので、AIが作った検索処理をそのまま使いたくなった。でも、名前にアポストロフィが一つ入ったら止まるとしたら? 入力例が一つ増えるだけで、「完成」の意味は変わってきます。
機能のテストは、思った入力で望む結果が出るかを調べます。安全性を調べるときは、想定外の入力や、見せてはいけない相手、触らせてはいけない範囲にも目を向ける。同じ「動くコード」の話でも、問いが違うんですね。
研究が調べたのは、文章の上手さよりコードの中身
Pearce氏らの研究は、当時のCopilotへ安全性に関わる場面を与え、危険なコードが提案され得ることを調べました。生成されたコードが整っていることと、安全な処理であることが、別の問題だと分かる研究です。[1]
さらにPerry氏らは、47人にPython・JavaScript・Cを使う5種類の課題へ取り組んでもらいました。AIを利用できる条件では脆弱性が増える場面があり、それでも利用者は安全だと思いやすかったと報告しています。見た目の納得と、実際に確かめた結果がずれる点は、気になりますよね。[2]
ただし「AIを使うほど必ず危険」という一本道にもなりません。Sandoval氏らの学生58人の研究は、Cで買い物リストの連結構造を作る設定で、安全性への影響は小さいと報告しました。言語、課題、利用者、支援の受け方が違えば、観察される結果も変わる。ここは両方を残して読みたいところです。[3]
悪意がなくても、名前の一文字で境界が見える
最初の名前検索を、もう少し近くで見てみます。SQLというデータベースへの依頼文を、利用者が入力した名前と文字列でつなぐと、名前の中の記号まで依頼文の一部として読まれることがあります。たとえば架空の名前 O'Reilly の引用符が、文字列の区切りとぶつかる。これは、入力データと命令の境界が混ざる一例です。
対処は「変な名前は禁止」に寄せるより、値を別の引数で受け取る、DBの呼び出し機能を使うこと。下の資料では、SQLの形を固定し、名前を別の引数に分けています。同じ考え方でも、Python、Java、JavaScriptや使うDBで書式が違うので、製品の正しい呼び出し方を確認します。
問い合わせ窓口に置き換えると、「この名前の人を探して」という仕事の型と、「探す名前」という記入欄を分ける感覚です。名前にどんな記号があっても、窓口の仕事そのものは変えない。その役割を、パラメーターを受け取るAPIに担ってもらいます。人が毎回、入力中の記号を数える方法より、境界を保つ場所が明確になるでしょう。
それでも、入力を分けた後に全会員の情報を返していたら、別の問題が残ります。自分の画面で検索できたことを喜ぶのと同時に、別の利用者から同じ結果が見えてよいかを考える。この問いの切り替えが、安全性の確認を、単なる「変な入力を弾く作業」から広げます。
Asare氏らは、人間が脆弱性を入れた場面を起点としてCopilotの提案を比較しました。ここから持ち帰れるのは、人間のコードも比較の対象になるということです。AIか人間かのラベルだけで通すより、今の入力と処理がどこで混ざるかを具体的に読む方が、直す場所へ近づけるでしょう。[4]
さらに検索結果を返す前には、「この利用者へ、その会員情報を見せてよいか」という確認も要ります。入力を安全に渡せたからといって、閲覧の権限まで決まるわけではない。入力、操作できる範囲、返す情報。この三つを別々の問いにすると、確認を忘れにくくなります。
つまり、出力されたコードに「安全にして」と追記するだけで終わらず、安全という言葉を、試せる条件へほどく。自分の名前、記号のある名前、存在しない名前、別の利用者の情報。何を返すべきかを先に書けば、AIにも同じ検査条件を渡せます。
入力した名前を、命令と別の場所へ渡す
SELECT name FROM people WHERE name = 'O'Reilly'
↑
名前の一文字が、区切りに混ざる- 1SQLの前半
引用符までを文字列で作る
- 2名前を追加
入力にも引用符がある
- 3混ざる境界
値の一文字がSQLの構文に入る
まず練習用データで、一つだけ境界を増やす
手元でPythonを動かせるなら、外へ通信しない一時的なSQLiteデータベースで試せます。下の例は、記号を含む架空の名前を登録して検索するだけ。既存のDBへ接続せず、実名も秘密も使いません。期待する結果は、その一件だけが返ることです。
コードをAIへ見てもらうなら、「この処理は、どの入力とどの権限を試していないか」と問いを絞るのも一案です。出てきたテストは自分でも読んで、値の受け渡しと、結果を見る相手を確認する。AIの答えを、もう一度AIが褒めたことを検証結果にしないのがコツでしょう。
import sqlite3
con = sqlite3.connect(':memory:')
con.execute('CREATE TABLE demo(name TEXT)')
name = "O'Reilly"
con.execute('INSERT INTO demo VALUES (?)', (name,))
rows = con.execute('SELECT name FROM demo WHERE name = ?', (name,)).fetchall()
print(rows) # 架空の名前の一件だけが返る
con.close() 採用する前に、説明できる三行を残そう
確認の最後に、「外から来る値は何か」「誰が何をできるか」「失敗したらどう止めるか」を三行にしてみます。途中の一行が書けなければ、そこが次に調べる場所です。分からないコードを丸ごと採用するより、小さく戻れる形で受け取れます。
研究の結果が違っても、この確認は無駄になりません。昔のモデルの数字を今のコードへ貼る代わりに、今の自分が守りたい条件をテストにする。動いたことを喜びながら、もう一つの入力でも確かめられたら、「使えそう」が少し具体的になるはずです。
研究の補足と、よくある疑問
このSQLiteの例だけで安全性を確認できる?
ここで確かめるのはパラメーターとして値を渡す一例です。権限、セッション、エラー処理、依存ライブラリ等の試験は別に必要です。実データや本番のDBへそのまま接続する例ではありません。
出典と確認した範囲(4資料)
出典と、読み取れる範囲
原著の公開要旨・取得できた本文部分・研究レビューと、仕様を確認する公式資料を照合しています。対象と比較条件が違う結果を一つの効果へ合算せず、手元の操作例は編集上の説明例として区別します。確認範囲は資料ごとに示しています。
対象・方法:当時のCopilotへ脆弱性に関係するシナリオを与え、生成プログラムを評価。
主な結果:危険なコードが生成され得ることを示す。
限界:プロンプト・対象・旧モデルに依存。人間との協働全体の効果や現在の発生確率へ一般化しない。
確認した範囲
IEEE版の書誌と著者のarXiv公開要旨を確認。2025年の同題再掲を独立研究として重複計上しない。 確認日:2026-10-29
取得できた書誌で撤回・懸念通知は確認されていません。出版社の履歴を網羅した監査ではありません。
対象・方法:47人、Python・JavaScript・Cの5種類のセキュリティ関連課題。
主な結果:AI利用条件で脆弱性の増加と安全性への過信を観察した。
限界:Codex系の当時の支援と課題設定。現在のすべての生成コードの脆弱性率を示すものではない。
確認した範囲
2022年arXiv版の本文冒頭・結果の説明と、2023年CCS版の書誌を照合。版差の全面監査は未実施。 確認日:2026-10-29
取得できた書誌で撤回・懸念通知は確認されていません。出版社の履歴を網羅した監査ではありません。
対象・方法:学生プログラマー58人。Cの単方向連結リストによる買い物リスト実装。
主な結果:この設定では重大な安全性の悪化が小さいと報告。
限界:特定の低水準C課題。Perryらとは参加者・言語構成・課題・支援条件が異なる。原著は2022年投稿、確認した版は2023年改訂。
確認した範囲
原著の公開要旨を確認。本文全体の精読・実験の追試は未実施。 確認日:2026-10-29
取得できた書誌で撤回・懸念通知は確認されていません。出版社の履歴を網羅した監査ではありません。
対象・方法:人間が脆弱性を入れたコードを起点に、Copilotの提案を比較する評価。
主な結果:既知の人間の誤りを起点とする比較で、生成コードの安全性を調べる。
限界:任意の新規開発全体の危険率ではない。人間も誤ることと生成結果の検証不要は別。
確認した範囲
2022年arXiv版の研究設定の本文と、2023年誌上版の書誌を照合。誌上版全文の版差照合は未実施。 確認日:2026-10-29
取得できた書誌で撤回・懸念通知は確認されていません。出版社の履歴を網羅した監査ではありません。
この記事は複数文献を照合した編集記事です。既存研究を照合し、結果と条件を紹介しています。研究が同じ参加者を含む可能性があるため、文献間の人数を足していません。本文の行動提案・未来のシナリオは編集上の整理です。


