JDBI How can I dynamically create a WHERE clause while preventing SQL Injection?

ⅰ亾dé卋堺 提交于 2019-12-09 00:45:51

问题


I want to dynamically filter a JDBI query.

The a list of parameters is passed from the UI via REST e.g.

http://localhost/things?foo=bar&baz=taz
http://localhost/things?foo=buz

Which is (clumsily) built (Jersey @Context UriInfo::getQueryParameters -> StringBuilder) to something like this:

WHERE foo=bar AND baz=taz

And passed to JDBI which looks like this:

@UseStringTemplate3StatementLocator
public interface ThingDAO {
   @SqlQuery("SELECT * FROM things <where>)
   List<Thing> findThingsWhere(@Define("where") String where);
}

As far as I understand the current implementation is vulnerable to SQL injection. I can obviously sanitize the column names but not the values. 1

There must be a more elegant and SQL Injection proof way of doing this.


回答1:


Inspired by Jean-Bernard I came up with this:

public class WhereClause {
    public HashMap<String, String> queryValues; // [<"foo","bar">, <"baz","taz">]
    public String preparedString; // "WHERE foo=:foo AND bar=:baz"
}

Which is bound via a custom Binder BindWhereClause:

@BindingAnnotation(BindWhereClause.WhereClauseBinderFactory.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface BindWhereClause {
    class WhereClauseBinderFactory implements BinderFactory {
        public Binder build(Annotation annotation) {
            return new Binder<BindWhereClause, WhereClause>() {
                public void bind(SQLStatement q, BindWhereClause bind, WhereClause clause) {
                    clause.queryValues
                            .keySet()
                            .forEach(s -> q.bind(s, clause.queryValues.get(s)));
                }
            };
        }
    }
}

And a combination of @Define and @Bind:

@UseStringTemplate3StatementLocator
public interface ThingDAO {
   @SqlQuery("SELECT * FROM things <where>")
   List<Thing> findThingsWhere(@Define("where") String where, 
                               @BindWhereClause() WhereClause whereClause);
}

This should be injection proof. (is it?)




回答2:


Use a parameterized query. Here is the jdbi page for them.
Parameterized queries are the way to prevent sql injection in most settings.

You can dynamically create the where statement, but leave parameter names instead of values, they will be bound later, in a safe way.

You would probably be interested in this bit specifically, since your parameters are dynamic:

@BindingAnnotation(BindSomething.SomethingBinderFactory.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface BindSomething 
{ 

  public static class SomethingBinderFactory implements BinderFactory
  {
    public Binder build(Annotation annotation)
    {
      return new Binder<BindSomething, Something>()
      {
        public void bind(SQLStatement q, BindSomething bind, Something arg)
        {
          q.bind("ident", arg.getId());
          q.bind("nom", arg.getName());
        }
      };
    }
  }
}

I've never used jdbi so I'm not 100% sure what you'd have to do, but it looks like the q.bind(...) method is exactly what you want.



来源:https://stackoverflow.com/questions/36359708/jdbi-how-can-i-dynamically-create-a-where-clause-while-preventing-sql-injection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!