Reputation: 21
I'm trying out SQL server's full-text search indexes and more specifically the CONTAINS function for finding out whether a column contains an exact match for a user's full name.
I am using the Kotlin Exposed library where the contains function is not supported. So I created a custom boolean operation for this so that I could still take advantage of the Exposed DSL when writing my queries and not have to resort to native queries.
See my implementation below:
class Contains(private val columns: List<Column<*>>, private val searchText: String) : Op<Boolean>() {
override fun toQueryBuilder(queryBuilder: QueryBuilder) = queryBuilder {
append("CONTAINS((")
append(columns.joinToString(", ") { it.name })
append("), ")
registerArgument(VarCharColumnType(), "\"*$searchText*\"")
append(")")
}
}
internal infix fun Column<*>.contains(searchText: String): Op<Boolean> =
Contains(columns = listOf(this), searchText = searchText)
And in my Spring repository class I have added this function for finding users by their full name:
fun findUsersByName(searchName: String): List<User> =
Users
.selectAll()
.limit(100)
.where {
(Users.fullName contains searchName) and
(Users.protectedIdentity eq false)
}
.orderBy(
Users.firstName to SortOrder.ASC_NULLS_LAST,
Users.lastName to SortOrder.ASC_NULLS_LAST,
Users.userSsn to SortOrder.ASC
)
.map(User::wrapRow)
Now to my question, how do I ensure that my code is safe from SQL injections when defining custom boolean operations like this? How do I ensure the statement is prepared and that the user cannot inject malicious strings? I used registerArgument
in my QueryBuilder
but I'm not sure if it's the right way to go about it.
I wrote a test but I get a syntax error when trying to escape the string instead of the expected empty list of users which makes me suspect I did not implement the parameterization correctly.
@ParameterizedTest
@ValueSource(strings = [
"*\"') OR 1=1;--",
"*\"\') OR 1=1;--",
])
fun `should not be able to find all users by SQL injection string`(injectionString: String) = transaction(database) {
assertThat(userRepository.findUsersByName(injectionString))
.isEmpty()
}
org.jetbrains.exposed.exceptions.ExposedSQLException: com.microsoft.sqlserver.jdbc.SQLServerException: Syntax error near ''' in the full-text search condition '"**"') OR 1=1;--*"'.
I have input validation in my REST controller which ensures a user could technically not provide this string but I still want to implement it correctly just to be sure.
Upvotes: 0
Views: 92