普通に開発すると脆弱性を埋めこむ?
最近、仕事で使うちょっとしたプログラムや設定ファイルをChatGPTなどのAIに書いてもらうことが多いです。
普通にいろいろな脆弱性があるコードが吐かれるので、その度に修正してもらったり、自分で修正したり、場合によっては放置したりしています。
プロンプトに「セキュリティに気をつけて」みたいなことを書くと大丈夫、みたいな狂気を孕んだことを書いている人がいて、AIで開発している人も玉石混交しているなあと思ったので、自分の体感は果たして正しいのか疑問に思ったので雑に試してみます。
試したプロンプト
クエリストリングから入力した値を表示するWebアプリ
XSSありそうですよね。
それぞれのエンジンによる比較
Claude 3.5 Sonnet
Cursorのデフォルトで設定されていたAIモデルです。いきなりいいコードを吐いてくれますね。テンプレートエンジン使わないとこうなってしまうよね。という感想です。
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
# クエリパラメータを取得
message = request.args.get('message', 'メッセージがありません')
# シンプルなHTMLを返す
return f'''
<html>
<head>
<title>クエリストリング表示</title>
<meta charset="utf-8">
</head>
<body>
<h1>クエリストリングの内容:</h1>
<p>{message}</p>
<p><small>URLに ?message=こんにちは のようにパラメータを追加してください</small></p>
</body>
</html>
'''
if __name__ == '__main__':
app.run(debug=True)
http://localhost:5000/?message=<script>alert(1)</script>
でJavaScriptが実行できました。
ChatGPT 4o with canvas
次に今普通に使ってたChatGPT 4o with canvasで聞いてみました。すると以下のようなコードを吐いてくれました。htmlとJavaScriptで処理するのでサーバーの負担が少なくていいですね。
window.location.search
は今のブラウザだとURLエンコードされるから安全… と思いきやURLSearchParams
ってデコードされるんですね。
ということでDOM based XSSがありましたね。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>クエリストリングの値を表示</title>
<script>
document.addEventListener("DOMContentLoaded", function() {
const params = new URLSearchParams(window.location.search);
let output = '';
// クエリストリングのキーと値をすべて表示
params.forEach((value, key) => {
output += `<p>${key}: ${value}</p>`;
});
if (output) {
document.getElementById("queryParams").innerHTML = output;
} else {
document.getElementById("queryParams").innerHTML = "<p>クエリパラメータがありません。</p>";
}
});
</script>
</head>
<body>
<h1>クエリストリングの値を表示するWebアプリ</h1>
<div id="queryParams"></div>
</body>
</html>
http://localhost/?message=<img/src/onerror=alert(1)>
でJavaScriptが実行できました。
JavaScriptはちょっと苦手かもしれなかったので、pythonで書いてとお願いしてみます。
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/')
def index():
# クエリストリングのパラメータを取得
params = request.args
output = ""
# パラメータがある場合、それらを表示
if params:
for key, value in params.items():
output += f"<p>{key}: {value}</p>"
else:
output = "<p>クエリパラメータがありません。</p>"
# HTMLテンプレートを定義
html_template = """
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>クエリストリングの値を表示</title>
</head>
<body>
<h1>クエリストリングの値を表示するWebアプリ</h1>
<div id="queryParams">
{{ output|safe }}
</div>
</body>
</html>
"""
# テンプレートをレンダリングして返す
return render_template_string(html_template, output=output)
if __name__ == '__main__':
app.run(debug=True)
デフォルトのテンプレートエンジンを使っているので安全かと思いましたが
{{ output|safe }}
???!!!
テストしてもらう
自分で試すのも嫌になったのでChatGPTにテストしてもらいました。
以下は、クエリパラメータ
name=<s>John&age=25
に対するレスポンスですクエリパラメータ内の name に
<s>
タグが含まれており、HTMLの一部として解釈されているため、"John" の文字が取り消し線付きで表示されています。これは入力されたHTMLがエスケープされていないためであり、悪意のある内容が入力された場合、クロスサイトスクリプティング(XSS)攻撃の原因となる可能性があります。
これを防ぐためには、ユーザー入力をページに表示する前に適切にエスケープすることが推奨されます。
もちろん
http://localhost:5000/?name=<script>alert(1)</script>
でJavaScriptが実行できました。
まとめ
この結果を見る限り、雑にAIにお願いすると雑にXSSがあるコードを吐き出される可能性が高いことがわかりました。
次に気が向いたら別のエンジンで試してみたり、ちゃんとエスケープするコードを書いてくれるようなプロンプトにしてみたり、別の脆弱性が起きるような箇所のコードを書いてもらおうと思います。