In the previous parts I talked about database security and the infrastructure part of database security. In this post I will discuss the development aspect of the database.
When I think about database development security, there are several things that come to my mind: the user interaction with the application, the application interaction with the database and the developers interaction with the database.
User and Application
There is a lot of information about writing secure code, and I’m not going to get too much into it here.I’ll just mention SQL injection as a major risk.
SQL injection allows the attacker to change the SQL in ways the developer didn’t intend. I’ll give a short example to those who are not familiar with the concept. In this example a developer reads a variable from the user, let’s say a customer ID and then it takes the input and concatenate it to the following query:
select * from customers where id=<input>
If this is a simple concatenation, the user might send something like this: “3 or 1=1”. That will create the following query:
select * from customers where id=3 or 1=1
Which returns the entire table. SQL injection is much more complex than that, but you get the idea. The solution for that is to validate your input as much as possible and use bind variables which don’t allow this behavior.
Application and Database
The second issue is the application access to the database. We don’t know how many bugs there are in the application and how secure it is, so our goal is to limit the risk and potential damage as much as possible. I’ve seen many cases where the application connects to the database with a user that has the DBA role. If someone is able to hack the application he can do whatever he wants. We shouldn’t allow this to happen. The application needs to have the minimum permissions it needs and not more. Let me show you how I use the “create function” privileges (that might be legit) and another privilege (one with the ANY keyword) to perform privilege escalation and get DBA access.
To do that I’ll create a function that simply run “grant dba to <user>” (<user> is the weak user I have in the database).
If I happen to have “create any index” I can use create a function based index that way:
Create index x on system.aud$(get_dba(owner));
This will cause the system user to run my function as part of the function based index, and will give me DBA privileges.
I can do the same with extended statistics (statistics on the output of a function) if I have the “analyze any” privilege:
exec dbms_stats.create_extended_statistics ('SYSTEM','AUD$','get_dba(owner)');
Developers and Database
The last thing I’m going to talk about is the developers themselves. Developers (and any person that is not responsible for the production as well, like QA) should not have access to the production database or any sensitive data. It’s not because we don’t trust them, even though sometimes we actually don’t. It’s because we want to reduce the amount of people who can access the production. When many people have access, we have less control over the database and it is more prone to human errors as well. DBAs and sysadmins make mistakes as well, but they HAVE to have access to the production. We can simply avoid developers mistakes by not granting them access that they actually don’t need.
This is why it is best to have complex passwords for the production environments that are different than the development/QA environments. And as I said before, sensitive data should be masked in non-production databases.
So this was the forth part, talking about developers and application access to the database. The production is a critical part of our environment and we need to reduce access to it as much as possible, to prevent data theft and mistakes that might make a mess. But we also need to verify that sensitive data (which should be secured in the production), does not leak into other environment that have open access and are far less protected.
The last part of this series will be about passwords. See you soon.