Useful to determine if braces are correctly matched before processing. Helps reduce SQLi.
I was using a version of smartSearch from CFLib.org that I had updated with some simple regex detection for SQLi strings, but it wasn't catching everything. I considered disabling the bracket matching feature and rejecting any query search terms that attempted to use (
or )
, but then considered that I should validate so that the feature could still be used since it is beneficial when not being exploited.
I couldn't find any UDFs on CFLib or other ColdFusion/CFML snippets to validate brackets in a string. (If there's existing code, let me know. I wasn't able to find it.) I read a couple recommendations on StackOverflow indicating that it shouldn't be validated using regex, so I wrote a UDF that reduces & validates braces in a string and returns a Boolean response. This allows us to determine whether we can safely use the string when generating a SQL search string (or use 1=0
as a fallback).
Source Code
https://gist.github.com/JamoCA/a35ffaabc00e0339a9996e27825159a7
<!--- areBracesValid ColdFusion/CFML UDF (2022-09-16) | |
Useful to determine if braces are correctly matched before processing. Helps reduce SQLi. | |
By James Moberg - SunStar Media https://www.sunstarmedia.com/ | |
Gist: https://gist.github.com/JamoCA/a35ffaabc00e0339a9996e27825159a7 | |
Blog: https://dev.to/gamesover/arebracesvalid-udf-for-coldfusioncfml-21fg | |
Tweet: https://twitter.com/gamesover/status/1570911352138641408 | |
20220918 Updated to use single refind/replaceAll expressions | |
20221108 Updated to use "while" instead of cfloop/condition (which isn't supported by Lucee in cfscript.) | |
---> | |
<cfscript> | |
boolean function areBracesValid(required string string) hint="Validates if braces are correctly matched" { | |
local.string = javacast("string", arguments.string).replaceAll("[^\[\]\{\}\(\)]", ""); | |
if (!len(local.string)) return true; // no braces | |
if (len(local.string) mod 2) return false; // odd number of braces | |
local.bracketFound = 1; | |
while (local.bracketFound) { | |
local.bracketFound = refind("(\(\))|(\[\])|(\{\})", local.string); | |
if (local.bracketFound){ | |
local.string = local.string.replaceAll("(\(\))|(\[\])|(\{\})", ""); | |
} | |
} | |
return (len(local.string)) ? false : true; | |
} | |
</cfscript> |
<cfset tests = [ | |
"1') AND 5410=3868 AND ('tVgF'='tVgF" | |
,"1') AND 3265=DBMS_PIPE.RECEIVE_MESSAGE(CHR(90)||CHR(76)||CHR(98)||CHR(98),5) AND ('wIxt'='wIxt" | |
,"(1=0) and (R.ID = 2)" | |
,"(([R].[Name] LIKE '%a%') OR ([R].[First] LIKE '%a%') OR ([R].[Last] LIKE '%a%') OR ([R].[Company] LIKE '%a%')) AND (([R].[Name] LIKE '%b%') OR ([R].[First] LIKE '%b%') OR ([R].[Last] LIKE '%b%') OR ([R].[Company] LIKE '%b%')) AND (([R].[Name] LIKE '%c%') OR ([R].[First] LIKE '%c%') OR ([R].[Last] LIKE '%c%') OR ([R].[Company] LIKE '%c%')) AND (([R].[Name] LIKE '%d%') OR ([R].[First] LIKE '%d%') OR ([R].[Last] LIKE '%d%') OR ([R].[Company] LIKE '%d%'))" | |
]> | |
<cfoutput> | |
<cfloop array="#tests#" index="test"> | |
<fieldset> | |
<legend>#encodeforhtml(test)#</legend> | |
VALID = #areBracesValid(test)# | |
</fieldset> | |
</cfloop> | |
</cfoutput> |
Top comments (3)
I was about to ask if you were using
cfqueryparam
for your search; but, I just looked at the Smart Search UDF, and it looks like it uses some SQL generation, which somewhat rules-out the parameterization.I use an internally updated version of SmartSearch. I've added logic to identify SQLi and return
1=0
if any is detected. (I'm planning on sharing my UDF updates, but will be releasing it on a new CFML resource website that I'm building.)Sounds exciting! Looking forward to seeing what you've got in store.