I'm trying to implement security on our applications (which consist mainly of websites using SQL Server stored procedures).
Microsoft seem to suggest that best practice is to wrap all data functionality in stored procedures which we have done. Our structure is something like:
SQL Server database - [data1]
SQL Server database - [webSPs]
PHP / Symfony web apps
All the data resides in tables in [data1] and on the same server is another 'database' which just contains the stored procedures used by the web apps.
The SQL Server has a login 'webapp' which, as a user on [webSPs], has permissions just to run the stored procedures in [webSPs].
However, as these stored procedures read, write, append data on the [data1] tables, this user has to be a member of db_datawriter and db_datareader database roles in [data1].
This all works fine, but it seems there is a hole in the security here, as its then possible for a user accessing [webSPs] to not only run the stored procedures but write to any underlying table - is it possible to give permission that say something like:
you can read/write to tables on this database but only through SPs - not directly
From what I've read, I think it would work as I hoped if there wasn't the second database which I guess is breaking ownership chaining.
Thank you in advance.
Yes, but it's frankly a pain to implement. You can use module signing. Essentially, you:
Create a certificate in your WebSps database.
Backup and restore the certificate to the data1 database
Create a user from the certificate
Grant whatever read and write permissions to the certificate-based user in data1
Sign any stored procedure you want to use cross-database with the certificate by calling the add signature syntax
Keep in mind that every time that you change the procedure (either via alter or drop and create), the signature gets lost. So you'll be in a constant cycle of re-signing. You can read more about the process of module signing here
Related
I am using SQL Server 2017. I am in the role of sa for the server in question. I have two databases that are used in an ETL process. The ETL is coded in one database, and the raw imported tables are located in the staging database. All ETL is handled in SQL stored procedures that follow a pattern. The first step in each ETL SP is a call to a diagnostics table in the staging database.
My current ETL job is a wrapper around two of these ETL sps; the wrapper itself contains only code that accesses the main db.
The first SP can be called and successfully selects the data from the staging db, however, the second SP that has identical code up to the point of failure with the first, fails on accessing the diagnostics table and tells me
The server principal "sa" is not able to access the database "staging" under the current security context.
The problem stays if I comment out the first SP call, so something must be different in the definition of the two SPs, but I cannot spot it.
There are plenty of SPs that use the diagnostics staging table, so it is not a general problem (as stated in answers to similar questions that suggest changing security options in the staging database), but must be related to the new SP somehow.
Any suggestions?
There are three things to check/do.
First of all, the login associated with the user in database DB1 must also be associated with a user in DB2. This provides the login with a security context in database DB2. The sa login will map to dbo in both databases, so this should already be fine.
Second, the security context of the code being executed in DB1 must be "trustworthy". In other words, when the user context goes from DB1 back up to the server level and then down into DB2 via the cross-database call, the new user context has to trust the original login. There are two ways to do this, the quick and dirty and opens-up-possible-security-holes way, and the more complicated but safer way:
Quick and not entirely safe: alter database DB1 set trustworthy on.
Safe: Use signed modules
Third, in the general case you should check that the owner of DB1 and the owner of DB2 are the same (otherwise you can't cross database ownership chain): select owner_sid from sys.databases where name in ('DB1', 'DB2') But as with the first point, as a sysadmin you can take ownership of anything.
As too often happens, I failed to recognize a subtle difference between the two stored procedures: They both call a logging stored procedure, but this logging procedure has two variants, one with prefix sp_, and another one with the prefix usp. (Someone reacted to the Microsoft warning not to use sp_ as prefix.) The old one had an 'execute as owner' inside, which caused the error.
Replacing the function call with the new version fixed the error.
Sometimes the error is on the other side of the screen.
Is there a way to lock down (preferably at the server level but DB level is also fine) tables and stored procedures? I don't want specific users creating new procedures or tables, but want them to be able to write to tables (via some Excel macros we have in place) and execute existing procedures. I would not want this to be a blanket policy, I'd prefer to specify which users this applies to.
Do you have individual ms SQL server logins for every user? Then you can set the access rights of these to Datareader and datawriter only in the security section or on database level.
If everyone uses the same login, you will have to create trigger on the data definition level. E.g. a trigger on CREATE TABLE that aborts any action.
Edit: I suggest you create a new login specifically for those excel macros.
I have been looking through old posts but am seeing some conflicting info on this. I have a SQL Server database and I am talking to it with C# code (.Net 4.5). I used stored procedures to do everything in the database and parameterising the inputs when I make a call.
I would like to lock down the database so that it won't accept SQL code and will only respond to calls to the stored procedures. Is this possible?
Although there is no setting to disallow ad-hoc SQL entirely, you could grant only EXECUTE permissions on only the stored procedures called directly by application code. Permissions on objects referenced by stored procedures are not needed as long as the ownership chain is unbroken, meaning that the stored procedure and referenced schema/objects are owned by the same user (typically dbo). This method will prevent ad-hoc access by non-privileged accounts.
Be aware ownership chaining does not apply when dynamic SQL is used with the procs. If procs have dynamic SQL (with securely built SQL and parameterized), you can sign the module with a certificate to provide the needed permissions from within the proc without granting users direct permissions on objects referenced by the dynamic SQL statement. See giving permissions through stored procedures for details and examples.
Be sure to specify CommandType.StoredProcedure on all SqlCommand objects to ensure parameters must be passed separately. Although it is possible to execute stored procedures using CommandType.Text, more attention to detail is needed to do securely.
For example
if a SQL Server user account is given
only the DataReader role and ability to execute one stored
procedure that modifies data in some
way, should the execution of that
stored procedure by that user cause
the modification to occur?
Overall, I want to give one user only read ability to the entire database including use of SQL syntax, Views and to execute any store procedures that return result sets. But I don't want any side effects to cause changes in the database. Therefore in the aforementioned stored procedure example, the attempt would ideally error out to satisfy my requirement, and all similar scenarios where a side-effect might cause a change. I want to ensure my database is protected against those.
Is this doable simply at the role level?
Product: SQL Server 2005 and up
Sure you can do this. Simply create a database role at the database level, and grant that role read on the tables and execute on only the stored procedures you want (i.e. the ones that read). Then, add the desired user(s) to your database role.
However, all things considering, if you are using stored procedures to read data, do so completely and do not grant read on tables for users of any level. Drive all data access through stored procedures (and views).
EDIT: Just noticed you said SQL 2005 "and up." If you are using SQL Server 2008, look at application roles instead of the traditional database roles.
In SQL Server 2005, a snapshot of a database can be created that allows read-only access to a database, even when the database is in "recovery pending" mode. One use case for this capability is in creating a reporting database that references a copy of a production database, which is kept current through log-shipping.
In this scenario, how can I implement security on the "snapshot" database that is different from the "production" source database?
For example, in the production database, all access to data is through stored procedures, while in the snapshot database users are allowed to select from table in the database for reporting purposes. The problem the I see is that security for the snapshot database is inherited from the source database, and can not be changed because snapshots are strictly read-only.
Are you able to manage permissions on this database? Would adding a separate user who only has read access to a database be sufficient for this type of scenario? This could be a read-only user on the main database, but is only effectively used on the snapshot db.
i.e. Add a new user, readerMan5000 who is only given select access, to the database in question. Then require users to authenticate through that new credential.
Note to future commenters, you may want to read:
http://www.simple-talk.com/sql/database-administration/sql-server-2005-snapshots/
or
http://msdn.microsoft.com/en-us/library/ms187054(SQL.90).aspx
before you open your big mouth like me. :)
You can't change permissions after you take the snapshot, but here's one workaround: instead of having them access the tables directly, require them to use views instead. If the views are used only for reporting, then you can set tight security on them in the original database, and then have the users hit those views in the snapshot. You'll need to restrict access on the underlying tables though if you want it to be effective.