What would be the correct way to write this query?
String squery= "update Room set GuestCode="+gc+", FirstName=(select FirstName from GuestDetails where GuestCode="+gc+"), LastName=(select LastName from GuestDetails where GuestCode="+gc+"), Country=(select Country from GuestDetails where GuestCode="+gc+"), State=(select State from GuestDetails where GuestCode="+gc+"), City=(select City from GuestDetails where GuestCode="+gc+"), ContactNo=(select ContactNo from GuestDetails where GuestCode="+gc+") where RoomNo="+rn+"";
i am trying to set some values in a table(Room) from another table(GuestDetails) with guestcode as input. I am getting an exception as invalid memo, ole, or hyperlink object in subquery. Please help.
This may be a Better way of writing your update. This works in sql server
UPDATE A
SET GuestCode = 'gc',
FirstName = B.FirstName,
LastName = B.LastName,
Country = Country,
State = B.State,
City = B.City,
ContactNo = B.contactNO
FROM ROOM A
JOIN GuestDetails B
ON b.GuestCode = 'gc'
WHERE RoomNo = 'rn';
Its better to use a stored procedure to avoid sql injection. Your code is vers vulnerable for injection.
Also use a join to avoid sub-selects due to performance issues.
Related
I am having code something like this.
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
Calculation of fullTableName is something like:
public String getFullTableName(final String table) {
if (this.schemaDB != null) {
return this.schemaDB + "." + table;
}
return table;
}
Here schemaDB is the name of the environment(which can be changed over time) and table is the table name(which will be fixed).
Value for schemaDB is coming from an XML file which makes the query vulnerable to SQL injection.
Query: I am not sure how the table name can be used as a prepared statement(like the name used in this example), which is the 100% security measure against SQL injection.
Could anyone please suggest me, what could be the possible approach to deal with this?
Note: We can be migrated to DB2 in future so the solution should compatible with both Oracle and DB2(and if possible database independent).
JDBC, sort of unfortunately, does not allow you to make the table name a bound variable inside statements. (It has its reasons for this).
So you can not write, or achieve this kind of functionnality :
connection.prepareStatement("SELECT * FROM ? where id=?", "TUSERS", 123);
And have TUSER be bound to the table name of the statement.
Therefore, your only safe way forward is to validate the user input. The safest way, though, is not to validate it and allow user-input go through the DB, because from a security point of view, you can always count on a user being smarter than your validation.
Never trust a dynamic, user generated String, concatenated inside your statement.
So what is a safe validation pattern ?
Pattern 1 : prebuild safe queries
1) Create all your valid statements once and for all, in code.
Map<String, String> statementByTableName = new HashMap<>();
statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
If need be, this creation itself can be made dynamic, with a select * from ALL_TABLES; statement. ALL_TABLES will return all the tables your SQL user has access to, and you can also get the table name, and schema name from this.
2) Select the statement inside the map
String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);
See how the unsafeUserContent variable never reaches the DB.
3) Make some kind of policy, or unit test, that checks that all you statementByTableName are valid against your schemas for future evolutions of it, and that no table is missing.
Pattern 2 : double check
You can 1) validate that the user input is indeed a table name, using an injection free query (I'm typing pseudo sql code here, you'd have to adapt it to make it work cause I have no Oracle instance to actually check it works) :
select * FROM
(select schema_name || '.' || table_name as fullName FROM all_tables)
WHERE fullName = ?
And bind your fullName as a prepared statement variable here. If you have a result, then it is a valid table name. Then you can use this result to build a safe query.
Pattern 3
It's sort of a mix between 1 and 2.
You create a table that is named, e.g., "TABLES_ALLOWED_FOR_DELETION", and you statically populate it with all tables that are fit for deletion.
Then you make your validation step be
conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
If this has a result, then you execute the safe_table_name. For extra safety, this table should not be writable by the standard application user.
I somehow feel the first pattern is better.
You can avoid attack by checking your table name using regular expression:
if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
}
It's impossible to inject SQL using such a restricted set of characters.
Also, we can escape any quotes from table name, and safely add it to our query:
fullTableName = StringEscapeUtils.escapeSql(fullTableName);
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
StringEscapeUtils comes with Apache's commons-lang library.
I think that the best approach is to create a set of possible table names and check for existance in this set before creating query.
Set<String> validTables=.... // prepare this set yourself
if(validTables.contains(fullTableName))
{
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
//and so on
}else{
// ooooh you nasty haker!
}
create table MYTAB(n number);
insert into MYTAB values(10);
commit;
select * from mytab;
N
10
create table TABS2DEL(tname varchar2(32));
insert into TABS2DEL values('MYTAB');
commit;
select * from TABS2DEL;
TNAME
MYTAB
create or replace procedure deltab(v in varchar2)
is
LvSQL varchar2(32767);
LvChk number;
begin
LvChk := 0;
begin
select count(1)
into LvChk
from TABS2DEL
where tname = v;
if LvChk = 0 then
raise_application_error(-20001, 'Input table name '||v||' is not a valid table name');
end if;
exception when others
then raise;
end;
LvSQL := 'delete from '||v||' where n = 10';
execute immediate LvSQL;
commit;
end deltab;
begin
deltab('MYTAB');
end;
select * from mytab;
no rows found
begin
deltab('InvalidTableName');
end;
ORA-20001: Input table name InvalidTableName is not a valid table name ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 21
ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 16
ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721
Have an application that has a table with a date column set as unique. Unique is set so that there will always only be one record per day in the table.
The following code updates the information in the table when run for the x'th time during the day:
else{
StringBuilder query = new StringBuilder("from Weather where COLUMN_DATE = '" + weatherData.getDate() + "'");
List weatherDat = session.createQuery(query.toString()).list();
if (weatherDat.size()>0) {
Weather curWeather = (Weather) weatherDat.get(0);
curWeather.setWeatherId(weatherData.getWeatherId());
curWeather.setDescription(weatherData.getDescription());
curWeather.setHumidity(weatherData.getHumidity());
curWeather.setPressure(weatherData.getPressure());
curWeather.setTemperature(weatherData.getTemperature());
curWeather.setWindDirection(weatherData.getWindDirection());
curWeather.setWindSpeed(weatherData.getWindSpeed());
}
Where the code before the else checks the table for an existing record for the day and weatherData is a object that contains data collected. Is there a better, less verbose way of updating the daily record?
Use uniqueResult instead of list
Check out the method uniqueResult() here: https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/Query.html#uniqueResult%28%29
Then you can do the following:
Weather curWeather = (Weather) session.createQuery(query.toString()).uniqueResult();
Side Note
Trusting weatherData.getDate() is vulnerable to SQL injection attacks. Instead use named query parameters. These are tokens of the form :name in the query string. A value is bound to the parameter :weatherDate by calling
session.createQuery("from Weather where COLUMN_DATE = :weatherDate")
.setParameter("weatherDate", weatherData.getDate());
I have two tables made in access. One table (Owner) contains: ownerID, name which owner has. Second table (Cars) contains: CarId, carname, year, ownerID they has relations between carid
In my java program I get from first table OwnerName and put them all into comboBox1
String sql="SELECT * FROM Owner ;";
ResultSet dane = zadanie.executeQuery(sql);
while(dane.next()) {
String OwnerId = dane.getString("OwnerID");
String OwnerName = dane.getString("OwnerName");
if (OwnerId != null) {OwnerId = OwnerId.trim();}
if (OwnerName != null) {OwnerName = OwnerName.trim();}
comboBox.addItem(OwnerId);
comboBox_1.addItem(OwnerName);
}
When I choose owner I want to have in combobox2 only these cars that have this owner.
Can someone suggest a solution?
I don't know exactly how to write the SQL statement to get that.
select tablename.carname from tablename where ownerID=SelectedOwnerID
To get selected owner id you can, for example, create a map Map<Integer,Integer> and store pairs ComboboxItemNumber -> OwnerId
i have two tables "Table1" with columns user_name,Password and course ID and another table "course" with columns course_id,course_name.I have used the following code to display the course ID from Table1 according to the user_name received from the login page.using ResultSet rs1.now i want to retrieve the course_name from the table "course" according to the course ID receieve from "Table1".for that in the second query pstmt2.setString(1, ); what parameter i should use to get the course_id value from the previous query
HttpSession sess=request.getSession();
String a=(String)sess.getAttribute("user");
String b=(String)sess.getAttribute("pass");
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con=DriverManager.getConnection("jdbc:odbc:ggg");
Statement st = con.createStatement();
String query="select * from Table1 where user_name=?";
PreparedStatement pstmt=con.prepareStatement(query);
pstmt.setString(1,a);
ResultSet rs1=pstmt.executeQuery();
while(rs1.next())
out.println("<h3>COURSE ID: "+rs1.getString("course ID")+"<h3>");
String query2="SELECT * from course where course_id=?";
PreparedStatement pstmt2=con.prepareStatement(query2);
pstmt2.setString(1,);
ResultSet rs2=pstmt2.executeQuery();
while(rs2.next())
{
out.println("<h3>course name: "+rs2.getString("course_name")+"<h3>");
}
why do you go for two turns of database hit, even though you created one time connection object.
modify the query as below
SELECT * from course where course_id = (select course_id from Table1 where user_name=?);
from this query you noneed to give input of courseid also.
No need to hit database twice to get the results that you need. use the query
Select table1.course_id, course.course_name from table1, course where table1.course_id=course_id and table1.user_name=?
This should set the course_id parameter:
pstmt2.setString(1,rs1.getString("course_id"));
Or, as I see the "course_id" column may have a different name in "Table1":
pstmt2.setString(1,rs1.getString("course ID"));
As the other post mentioned there's no need to go to another set of query. Try this example query:
SELECT course.course_id, course.course_name
FROM table1 t1
INNER JOIN course c
ON t1.course_id = c.course_id
WHERE t1.user_name = ?;
Now if you insist your coding the parameter o your pstmt2.setString(1,); is:
pstmt2.setString(1,rs1.getString("course_id")); //or course ID defending on your column name
Oracle keeps giving me an invalid identifier error when I clearly have identified the variable.
//get parameters from the request
String custID=request.getParameter("cust_ID");
String saleID=request.getParameter("sale_ID");
String firstName=request.getParameter("first_Name");
String mInitial=request.getParameter("mI");
String lastName=request.getParameter("last_Name");
String streetName=request.getParameter("street");
String city=request.getParameter("city");
String state=request.getParameter("state");
String zipCode=request.getParameter("zip_Code");
String DOB2=request.getParameter("DOB");
String agentID=request.getParameter("agent_ID");
String homePhone=request.getParameter("home_Phone");
String cellPhone=request.getParameter("cell_Phone");
String profession=request.getParameter("profession");
String employer=request.getParameter("employer");
String referrer=request.getParameter("referrer");
query =
"UPDATE customer"
+ " SET customer.cust_ID=custID, customer.sale_ID=saleID, customer.first_Name=firstName, customer.mI=mInitial, customer.last_Name=lastName, customer.street_Name=streetName, customer.city=city, customer.state=state, customer.zip_Code=zipCode,customer. DOB=DOB2, customer.agent_ID=agentID, customer.home_Phone=homePhone, customer.cell_Phone=cellPhone, customer.profession=profession, customer.employer=employer, customer.referrer=referrer"
+ " WHERE customer.cust_ID=custID " ;
preparedStatement = conn.prepareStatement(query);
preparedStatement.executeUpdate();
SQL TABLE
CREATE TABLE customer
(cust_ID NUMBER NOT NULL,
sale_ID NUMBER NOT NULL,
first_NameVARCHAR2(30) NOT NULL,
mI VARCHAR2(2) ,
last_Name VARCHAR2(50) NOT NULL,
street_Name VARCHAR2(50) ,
city VARCHAR2(30) NOT NULL,
state VARCHAR2(50) NOT NULL,
zip_Code VARCHAR2(5) NOT NULL,
DOB DATE ,
agent_ID NUMBER ,
home_Phone VARCHAR2(12) UNIQUE,
cell_Phone VARCHAR2(12) UNIQUE,
profession VARCHAR2(30) ,
employer VARCHAR2(30) ,
referrer VARCHAR2(30)
);
Your code is not doing what you think it is. Look at this:
query =
"UPDATE customer"
+ " SET customer.cust_ID=custID, customer.sale_ID=saleID, customer.first_Name=firstName, customer.mI=mInitial, customer.last_Name=lastName, customer.street_Name=streetName, customer.city=city, customer.state=state, customer.zip_Code=zipCode,customer. DOB=DOB2, customer.agent_ID=agentID, customer.home_Phone=homePhone, customer.cell_Phone=cellPhone, customer.profession=profession, customer.employer=employer, customer.referrer=referrer"
+ " WHERE customer.cust_ID=custID "
The content of query at this point is exactly what will be sent to the database. JSP will not magically fill in custID, saleID (etc...) for you before sending the query to the database. Because of this, Oracle has no sweet clue what custID is (it certainly isn't the name of some other column in the customer table). Hence, you receive the invalid identifier error.
I think you were trying to do this:
query =
"UPDATE customer"
+ " SET customer.cust_ID=" + custID + ", customer.sale_ID=" + saleID + ...
Like duffymo mentioned, this is asking for serious SQL-injection trouble (just think of the values that the client could submit in order to hijack your SQL via the custID field). The better way is to use parameters on a PreparedStatement:
query =
"UPDATE customer"
+ " SET customer.cust_ID=?, customer.sale_ID=? ...";
PreparedStatement statement = conn.prepareStatement(query);
statement.setString(1, custID);
statement.setString(2, saleID);
statement.executeUpdate();
I'd recommend not using scriplets in your JSPs. Learn JSTL as quickly as you can.
The answer seems pretty obvious: your parameters are all Strings, but the Oracle schema has some Data and Number types. You've got to convert to the correct type when you INSERT.
This code is begging for a SQL injection attack. You don't do any binding or validation before you INSERT. You couldn't possibly be less secure than this. I hope you don't intend to use this site for anything on the web.
A better approach would take the scriptlet code out of the JSP, use only JSTL to write it, and introduce a servlet and some other layers to help with binding, validation, security, etc.
I think in the sql query you have entered space in between customer,DOB.
customer. DOB=DOB2