Getting rid of SQL injection without using Prepared statement

前端 未结 1 705
被撕碎了的回忆
被撕碎了的回忆 2021-01-24 09:13

My application has a lot of JDBC queries written using Statement and is thus vulnerable to SQL injection. Unfortunately this application was developed around 10 years back and p

相关标签:
1条回答
  • 2021-01-24 09:37

    Hah. Unfortunately, it almost always depends on the monetary and managerial decision for what's appropriate, but "it is very tedious" is not generally considered a valid engineering concern -- it is but an excuse to appropriately refactor the code.

    Meanwhile, the question requests non-PreparedStatement methods: in short, if you cannot offload the work to a library (e.g. prepared statements), then the only other method is to do it yourself for each "injected" item. Either way, the work of checking the input must be performed. The sole question is where it is done, and what programmer's expertise made the input validation code.

    For example, consider a simple SELECT statement:

        sql = "SELECT * FROM mytable WHERE id = " + untrustedVar;
    

    For completeness, we might assume the injection example where untrustedVar is a string like 1 OR 1 or 1; DROP TABLE mytable; Obviously this would result in unwanted behavior, vis-a-vis all rows returned to the caller, or a now missing database table:

    SELECT * FROM mytable WHERE id = 1;
    DROP mytable;
    

    Obligatory XKCD reference

    In this case, you could let the language semantics at least ensure that unstrustedVar is an integer, perhaps in your function definition:

    String[] selectRowById ( int untrustedVar ) { ...
    

    Or if it's a string, you might do this with a regex like:

    Pattern valid_id_re = Pattern.compile('^\d{1,10}$');  // ensure id is between 1 and 10 billion
    Matcher m = valid_id_re.matcher( unstrustedVar );
    if ( ! m.matches() )
        return null;
    

    But if you have longer inputs that do not have any grammar or structure guarantees (e.g., a web form textarea), then you will need to do the lower-level character replacements to escape the potentially bad characters. Per statement. Per variable. Per database flavor (PostgreSQL, Oracle, MySQL, SQLite, etc.). This ... is a can of worms.

    The upside, of course, is that if you are not using prepared statements, and no one has yet done the work to otherwise avoid SQL injection attacks for your application, you have no where to go but up.

    Meanwhile, I urge, urge, urge you to reconsider your stance on "We can't use prepared statements because reasons." More to the point, and as Gord Thompson correctly points out in the comments below, "it will be a considerable amount of work in any case, so why not just do it right and be done with it?"


    Edit

    After writing the above, it occurred to me that some might think that merely writing a prepared statement = better security. Actually, it's prepared statements with bound parameters that ups the security. For example, one could write this:

    String sql = "SELECT * FROM mytable WHERE id = " + untrustedVar;
    PreparedStatment pstmt = dbh.prepareStatement( sql );
    return pstmt.executeQuery();
    

    At this point, you've done little more than prepare a statement that has already been injected with malicious code. Instead, consider the actual binding of parameters:

    String sql = "SELECT * FROM mytable WHERE id = ?";  // Raw string; never touched by "tainted" variable
    PreparedStatment pstmt = dbh.prepareStatement( sql );
    pstmt.setObject(1, p);  // Perform the actual binding.
    return pstmt.executeQuery();
    

    This latter example does two things. It first creates a known safe format, and sends that to the DB for preparation. Then, after the DB has returned a handle to the prepared statement, do we bind the variables, and finally execute the statement.

    0 讨论(0)
提交回复
热议问题