はじめに
この記事は「Snykを使ってコードをセキュアにした記事を投稿しよう! by Snyk Advent Calendar 2023」の14日目の記事になります。tajima_tasoからお届けします。
生成AIのソースコードには脆弱性が潜んでいる
コード生成におけるAIはLLMの事を指している
2023年12月現在のアプリ開発者にとって、AIとは何か?という問いからイメージするもののほとんどは、LLMだと言っても過言ではないでしょう。
Github Copilotをはじめとしたコード生成AIの実現を支えているものは、LLM(大規模言語モデル)というAIモデルです。誤解を恐れずにいうとLLMは「それっぽい文字列をつなぎあわえて、文章を生成するAIモデル」です。では、何をもとにそれっぽい言葉を選んでいるのかというと数十億行レベルの膨大なテキストデータです。たとえばコード生成AIの場合は、Githubのパブリックリポジトリに上がっているようなソースコードを大量に学習して、throwときたら次はnew Errorが来る確率が高いだろうという判断を繰り返してソースコードを生成してくれます。
LLMは詐欺師と紙一重かもしれない
LLMは大量のソースコードをよりどころとして、それっぽさを出すように訓練されていきます。ただ、私達開発者はよく知っているように、ソースコードには脆弱性(バグ)がつきものです。それは世界中の人が開発に関わっている有名なOSSであろうと例外ではありません。
よって、LLMは脆弱性のあるソースコードも学習している可能性もあるという事です。そして、脆弱性のあるソースコードを学習したLLMをもとにしたコード生成AIツールが、脆弱性のあるソースコードを出力し、それをもとにまたLLMが脆弱性のあるソースコードを学習する連鎖ができてしまう危険性があります。そうなると、コード生成AIツールが出力するソースコードの信頼性はなくなってしまいます。
もちろんコード生成AIツールを提供する側もそういったLLMの性質による問題は重々承知であり、その対策の一環としてたとえばMicrosoftではGithub Copilotにセキュリティ脆弱性のフィルタリング機能を適用しています。
ただ、そうはいってもLLMは「それっぽい文字列をつなぎあわせるプロフェッショナル」なので、それっぽさを出す事にかけてはとても優秀です。世の中には人を騙す詐欺師がいると警戒していても、言葉巧みに専門用語を使う詐欺師を信じて騙されてしまう事があります。よって、騙されないようその道のプロのチェックを入れる方が安全だと考えています。
たとえとして不適切かもしれませんが、わかりやすさの為にコード生成AIツールを「詐欺師かもしれない人」として、Snykを「詐欺師の言っていることの中で、嘘を見破る人」として捉えることにします。
前置きが長くなりましたが、今回は「コード生成AIツールが生成したコードに脆弱性はあるのか?」そして、「脆弱性があった場合、Snykがそれを検出できるのか?」を検証してみました。
Snyk初心者の方へ
ちなみに、検証にあたっては以前私が書いた「アプリの脆弱性もインフラの脆弱性もSnykで全部漏らさず対処しよう!」をベースにしています。この記事の検証では、SQLインジェクション、OSコマンドインジェクション、ファイルインクルード脆弱性、クロスサイトスクリプティングの検出をSnykで実現できるかの検証を行っていました。Snykの機能を網羅して紹介しているので、ゼロからSnykを知りたい方は先にこちらの記事をご覧いただくと、イメージがしやすいと思います。CI/CDにも踏み込みたい場合は、「アジャイル開発時代のアプリケーションをセキュアにする方法」も参考になるかと思います。
Github Copilot vs Snyk
前提
それでは、Github Copilotを使って生成されたコードに脆弱性がないか?という検証をしてみます。生成されたコードに対してSnyk CLIを実行する事でも検証可能ですが、今回はVisual Studio CodeのSnykプラグインを用いて検証します。
Github Copilotを用いてソースコードを生成し、一般的な脆弱性が潜んでないか?を検証していきます。なお、Github Copilotのプラグイン導入は完了ずみとします。
コメントだけでコードを生成する
たとえば下記のようにコメントで、ユーザーIDを入力として受け入れて、MySQLからユーザーデータを取得する関数を作成する
とだけ書くと、薄いグレーで表示されたコードが提案されます。ここでTabキーを押すとこのコードが実際に出力されます。このようにコード生成AIツール(Github Copilot)を利用すると指示文だけ送るとコードの自動生成が可能です。
ちなみに、コーディング中は気づきませんでしたがキャプチャのコメントでは、「ユーザーIDを入力を受け入れて・・・」という日本語としておかしい文章を書いてしまっています。これは人間の私が書いたミスですが、LLMはそのニュアンスを理解して適切なコードを出力してくれています。これも、それっぽさのプロであるLLMの特徴が現れているところだと思います。
この要領で、コメントだけ書いて次々とコードを生成していきます。すると、最終的な結果は以下のようになりました。私が書いたのは日本語のコメントのところだけで、他の箇所はGithub Copilotが自動生成したコードです。
<?php
// ユーザーからの入力を受け入れて、任意のコマンドを実行する関数を作成する
function execute_command($command)
{
$output = array();
exec($command, $output);
return $output;
}
//上記の巻数をユーザーからの入力を受け入れて実行する
$command = $_POST['command'];
$output = execute_command($command);
// ユーザーIDを入力を受け入れて、MySQLからユーザーデータを取得する関数を作成する
function get_user_info($user_id)
{
$connection = mysqli_connect('localhost', 'root', '', 'test_db');
$sql = "SELECT * FROM users WHERE id = '$user_id'";
$result = mysqli_query($connection, $sql);
$user_info = mysqli_fetch_assoc($result);
return $user_info;
}
//ユーザーからの入力を受け入れて、上記の関数を実行する
$user_id = $_POST['user_id'];
$user_info = get_user_info($user_id);
// ユーザーからの入力を受け入れて、ファイルを読み込む関数を作成する
function read_file($file_path) {
$file_content = file_get_contents($file_path);
return $file_content;
}
// ユーザーからの入力を受け入れて、上記の関数を実行する
$file_path = $_POST['file_path'];
$file_content = read_file($file_path);
// ユーザーからの入力を受け入れて、画面に出力する関数を作成する
function display_output($output) {
echo "<pre>";
echo $output;
echo "</pre>";
}
// ユーザーからの入力を受け入れて、上記の関数を実行する
$output = $_POST['output'];
display_output($output);
VS Code上はどのようになっているかというと、下記のようになっています。プログラム上エラーがでてないので、VS Codeから何も警告は出ていません。つまり、開発者の読解力と知識だけで、このコードに脆弱性があるかどうか判断しなければならない状態です。
ここで、Snykのプラグインを有効化してみます。拡張機能でSnykと検索すればヒットするので簡単にインストールできます。
Snykをインストールした後、再びソースコードを見てみると、警告がたくさんでています。Snykを有効にした事によって警告が出ているので、恐らく脆弱性に関する指摘のはずです。該当の箇所にカーソルをあてて確認してみましょう。
たとえば$result = mysqli_query($connection, $sql);
の箇所にカーソルをあてると、高レベルの深刻度でSQLインジェクションが発生しているというメッセージを確認できます。
続いて、その上の$connection = mysqli_connect('localhost', 'root', '', 'test_db');
にカーソルをあてると、クレデンシャルをハードコーディングするな!というメッセージが確認できます。
このようにソースコード上で一個一個確認してもいいのですが、アクティビティバーのSnykのアイコンをクリックすると一覧で脆弱性がある箇所を確認できます。
以上の事から、少なくとも現段階においてコード生成AIツールが出力したコードには、脆弱性が潜んでしまっている事がわかりました。そして、Snykを使う事によってその脆弱性をあぶり出す事ができました。
自動で脆弱性を修正したい
「Snykで脆弱性を検出できたのはいいけど、そのまま修正まで自動でやってくれたら素敵だな」と夢見てしまいますが、実はそれも可能です。実現にはSnykが買収したDeepCode社の技術が活用されているようですが、まだオープンベータ版なのでJavaScriptとTypeScriptのみ対応しています。
Fix code vulnerabilities automatically
せっかくなので、さきほどPHP版でハードコーディングすな!と指摘されていたコードをTypeScriptで書き換えて検証してみました。なお、この別のプログラミング言語へのソースコード書き換えにあたっても生成AIを活用しました。
書き換えた直後はpasswordの値がベタ書きされていますが、何やら上部に「Fix this issue: Use of Hardcoded Credentials」というメッセージが出てきました。クリックできそうな雰囲気があるので、クリックしてみます。
しばらくするとCongratulations!というメッセージとともに、ベタ書きされていたhogehoge
がprocess.env.DB_PASSWORD
という環境変数に自動的に書き換わりました。機能的にはまだ発展途上ですが、コード生成AIツールと併用したいSnykの今後の目玉機能の一つだと思います。
まとめ
当初の検証目的だった「コード生成AIツールが生成したコードに脆弱性はあるのか?」、および「脆弱性があった場合、Snykがそれを検出できるのか?」の答えはどちらもYESでした。
コード生成AIツールを活用した開発によって生産性が爆発的に向上しましたが、今回は手放しで喜べない側面にあえて焦点をあててみました。しかしながら「コード生成AIツールは危ないから使わないでおこう」と主張したいわけではないです。むしろ逆で、Snykのようなセキュリティを担保してくれるツールも併用して、より積極的かつ安全に生成AIを活用していこうという主張です。
生成AIによってプロダクトやサービス開発のスピードは今後向上していくでしょう。それとともに、セキュリティも含めたテストの観点が今以上に重要になってくると思います。
テストやセキュリティ対策の工程をとにかく前倒しでやっていこうという、いわゆる「シフトレフト」が近年注目されています。それを実現する為にも、生成AIでガンガン攻める一方で、Snykで守りを固める開発手法を確立されてはいかがでしょうか?
参考文献