Prevent sql injection, Activerecord #where, with multiple AND/OR clause - postgresql

I am fairly new to rails. I have a rails Model 'Message' with: 'belongs_to :sender' and 'belongs_to :receiver' relations.
I am trying to create a message thread between two users: 'current_user' and 'params'.
In the show controller action of the MessagesController, I want to use the equivalent of this sql query:
Message.find_by_sql(
"SELECT *
FROM messages
WHERE
(sender_id = #{current_user.id} OR sender_id = #{params[:id]})
AND
(receiver_id = #{current_user.id} OR receiver_id = #{params[:id]});"
)
If I where looking for one Message I would use this Activerecord queryto prevent SQL injection:
Message.where('sender_id = ? OR receiver_id = ?', current_user.id, current_user.id).find(params[:id])
My current query is:
Message.where(sender_id: [current_user.id, params[:id]], receiver_id: [current_user.id, params[:id]])
Is this query currently guarded against SQL injection?

it's safe. the final query would be something like sender_id IN (1, 2) AND receiver_id IN (3, 4) and all integer values are sanitized. You can simply run tests:
Message.where(sender_id: [current_user.id, "' is dangerous"], receiver_id: [current_user.id, params[:id]])
and see raw SQL output in console. illegal integers should be converted to 0.

Related

Oracle query to Postgress query change ERROR

We are in progress to move our db to Postgres from Orcale.
There is this Oracle query (changed verbiage for privacy concerns):
SELECT * FROM (SELECT somedata.* FROM SNSN.SMS_TXN_SOMEDATA somedata
WHERE ((car= 'tesla' OR car = 'teslaX' OR car = 'teslaY') OR (buyer= 'john' OR buyer = 'rony' OR jim = 'sam'))
AND code = :code
ORDER BY somedata.datetime)
WHERE LIMIT :num
When I hit the endpoint I get this error
ERROR: argument of LIMIT must be type bigint, not type character varying
What would be a suitable/alternative variable to bind this. What can I use instead of variables :code and :num.

TYPO3 DBAL Querybuilder: Nested SELECT statements?

Is it possible to build nested SELECT statements like the one below using the DBAL QueryBuilder?
SELECT i.id, i.stable_id, i.version, i.title
FROM initiatives AS i
INNER JOIN (
SELECT stable_id, MAX(version) AS max_version FROM initiatives GROUP BY stable_id
) AS tbl1
ON i.stable_id = tbl1.stable_id AND i.version = tbl1.max_version
ORDER BY i.stable_id ASC
The goal is to query an external non TYPO3 table which contains different versions of each data set. Only the data set with the highest version number should be rendered. The database looks like this:
id, stable_id, version, [rest of the data row]
stable_id is the external id of the data set. id is the internal autoincrement id. And version is also incremented automatically.
Code example:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
->select(...$this->select)
->from($this->table)
->join(
'initiatives',
$queryBuilder
->select('stable_id, MAX(version) AS max_version' )
->from('initiatives')
->groupBy('stable_id'),
'tbl1',
$queryBuilder->and(
$queryBuilder->expr()->eq(
'initiatives.stable_id',
$queryBuilder->quoteIdentifier('tbl1.stable_id')
),
$queryBuilder->expr()->eq(
'initiatives.version',
$queryBuilder->quoteIdentifier('tbl1.max_version')
)
)
)
->orderBy('stable_id', 'DESC')
I cannot figure out the correct syntax for the ON ... AND statement. Any idea?
Extbase queries have JOIN capabilities but are otherwise very limited. You could use custom SQL (see ->statement() here), though.
A better API to build complex queries is the (Doctrine DBAL) QueryBuilder, including support for JOINs, database functions like MAX() and raw expressions (->addSelectLiteral()). Make sure to read until the ExpressionBuilder where it gets interesting.
So Extbase queries are useful in order to retrieve Extbase (model) objects. It can make implicit use of its knowledge of your data structure in order to save you some code but only supports rather simple queries.
The (Doctrine DBAL) QueryBuilder fulfills all other needs. If needed, you can convert the raw data to Extbase models, too. (for example $propertyMapper->convert($data, Job::class)).
I realize that we lack clear distinguishing between the two because they were both known at some time as "QueryBuilder", but they are totally different. That's why I like to add "Doctrine" when referring to the non-Extbase one.
An example with a JOIN ON multiple criteria.
$q = TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(TYPO3\CMS\Core\Database\ConnectionPool::class)
->getQueryBuilderForTable('fe_users');
$res = $q->select('*')
->from('tt_content', 'c')
->join(
'c',
'be_users',
'bu',
$q->expr()->andX(
$q->expr()->eq(
'c.cruser_id', $q->quoteIdentifier('bu.uid')
),
$q->expr()->comparison(
'2', '=', '2'
)
)
)
->setMaxResults(5)
->execute()
->fetchAllAssociative();
Short answer: it is not possible because the table to be joined in is generated on the fly. The related expression is back-ticked and thus causes an SQL error.
But: The SQL query can be changed to the following SQL query which does basically the same:
SELECT i1.id,stable_id, version, title, p.name, l.name, s.name
FROM initiatives i1
WHERE version = (
SELECT MAX(i2.version)
FROM initiatives i2
WHERE i1.stable_id = i2.stable_id
)
ORDER BY stable_id ASC
And this can be rebuild with the DBAL queryBuilder:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
->select(...$this->select)
->from($this->table)
->where(
$queryBuilder->expr()->eq(
'initiatives.version',
'(SELECT MAX(i2.version) FROM initiatives i2 WHERE initiatives.stable_id = i2.stable_id)'
),
->orderBy('stable_id', 'DESC')
->setMaxResults( 50 )
->execute();

Add exclusion to Doctrine Query Builder in JOIN

I have built the following query with the Doctrine Query Builder in my Symfony application.
$qb->select('c')
->from('AppBundle:Course', 'c')
->join('AppBundle:Log', 'a', Expr\Join::WITH, $qb->expr()->eq('c.id', 'a.course'))
->where($qb->expr()->in('a.type', ':type'))
->andWhere($qb->expr()->between('a.time', ':start', ':end'))
->andWhere($qb->expr()->eq('c.status', ':status'))
->setParameter(':type', ['opened'])
->setParameter(':standardScratchScore', [74])
->setParameter(':status', Course::OPENED)
->setParameter(':start', $dateFrom->format('Y-m-d H:i:s'))
->setParameter(':end', $dateTo->format('Y-m-d H:i:s'))
;
In my code I iterate over the Courses and then again query the Log table to check that an entry with a specific type doesn't exist for the Course. Is there a way I can incorporate the exclusion of log.type = 'log.sent-email' for this Course into this initial query, without using something like a sub-select?
Querying the same table again within the loop feels sub-optimal to me and NewRelic suggests it is hurting the performance of my application.
Well you can always join the table one more time for this specific need:
$qb->select('c')
->from('AppBundle:Course', 'c')
->join('AppBundle:Log', 'a', Expr\Join::WITH, $qb->expr()->eq('c.id', 'a.course'))
->leftjoin(
'AppBundle:Log',
'b',
Expr\Join::WITH,
$qb->expr()->andx(
$qb->expr()->eq('c.id', 'b.course'),
$qb->expr()->eq('b.type', 'log.sent-email')
))
) // join log a second time, with the type condition
->where($qb->expr()->in('a.type', ':type'))
->andWhere($qb->expr()->between('a.time', ':start', ':end'))
->andWhere($qb->expr()->eq('c.status', ':status'))
->andWhere($qb->expr()->isNull('b.type')) // Only select records where no log record is found
->setParameter(':type', ['opened'])
->setParameter(':standardScratchScore', [74])
->setParameter(':status', Course::OPENED)
->setParameter(':start', $dateFrom->format('Y-m-d H:i:s'))
->setParameter(':end', $dateTo->format('Y-m-d H:i:s'))
;

zap tool showing security vulnerability but we can't find those vulnerability in our source code?

vulnerability showing as:-
SQL Injection - SQLite
Method: GET
Parameter: query
Attack: ' | case randomblob(10000000) when not null then "" else "" end --
Evidence: The query time is controllable using parameter value [' | case randomblob(10000000) when not null then "" else "" end --], which caused the request to take [542] milliseconds, parameter value [' | case randomblob(100000000) when not null then "" else "" end --], which caused the request to take [900] milliseconds, when the original unmodified query with value [query] took [167] milliseconds.
SQL Injection - Oracle - Time Based
Method: GET
Parameter: query
Attack: field: [query], value [query and exists (SELECT UTL_INADDR.get_host_name('10.0.0.1') from dual union SELECT UTL_INADDR.get_host_name('10.0.0.2') from dual union SELECT UTL_INADDR.get_host_name('10.0.0.3') from dual union SELECT UTL_INADDR.get_host_name('10.0.0.4') from dual union SELECT UTL_INADDR.get_host_name('10.0.0.5') from dual) -- ]
Advanced SQL Injection - Oracle AND time-based blind
Method: GET
Parameter: query
Attack: query AND 2972=DBMS_PIPE.RECEIVE_MESSAGE(CHR(113)||CHR(65)||CHR(80)||CHR(114),5)
SQL Injection - MsSQL
Method: GET
Parameter: query
Attack: query WAITFOR DELAY '0:0:15' --
SQL Injection - Hypersonic SQL - Time Based
Method: GET
Parameter: query
Attack: field: [query], value ["; select "java.lang.Thread.sleep"(15000) from INFORMATION_SCHEMA.SYSTEM_COLUMNS where TABLE_NAME = 'SYSTEM_COLUMNS' and COLUMN_NAME = 'TABLE_NAME' -- ]
SQL Injection - PostgreSQL - Time Based
Method: GET
Parameter: query
Attack: field: [query], value [case when cast(pg_sleep(15) as varchar) > '' then 0 else 1 end]
SQL Injection - MySQL
Method: GET
Parameter: query
Attack: query / sleep(15)
Advanced SQL Injection - PostgreSQL > 8.1 stacked queries (comment)
Method: GET
Parameter: query
Attack: query;SELECT PG_SLEEP(5)--
Advanced SQL Injection - Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)
Method: GET
Parameter: query
Attack: Feb 2018;SELECT DBMS_PIPE.RECEIVE_MESSAGE(CHR(105)||CHR(122)||CHR(102)||CHR(108),5) FROM DUAL--
Advanced SQL Injection - Microsoft SQL Server/Sybase time-based blind.
Method: GET
Parameter: query
Attack: query) WAITFOR DELAY CHAR(48)+CHAR(58)+CHAR(48)+CHAR(58)+CHAR(91)+CHAR(83)+CHAR(76)+CHAR(69)+CHAR(69)+CHAR(80)+CHAR(84)+CHAR(73)+CHAR(77)+CHAR(69)+CHAR(93) AND (1972=1972
All of our source code following the given Example:-
public interface UserRepository extends JpaRepository<User, Long> {
#Query("select u from User u where u.firstname = :firstname or u.lastname = :lastname")
User findByLastnameOrFirstname(#Param("lastname") String lastname,
#Param("firstname") String firstname);
}
Pick one of the timebased attacks and rerun it - you can do that by rightclicking on the alert in ZAP and selecting 'Open/Resend with Request Editor'.
Check to see how long the request took (its shown at the bottom of the dialog) - was it the same time (or a bit more) than the delay that the attack is using?
If so try increasing the delay and resending - is it now taking the longer period of time?
If the time is being affected by the time specified in the attack then you will have an SQL injection vulnerability.
Why havnt I said anything about the source code you posted? Thats because I have no idea if thats all of the relevant code :)
You might also want to try using a static analyser on your code - it will probably show loads of false positives, but you can just focus on any SQL injection vulnerabilities it reports.

Error when executing query with variables in sp_send_dbmail

I am trying to send emails individually to a list of recipients. I am receiving the error:
Msg 22050, Level 16, State 1, Line 0
Error formatting query, probably invalid parameters
Msg 14661, Level 16, State 1, Procedure sp_send_dbmail, Line 478
Query execution failed: Msg 4104, Level 16, State 1, Server xxxxxx, Line 1
The multi-part identifier "email#example.com" could not be bound.
Here is a simplified version of my code, asssuming table1 is a valid existing table and name and email are existing columns.
declare #current_mailaddress varchar(50), #query varchar(1000)
set #current_mailaddress = 'email#example.com'
set #query = 'select distinct name, email from table1
where email = ' + #current_email
exec msdb.dbo.sp_send_dbmail
#recipients = #current_email,
#subject = 'test',
#query = #query
So according to the error, the formatting (of presumably the #query) is wrong. I can't figure it out. Any ideas?
You need to put the value of #current_email in quotes:
'SELECT ... WHERE email = ''' + #current_email + ''''
To see why, consider what your query currently looks like without it:
SELECT ... WHERE email = email#example.com
Any time you work with dynamic SQL, it's a good idea to PRINT the variable for debugging if you get a strange error; it's usually the case that the SQL string you've built is not the one you're expecting. I suggested an easy way to manage debugging code in another, unrelated answer.