まず結論
AI生成コードで危ないのは、動くけれど守れていないコードです。
ここでは、AIが出しがちな弱いコード例を見ながら、何が危ないかを整理します。
SQLを文字列連結する
危ない例:
const query = `SELECT * FROM users WHERE email = '${email}'`;
ユーザー入力をSQL文字列に直接埋め込むと、SQLインジェクションの原因になります。
安全な方向:
const result = await db.query(
"SELECT * FROM users WHERE email = $1",
[email]
);
プレースホルダーやORMの安全なAPIを使います。
トークンをlocalStorageに長く保存する
危ない例:
localStorage.setItem("token", accessToken);
localStorageはJavaScriptから読めます。XSSが起きると、攻撃者のJavaScriptにも読まれる可能性があります。
安全な方向は要件によりますが、次を検討します。
- HttpOnly Cookie
- 短命なアクセストークン
- サーバ側セッション
- XSS対策
- トークンの権限を最小化
認証だけで認可をしていない
危ない例:
app.get("/api/users/:id", requireLogin, async (req, res) => {
const user = await getUser(req.params.id);
res.json(user);
});
ログインしていれば、他人のIDも見られる可能性があります。
安全な方向:
app.get("/api/users/:id", requireLogin, async (req, res) => {
if (req.user.id !== req.params.id) {
return res.status(403).json({ message: "forbidden" });
}
const user = await getUser(req.params.id);
res.json(user);
});
認証は「誰か」、認可は「その操作をしてよいか」です。
エラー詳細をそのまま返す
危ない例:
try {
await saveUser(input);
} catch (error) {
res.status(500).json({ error: String(error) });
}
内部のテーブル名、ファイルパス、設定情報が外に出る可能性があります。
安全な方向:
try {
await saveUser(input);
} catch (error) {
console.error(error);
res.status(500).json({ message: "internal server error" });
}
ユーザーには一般的なメッセージ、運営側にはログを残します。
入力検証がない
危ない例:
app.post("/api/posts", async (req, res) => {
const post = await createPost(req.body);
res.json(post);
});
req.body をそのまま信用すると、想定外の値が入ります。
確認すること:
- 必須項目
- 文字数
- 型
- 許可する値
- HTMLやスクリプトの扱い
- 権限境界
CORSを雑に全許可する
危ない例:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
認証情報を含むAPIで、雑に全Originを許可するのは危険です。
安全な方向:
- 許可Originを明示する
- credentialsの必要性を見直す
- 開発環境と本番環境を分ける
- APIの認証・認可を必ず確認する
AIに追加で聞くべきこと
AIがコードを出した後、次のように聞くと危険を見つけやすくなります。
このコードのセキュリティ上の弱い前提を列挙してください。
特に入力検証、認証、認可、秘密情報、エラー露出を見てください。
注意: AIが「安全です」と言っても、最終確認は公式ドキュメント、既存設計、テスト、レビューで行います。
まとめ
AI生成コードでは、SQL文字列連結、トークン保存、認可漏れ、エラー露出、入力検証不足、CORS全許可に注意します。
動くコードでも、安全とは限りません。特にユーザー入力、認証、認可、秘密情報に関わる部分は、必ず人間が確認します。