Kotlin Exposed:如何创建预准备语句或规避SQL注入?
安全处理Kotlin Exposed查询中的用户参数,防范SQL注入
嘿,刚好对Kotlin Exposed的参数安全这块熟得很,来给你捋清楚!
首先要给你吃个定心丸:Exposed的DSL查询默认就是用预准备语句处理参数的,完全不需要你手动去构建预准备语句框架——它底层会自动把你传入的参数作为绑定变量,而不是直接拼进SQL字符串里,从根源上避免了SQL注入风险。
你的现有代码其实已经是安全的
看你这段代码:
private fun accountInfo(msg: AccountInfoMsg) { transaction { val accountInfo = UserAccount.wrapRow( Account.innerJoin(Account_Banned) .select { Account.email.eq(msg.login.toLowerCase()) and (Account.id eq Account_Banned.accountId) }.single() ) } }
这里你用Account.email.eq(msg.login.toLowerCase())传递用户输入的参数,Exposed会自动把msg.login.toLowerCase()的值作为预准备语句的占位符参数,生成的SQL类似这样(底层逻辑):
SELECT ... FROM Account INNER JOIN Account_Banned ON ... WHERE Account.email = ? AND Account.id = Account_Banned.accountId
然后把msg.login.toLowerCase()的值绑定到?这个占位符上,完全不会有SQL注入的问题。
需要避免的危险操作
唯一会引入SQL注入的情况,是你手动拼接字符串到查询语句中,比如错误地使用SqlExpressionBuilder.raw直接拼参数:
// 危险!不要这么做! select { raw("Account.email = '${msg.login.toLowerCase()}'") }
这种写法会把用户输入直接拼进SQL,才会被注入攻击利用。只要你坚持用Exposed提供的DSL方法(比如eq、like、inList等)来传递参数,就绝对安全。
额外的优化建议
- 处理
single()的异常:single()会在查询结果为空或超过一条时抛出异常,建议用singleOrNull()更稳妥,然后做空值判断:
val accountRow = Account.innerJoin(Account_Banned) .select { Account.email.eq(msg.login.toLowerCase()) and (Account.id eq Account_Banned.accountId) }.singleOrNull() accountRow?.let { row -> val accountInfo = UserAccount.wrapRow(row) // 处理账号信息 } ?: run { // 处理未找到记录的情况 }
- 提前校验输入:虽然Exposed已经防注入,但对用户输入做业务校验(比如检查邮箱格式)还是很有必要的,能减少无效查询和异常情况。
内容的提问来源于stack exchange,提问作者nllsdfx
相关产品推荐
相关产品推荐

