Sql injection vulnerability check - sql

Just out of curiosity:
I know that values should not be passed to a query due to concern of SQL injection.
I have a query similar to the one below. How can this be used in terms of SQL injection?
select * from users u inner join departments d on u.id = d.user_id
where u.id = '#{name}'
So the query that is made is
select * from users u inner join departments d on u.id = d.user_id
where u.id = 'An'
That query is just an example, not a working one.

Use the following for name:
' UNION SELECT username, password /* more fields */ FROM secrettable WHERE '' = '

Related

dbGetQuery does not recognise user as database in command but as some systematic variable

I am trying to connect to the database in R using PostgreSQL. However, I have issues with quotation marks for database user - sql see user as some kind of systematic variable. Thus in the standard sql interface I would use
select count(*), email, name
from http_request_log hrl
inner join user_access_token uat on hrl.access_token_id = uat.id and uat.impersonated_by_id is null
inner join "user" u on uat.user_id = u.id
inner join customer c on u.customer_id = c.id
where hrl.created_time >= '2020-05-01'
group by u.email, c.name
order by count(*) desc
But in R, using dbGetQuery, there is an issue with quotation marks - I cannot use "user" and if I change to ' it does not work - it only works if the whole command starts and end with ' instead " - but then the where clause is not working as it does not recognize the date
uzivatele <- dbGetQuery(con,
"select count(*), email, name
from http_request_log hrl
inner join user_access_token uat on hrl.access_token_id = uat.id and uat.impersonated_by_id is null
inner join 'user' u on uat.user_id = u.id
inner join customer c on u.customer_id = c.id
where hrl.created_time >= '2020-05-01'
group by u.email, c.name
order by count(*) desc")
user is a reserved word in PostgreSQL.
One way to get around this is to prefix the table name with the schema name, e.g. public.user in your query.
Or you could try \"user\" in your query string.

SQL Where on different table

SELECT * FROM student_mentor sm INNER JOIN users u
ON sm.student_id = u.user_id
WHERE sm.teacher_id = $teacher_id
Teacher_id being the session id,
I want to see all the students that have the same mentor.
Right now if I run this I just see all of the students twice, maybe one of you knows why?
My db scheme
You are not specifying on which columns you want to do the join, so you're getting a cross reference where all records are joined to all records.
You should do something like (not sure about your column names):
SELECT * FROM student_mentor sm INNER JOIN users u
ON sm.student_id = u.user_id
WHERE sm.teacher_id = $teacher_id

SQL : transform union query to a single query

I have a database schema like this
User
id
matricule
Document
id
title
user_id(foreign key to user)
mode( can accept PUBLIC or PRIVATE)
I want to retrieve all document which are public and all documents which belongs to a given user(matricule)
I did a union query like this :
select * document d
Inner join user u ON u.id = d.user_id
and u.matricule ='matricule1'
UNION
select * from document d
Inner join user u ON u.id = d.user_id
where d.mode ='PUBLIC'
which works well but can i achieve the same result with another way( i read somewhere that union queries are bad for performance) like subquery for example ?
Thank you very much
select distinct *
from document d
Inner join user u ON u.id = d.user_id
where u.matricule = 'matricule1' or d.mode ='PUBLIC'
SELECT DISTINCT to remove duplicates just as UNION does. (Perhaps you want just SELECT?)
Assuming you just want the columns from the document table, this can also be written as:
select *
from document d
where exists (select *
from "user" u
where u.id = d.user_id
and u.matricule = 'matricule1')
or d.mode ='PUBLIC'
This makes removing duplicates unnecessary which UNION does implicitly and would be necessary for a JOIN solution.
But you have to check the execution plan for both solutions. In some cases the UNION solution might indeed be faster then the above (or a JOIN). This depends heavily on the DBMS being used (e.g. for Postgres or Oracle I wouldn't expect a big difference at all in this case)

SQL Query trouble

I am trying to write an SQL query to retrieve all of a users pending events, however it is difficult with how my tables are structured.
My tables are as follows :
event {
event_id
name
group_id}
Pending {
GroupID
UserID
}
Users{
Username
UserID
}
Ever user is identified by a UserID, and every group by a GroupID. Events have in them a GroupID which points to a list of users. I need to retrieve all pending events for a certain user, so :
SELECT * FROM event
WHERE event.group_id = (SELECT GroupID FROM Pending)
But how do I then link this so only the Pending events for a user with a certain UserID are returned?
select e.* from event e
inner join pending p on
e.group_id = p.GroupID
inner join Users u
on p.UserID = u.UserID
where u.UserID = 123
actually you can skip the join with the Users-table if you already have the UserID:
select e.* from event e
inner join pending p on
e.group_id = p.GroupID
where p.UserID = 123
The typical way to write this is using inner join. Traditionally it has been better performing than sub selects, but modern DBMS:s optimize them into the same query. If you really want to write with a sub select you type like this
SELECT * FROM event
WHERE event.group_id in (SELECT GroupID FROM Pending WHERE UserID = 123)
You want to use the JOIN clause to link the tables together rather than using the WHERE clause to filter
SELECT u.username,
e.name
FROM users u
INNER JOIN pending p
ON u.userid = p.userid
INNER JOIN event e
ON p.groupid = e.groupid
WHERE
u.UserID = SomeID
SELECT
event.event_id
FROM event
LEFT JOIN Pending ON event.group_id = Pending.GroupID
LEFT JOIN Users USING (UserID)
WHERE Users.Username = 'foobar'
USING() can be utilized when columns on both sides of selection have the same name. It makes for easier reading. That is one of main reason why i would recommend to have same name for same data throughout the database. For example, if you Documents table and primary key document_id, then in all the other tables, where you are referencing the ID of a document, you use the same name for the column.
To learn more about JOINs : in mysql or postgresql read the links. And for visual representation of what each join does: this article.
Also you should get some book about your preferred RDBMS and learn all the basics, then you can expand your knowledge by reading SQL Antipatterns book. Or you could just carefully look through slides, made by book's author.
You can try :
select e.*, u.* from pending as p, users as u, event as e
WHERE
p.group_id = e.group_id
AND p.user_id = u.userID
SELECT * FROM event E,
pending P,
Users U
WHERE E.group_id = P.GroupID
AND P.UserID = U.UserID
AND U.UserID = (Some XYZ id)
XYZ should be some Integer value.

How do I condense this SQL of multiple sub-selects into something more sane?

The following query returns a single row, as desired. The 'contracts' table has 6 fields, each with a different username in it, for which I want to retrieve first/last names from a separate 'users' table. This works fine, but is there something more concise? I'm think the solution must be something using GROUP BY contracts.id to keep it one row, but I can't seem to find anything better than this slew of sub-selects.
Help!
SELECT contracts.field1, contracts.field2,
(SELECT first_name FROM users WHERE username = service_provider_1),
(SELECT last_name FROM users WHERE username = service_provider_1),
(SELECT first_name FROM users WHERE username = service_provider_2),
(SELECT last_name FROM users WHERE username = service_provider_2),
(SELECT first_name FROM users WHERE username = service_org_business_contact),
(SELECT last_name FROM users WHERE username = service_org_business_contact),
(SELECT first_name FROM users WHERE username = client_service_contact_1),
(SELECT last_name FROM users WHERE username = client_service_contact_1),
(SELECT first_name FROM users WHERE username = client_service_contact_2),
(SELECT last_name FROM users WHERE username = client_service_contact_2),
(SELECT first_name FROM users WHERE username = client_business_contact),
(SELECT last_name FROM users WHERE username = client_business_contact)
FROM contracts
WHERE id = ?
It wouldn't be so bad if I could get both first/last name from a single sub-select... so even with a cruddy sub-select solution I have twice the verbosity I should think I need...
EDIT: I get it now. The answer to being able to join to the same table multiple times is to use an alias for the table. Thank folks! New code is:
SELECT contracts.field1, contracts.field2,
sp1.first_name, sp1.last_name,
sp2.first_name, sp2.last_name,
sobc.first_name, sobc.last_name,
csc1.first_name, csc1.last_name,
csc2.first_name, csc2.last_name,
cbc.first_name, cbc.last_name
FROM contracts
JOIN users AS sp1 ON service_provider_1 = sp1.username
JOIN users AS sp2 ON service_provider_2 = sp2.username
JOIN users AS sobc ON service_org_business_contact = sobc.username
JOIN users AS csc1 ON client_service_contact_1 = csc1.username
JOIN users AS csc2 ON client_service_contact_2 = csc2.username
JOIN users AS cbc ON client_business_contact = cbc.username
WHERE contracts.id = ?
Sadly, using joins is almost as verbose as using the subselects, but I assume it might be faster?
Why not join to the users table 6 times?
SELECT contracts.field1, contracts.field2,
sp1.first_name,
sp1.last_name
sp2.first_name,
sp2.last_name,
/* etc, etc */
FROM
contracts
INNER JOIN
users sp1
ON
contracts.id = sp1.id
AND sp1.username = service_provider_1
INNER JOIN
users sp2
ON contracts.id = sp2.id
AND sp2.username = service_provider_2
INNER JOIN
users sobc
ON contracts.id = sobc.id
AND sobc.username = service_org_business_contact
INNER JOIN
/* etc, etc */
WHERE contracts.id = #myid
did you also want to combine first_name and last_name for each username? You can do this like
RTRIM(sp1.first_name) + ' ' + RTRIM(sp1.last_name) as sp1_name
in your SELECT clause. The RTRIM are necessary if the data type is (N)CHAR, not necessary if the type is (N)VARCHAR
EDIT: As stated in the comments on this answer, the JOIN on id is probably not necessary, in which case it becomes
SELECT
contracts.field1,
contracts.field2,
sp1.first_name,
sp1.last_name
sp2.first_name,
sp2.last_name,
/* etc, etc */
FROM
contracts
INNER JOIN
users sp1
ON
sp1.username = service_provider_1
INNER JOIN
users sp2
ON
sp2.username = service_provider_2
INNER JOIN
users sobc
ON
sobc.username = service_org_business_contact
INNER JOIN
/* etc, etc */
WHERE contracts.id = #myid
My layout probably makes it appear longer! You may need to use LEFT OUTER JOINS if it is possible to have a contract record that doesn't have a first_name and last_name for one of it's fields within the users table.
select
c.field1,c.field2
,SP1.first_name ,SP1.last_name
,SP2.first_name ,SP2.last_name
,SOBC.first_name,SOBC.last_name
,CSC1.first_name,CSC1.last_name
,CSC2.first_name,CSC2.last_name
,CBC.first_name ,CBC.last_name
from contracts C
left join users as SP1 on SP1.Username = C.service_provider_1
left join users as SP2 on SP2.Username = C.service_provider_2
left join users as SOBC on SOBC.Username = C.service_org_business_contact
left join users as CSC1 on SP1.Username = C.client_service_contact_1
left join users as CSC2 on SP1.Username = C.client_service_contact_2
left join users as CBC on CBC.Username = C.client_business_contact
where c.ID = ?
I can't help but think that pushing the names out to a separate table along with a column for the name type might be a good idea here.
Edit: Or have another join table sitting between contracts and users to do a many-to-many join properly.