I am beginner in android development.
I have a question. what is ?s term used for in explanation below? i got it from the documentation of android developer.
public int update (String table, ContentValues values, String whereClause, String[] whereArgs)
Added in API level 1
Convenience method for updating rows in the database.
Parameters
table the table to update in
values a map from column names to new column values. null is a valid value that will be translated to NULL.
whereClause the optional WHERE clause to apply when updating. Passing null will update all rows.
whereArgs You may include ?s in the where clause, which will be replaced by the values from whereArgs. The values will be bound as Strings.
Returns
the number of rows affected
Basically its a variable to be filled in later. You should use these everywhere that data is coming from a user, a file, or anything else not hardcoded into the app. Why? Because it prevents security problems due to SQL injection. The variables cannot themselves be SQL, and will not be parsed as SQL by the database. So if all variables sent from users to the db are bind variables you remove that entire class of security issues from the app.
A PreparedStatement supports a mechanism called bind variables. For example,
SELECT * FROM table WHERE id = ?
In the above query, there is a single bind parameter for an id. You might use it (to get a row where id is 100) with something like
String sql = "SELECT * FROM table WHERE id = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, 100);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
}
}
} catch (SQLException e) {
e.printStackTrace();
}
Each ? corresponds, in order, to an index of the sql arguments passed in the method's last String[] whereArgs parameter
public int update (table, values, "age > ? AND age < ?", new String[] { "18", "25"});
The documentation meant literally using '?' in the whereClause statement. Simple example:
rawQuery("select * from todo where _id = ?", new String[] { id });
From the above statement, during execution, the ? will be replaced by the value of variable id.
This mechanism helps prevent SQL Injection.
Related
I want to generate SQL using MessageFormat so that same string can be used by many users and they just have to pass where clause arguments.
e.g. I want select * from user where name='John' and age=15 and area='JStreet'
I can do it using MessageFormat.format(select * from user where {0}={1} and {2}={3} and {4}={5} ,"name","'John'", "age","15","area","'JStreet'")
But I want it dynamic. Means here I am bounded till {0}-{5} what if I need to add more AND conditions.
How can I do this ?
Do not let the user specify the column names as strings. That makes your code easy to break, and it opens you to a very common and dangerous security vulnerability known as SQL injection. I know you said it’s only “for internal use,” but employees/students can be hackers and it’s always possible one will wish to cause harm.
Instead, represent the columns as enum values. I assume the columns of the user table are fixed, so you can hard-code them in the enum:
public enum UserField {
NAME,
AGE,
AREA
}
As others have mentioned, always use a PreparedStatement when making use of values from end users or from unknown code. You can now use the enums to build that PreparedStatement:
public PreparedStatement createStatement(Map<UserField, ?> values,
Connection conn)
throws SQLException {
Collection<String> tests = new ArrayList<>(values.size());
for (UserField field : values.keySet()) {
tests.add(field.name().toLowerCase() + "=?");
}
String sql;
if (tests.isEmpty()) {
sql = "select * from user";
} else {
sql = "select * from user where " + String.join(" and ", tests);
}
PreparedStatement statement = conn.prepareStatement(sql);
int index = 0;
for (Object value : values) {
statement.setObject(++index, value);
}
return statement;
}
I'm creating parcel machine program. Every parcel has unique parcelID which is exported to mysql db. The problem is that every time when I run the program, the program is counting parcelID from 0. I'm looking for a solution which will allow me to check the last parcelID in the database and create row after the last one.
Now it looks like this: 1. I'm creating a new row in db (successfully) by java program. 2. I'm closing the program after some time. 3. I run the program again and I can't add another new row because there is error "duplicate entry '1' for key 'PRIMARY'".
public static void post() throws Exception{
int parcelID = Parcel.generateID();
int clientMPNumber = Parcel.typeClientNumber();
int orderPassword = Parcel.generatePass();
try{
Connection con = getConnection();
PreparedStatement posted = con.prepareStatement("INSERT INTO Parcels.Orders (parcelID, clientMPNumber, orderPassword) VALUES ('"+parcelID+"', '"+clientMPNumber+"', '"+orderPassword+"')");
posted.executeUpdate();
}
catch(Exception e){
System.out.println(e);
}
finally{
System.out.println("Insert completed");
}
}
and the method is:
public static int generateID(){
parcelID = parcelID + 1;
return parcelID;
}
I'd let the database do the heavy lifting for you - Just define the parcelID column as serial instead of trying to set its value yourself.
You shouldn't use Id generation, just create auto_increment column in database table
As described here , define your primary key column to auto increment for each insert so your java code doesn't have to manually calculate primary key value each time.
If that is not a possibility, you need to show how you declare & initialize parcelID. As of your current code, parcelID looks to be a class level field that gets initialized to zero for each run so you always get the same value - 1. You need to initialize with last value from data base.
Also, implement suggestion as mentioned in comment to your question regarding PreparedStatement
There are a couple of things to attent to.
// parcelID should be an INT AUTOINCREMENT primary key.
try (PreparedStatement posted = con.prepareStatement(
"INSERT INTO Parcels.Orders (clientMPNumber, orderPassword) "
+ "VALUES (?, ?)",
Statement.RETURN_GENERATED_KEYS);
posted.setString(1, clientMPNumber);
posted.setString(2, orderPassword);
posted.executeUpdate();
try (ResultSet rsKey = posted.getGeneratedKeys()) {
if (rsKey.next()) {
int parcelID = rsKey.getInt(1);
return parcelID; // Or such
}try-with-resources
}
}
The database can deal with automatic numbering best, so that two transactions at the same time do not steal the same "next" number.
You should close things like Connection, PreparedStatement and ResultSet. This can best be done using the a bit awkward syntax of try-with-resources. That closes automatically even on exception and return.
PreparedStatements should be used with placeholders ?. This takes care for escaping special characters like ' in the password. Also prevents SQL injection.
Stylistic better use SQLException above Exception. Better maybe even a throws SQLException.
I have a use case in which I am trying to create the whole sql based on the user`s ip to the api.
EG: user hits the api with the format /what/table/field/field_value?name=value1
public static Result getRows(String what, String table, String field, String field_value, String more_where_clause) throws SQLException {
//more_where_clause will have as many condition for where clause
String sql = String.format("select ? from ? where ?=?");
if (!where.equals("")) {
sql += String.format(" and ?");
}
ResultSet rs = targetDB.query(sql, what, table, field, field_value, more_where_clause);
public <T extends Comparable<T>>ResultSet query(String sql, T... args) throws SQLException {
ResultSet rs = null;
try{
preparedStatement = conn.prepareStatement(sql);
for(int i=0; i<args.length; i++){
preparedStatement.setObject(i + 1, args[i]);
}
rs = preparedStatement.executeQuery();
}
catch(SQLException e)
{
e.printStackTrace();
}
return rs;
}
But when I query it using say :
/name/user_table/first_name/john?last_name=doe
I get a sql string like
select 'name' from 'user_table' where 'first_name'='john' and 'last_name=doe'
as preparedStatement.setObject(i + 1, args[i]);
infers it as String.
What is a better way to do this and also avoid sql injection.
EDIT :
Also apart from parameterizing the where clause part, what other extra checks can I do? How do I take care of the more_where_clause where user can enter more things to the where condition.
You can't bind names (field, table, etc.), just values.
Based on security principles you can define a "white-list" regular expression to check name conformity with your restriction (ie allowing only [A-ZaZ0-9_-]+)
Poorer solution is to define a "black-list" on which you forbid double-quotes (") and escape sequences (if dealing with multiple RDBMS engine, it can be a pain) and then put all names between double-quotes. Be aware of case-sentivity when using double-quotes.
You can also check OWASP librairies. I know they offer APIs to deal with HTML, CSS, JavaScript & HTTP injections. May be they also define API to deal with generated SQL.
Ultimately you can also build/query database metadata and match object names against provided ones. Which in this case you can rely on value binding. In this case don't forget to use value returned from metadata and to enclosed them in double-quotes on generated SQL.
I am using java to connect with oracle.This is the code which I have used
public List<FavouriteShop> getmyfavouriteshop(String username) {
List<FavouriteShop> res=null;
res = this.getJdbcTemplate().query("select * from(Select tbl_orderdetails.branch_name as myfavourite,tbl_orderdetails.branch_id as branch_id from tbl_orderdetails inner join tbl_ordermaster on tbl_orderdetails.order_master_id=tbl_ordermaster.ordermasterid where tbl_ordermaster.user_id='"+username+"' group by tbl_orderdetails.branch_name,tbl_orderdetails.branch_id order by count(tbl_orderdetails.branch_name) desc) where rownum<=3", new MyFavourite());
return res;
}
private class MyFavourite implements RowMapper<FavouriteShop> {
public FavouriteShop mapRow(ResultSet rs,int i) throws SQLException {
FavouriteShop g=new FavouriteShop();
g.setBranch_id(rs.getString("branch_id"));
g.setMyfavourite(rs.getString("myfavourite"));
return g;
}
}
I tried to execute same query in oracle I am getting output but not here and I am getting only empty result.
First, you have a possible SQL injection. You can avoid this by giving username as an argument to query
this.getJdbcTemplate().query("select * from (... where tbl_ordermaster.user_id=? ...) where rownum<=3",
new Object[]{ username }, new MyFavourite());
A possible reason for the empty result might be
... where tbl_ordermaster.user_id='"+username+"' ...
Usually, user_id is an integer value, but you compare it to a String and enclose it in quotes. Passing username as an argument to query as shown above, should already take care of this.
Usually it is not the same query or not the same database :)
Extract your query text to separate variable, print it to logs. Then copy-paste from logs to sql developer.
And check database and user name.
Also, it is possible that you inserted that entries but forgot to add COMMIT.
I know that the only really correct way to protect SQL queries against SQL injection in Java is using PreparedStatements.
However, such a statement requires that the basic structure (selected attributes, joined tables, the structure of the WHERE condition) will not vary.
I have here a JSP application that contains a search form with about a dozen fields. But the user does not have to fill in all of them - just the one he needs. Thus my WHERE condition is different every time.
What should I do to still prevent SQL injection?
Escape the user-supplied values? Write a wrapper class that builds a PreparedStatement each time? Or something else?
The database is PostgreSQL 8.4, but I would prefer a general solution.
Thanks a lot in advance.
Have you seen the JDBC NamedParameterJDBCTemplate ?
The NamedParameterJdbcTemplate class
adds support for programming JDBC
statements using named parameters (as
opposed to programming JDBC statements
using only classic placeholder ('?')
arguments.
You can do stuff like:
String sql = "select count(0) from T_ACTOR where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return namedParameterJdbcTemplate.queryForInt(sql, namedParameters);
and build your query string dynamically, and then build your SqlParameterSource similarly.
I think that fundamentally, this question is the same as the other questions that I referred to in my comment above, but I do see why you disagree — you're changing what's in your where clause based on what the user supplied.
That still isn't the same as using user-supplied data in the SQL query, though, which you definitely want to use PreparedStatement for. It's actually very similar to the standard problem of needing to use an in statement with PreparedStatement (e.g., where fieldName in (?, ?, ?) but you don't know in advance how many ? you'll need). You just need to build the query dynamically, and add the parameters dynamically, based on information the user supplied (but not directly including that information in the query).
Here's an example of what I mean:
// You'd have just the one instance of this map somewhere:
Map<String,String> fieldNameToColumnName = new HashMap<String,String>();
// You'd actually load these from configuration somewhere rather than hard-coding them
fieldNameToColumnName.put("title", "TITLE");
fieldNameToColumnName.put("firstname", "FNAME");
fieldNameToColumnName.put("lastname", "LNAME");
// ...etc.
// Then in a class somewhere that's used by the JSP, have the code that
// processes requests from users:
public AppropriateResultBean[] doSearch(Map<String,String> parameters)
throws SQLException, IllegalArgumentException
{
StringBuilder sql;
String columnName;
List<String> paramValues;
AppropriateResultBean[] rv;
// Start the SQL statement; again you'd probably load the prefix SQL
// from configuration somewhere rather than hard-coding it here.
sql = new StringBuilder(2000);
sql.append("select appropriate,fields from mytable where ");
// Loop through the given parameters.
// This loop assumes you don't need to preserve some sort of order
// in the params, but is easily adjusted if you do.
paramValues = new ArrayList<String>(parameters.size());
for (Map.Entry<String,String> entry : parameters.entrySet())
{
// Only process fields that aren't blank.
if (entry.getValue().length() > 0)
{
// Get the DB column name that corresponds to this form
// field name.
columnName = fieldNameToColumnName.get(entry.getKey());
// ^-- You'll probably need to prefix this with something, it's not likely to be part of this instance
if (columnName == null)
{
// Somehow, the user got an unknown field into the request
// and that got past the code calling us (perhaps the code
// calling us just used `request.getParameterMap` directly).
// We don't allow unknown fields.
throw new IllegalArgumentException(/* ... */);
}
if (paramValues.size() > 0)
{
sql.append("and ");
}
sql.append(columnName);
sql.append(" = ? ");
paramValues.add(entry.getValue());
}
}
// I'll assume no parameters is an invalid case, but you can adjust the
// below if that's not correct.
if (paramValues.size() == 0)
{
// My read of the problem being solved suggests this is not an
// exceptional condition (users frequently forget to fill things
// in), and so I'd use a flag value (null) for this case. But you
// might go with an exception (you'd know best), either way.
rv = null;
}
else
{
// Do the DB work (below)
rv = this.buildBeansFor(sql.toString(), paramValues);
}
// Done
return rv;
}
private AppropriateResultBean[] buildBeansFor(
String sql,
List<String> paramValues
)
throws SQLException
{
PreparedStatement ps = null;
Connection con = null;
int index;
AppropriateResultBean[] rv;
assert sql != null && sql.length() > 0);
assert paramValues != null && paramValues.size() > 0;
try
{
// Get a connection
con = /* ...however you get connections, whether it's JNDI or some conn pool or ... */;
// Prepare the statement
ps = con.prepareStatement(sql);
// Fill in the values
index = 0;
for (String value : paramValues)
{
ps.setString(++index, value);
}
// Execute the query
rs = ps.executeQuery();
/* ...loop through results, creating AppropriateResultBean instances
* and filling in your array/list/whatever...
*/
rv = /* ...convert the result to what we'll return */;
// Close the DB resources (you probably have utility code for this)
rs.close();
rs = null;
ps.close();
ps = null;
con.close(); // ...assuming pool overrides `close` and expects it to mean "release back to pool", most good pools do
con = null;
// Done
return rv;
}
finally
{
/* If `rs`, `ps`, or `con` is !null, we're processing an exception.
* Clean up the DB resources *without* allowing any exception to be
* thrown, as we don't want to hide the original exception.
*/
}
}
Note how we use information the user supplied us (the fields they filled in), but we didn't ever put anything they actually supplied directly in the SQL we executed, we always ran it through PreparedStatement.
The best solution is to use a middle that does data validation and binding and acts as an intermediary between the JSP and the database.
There might be a list of column names, but it's finite and countable. Let the JSP worry about making the user's selection known to the middle tier; let the middle tier bind and validate before sending it on to the database.
Here is a useful technique for this particular case, where you have a number of clauses in your WHERE but you don't know in advance which ones you need to apply.
Will your user search by title?
select id, title, author from book where title = :title
Or by author?
select id, title, author from book where author = :author
Or both?
select id, title, author from book where title = :title and author = :author
Bad enough with only 2 fields. The number of combinations (and therefore of distinct PreparedStatements) goes up exponentially with the number of conditions. True, chances are you have enough room in your PreparedStatement pool for all those combinations, and to build the clauses programatically in Java, you just need one if branch per condition. Still, it's not that pretty.
You can fix this in a neat way by simply composing a SELECT that looks the same regardless of whether each individual condition is needed.
I hardly need mention that you use a PreparedStatement as suggested by the other answers, and a NamedParameterJdbcTemplate is nice if you're using Spring.
Here it is:
select id, title, author
from book
where coalesce(:title, title) = title
and coalesce(:author, author) = author
Then you supply NULL for each unused condition. coalesce() is a function that returns its first non-null argument. Thus if you pass NULL for :title, the first clause is where coalesce(NULL, title) = title which evaluates to where title = title which, being always true, has no effect on the results.
Depending on how the optimiser handles such queries, you may take a performance hit. But probably not in a modern database.
(Though similar, this problem is not the same as the IN (?, ?, ?) clause problem where you don't know the number of values in the list, since here you do have a fixed number of possible clauses and you just need to activate/disactivate them individually.)
I'm not confident if there is a quote() method, which was widely used in PHP's PDO. This would allow you a more flexible query building approach.
Also, one of the possible ideas could be creating special class, which would process filter criterias and would save into a stack all placeholders and their values.