I need to set some users up to be able to create stored procedures with READ access only. Also, in production they cannot have SELECT, only EXECUTE on the procs they created. In development they would have SELECT so they could create their procedures.
I've set up a schema called Reports. The owner of that schema is a login - Report_Admin. That user has select access to tables. I then gave alter and execute on the Reports schema to my report writer account. Dbo owns the table - so it works if dbo also owns the Reporting schema - but then a delete will also work in the procedure!
We are using reporting services and would like to have all the SQL in the database for maintainability.
Thanks!
You're not going to succeed using ownership chaining, as you already discovered. A solution would be like this: report_writer must create its reports with an EXECUTE AS SELF clause so they get executed under the report_writer priviledges. Then the report_reader group will be able to leverage the EXECUTE permission on reports schema to execute said reports, and the reports will be able to read the data because of the execute as clause.
You can assign permissions to roles and users, no need for separate schema's. So I'd only use one schema: "dbo" (the default)
Create a database role for the users. Grant data_reader to that role on development. On both development and production, grant execute rights on the stored procedures. As far as I know, you'll have to grant the execute right for each stored procedure.
Related
I have a SQL Server database (let's call it DB1) with a few stored procedures, and these stored procedures are executed by my Web API (that will be consumed by my mobile app later).
These stored procedures get data from DB1 and from another database (let's call it DB2).
I created a SQL Server login to be used by the Web API with public and dbcreator server roles and only public database role in both DB1 and DB2.
Then I followed this guide and granted Execute permission on the stored procedures for the login.
But when I try to execute my Web API method that uses one of the procedures, I get an exception:
The SELECT permission was denied on the object 'APP_USERS', database 'DB2', schema 'dbo'.
So, do I have to grant the permissions on DB1 and DB2 for this to work, even if I already granted it on the stored procedure? Or am I just granting the wrong permission to the stored procedure?
Note: I used to grant permissions directly on the database for each login, because all applications consulting the database were internal of our enterprise (until now). But this mobile app will be public. I talked to a security expert who told me that this practice is insecure, and advised me to grant the permissions only on the stored procedures.
It looks like you've been introduced (whether you wanted to be or not!) to something called database permissions chaining. At a high level, you're allowed to have objects in your own database reference other objects and only have to grant permissions on the referencing object so long as both the referenced and referencing object are owned by the same database principal (i.e. user). For example, if I have a table that I own, I can write a stored proc doing whatever (say a SELECT) against the table and then grant execute on the proc to another user. When the other user goes to execute the proc, permission chaining kicks in and says "the proc and the table are owned by the same user - the execute permission is sufficient"
But! By default, the permissions chain is broken when the referenced object is in another database. Why? I can only speculate as to creators' intent, but imagine a multi-hosted database server and I'm an malicious actor. If I have my own database, I could write a proc that says select * from OtherDb.dbo.Users;, grant permissions on that proc and exfiltrate data from other users' databases.
There are a couple of ways around this:
You can enable cross db ownership chaining at the server level. I don't recommend this, but it is an easy button out of the problem you have.
You can grant permissions on the objects referenced in the procedure. This may be okay, depending on why you're gating data access through stored procedures (which, full disclosure, I like to do in general). This would be a simple grant select on dbo.APP_USERS to «some DB2 principal - a user or group»;. The downside here is that the principal to whom the permissions are granted can do any select on the table now, thereby bypassing the proc.
You can sign your stored procedures. This is a little more involved, but is the more secure option. It involves creating a certificate or asymmetric key in both databases, creating a user based on the same, granting permissions to that user, and finally calling add signature on the related procs. You'd think you're done, but you'll need to re-apply that signature any time someone changes the procedure definition. Why? Let's say that you sign the proc today but then I change it to do something unintended (either innocently or maliciously). If the signature persisted through an alter procedure, the original proc could be a Trojan horse.
Here is a rough sketch of the module signing dance.
use Db1;
create certificate ModuleSigningCert ...;
add signature to dbo.YourProc by certificate ModuleSigningCert;
use Db2;
-- import ModuleSigningCert - either by backup certificate/create certificate
-- you technically only need the public key portion
create user SigningUser from certificate ModuleSigningCert;
grant select on dbo.YourTable to SigningUser;
For what it's worth, I don't know that "database will be accessed by a public app" necessarily means "and now we need to do cross-database stuff". It may, but it may not. For instance, if the public app still accesses the database through an internal application server, you're not getting much security-wise with the multi-database setup.
In order to keep my database's data integrity, I am trying to unauthorize all the queries that aren't calls to a stored procedure. But I don't want queries that I run directly from SQL Server to be blocked because then I will be unable to make any changes to the database (If the database cannot differentiate between "inside" queries and "outside" queries, maybe queries will be allowed under a particular authorization). How can I do such a thing?
You need to create a custom application role in your database that can only execute those stored procedures - but doesn't have any permissions to read or update data directly.
The simplest approach I usually take is this:
Create a custom database role db_executor which works similar to db_datareader
Grant this role the permission to execute all current and future stored procedures
Create users that only get this role - these users can connect to your database and they can only execute stored procedures - nothing else
Step 1 - create new role
CREATE ROLE [db_executor]
Step 2 - grant permissions
GRANT EXECUTE TO db_executor;
This grants the permission to execute any stored procedures in this database - and the great thing is: this doesn't apply to just the stored procedures that exist at the time you run this - this will also include any stored procedures you might create in the future
Step 3 - add user(s) to this role
You can do this in the GUI of course - or using this T-SQL:
ALTER ROLE [db_executor] ADD MEMBER [your-user-name-here]
I have a C# Winform application that interacts with an SQL Server DB via stored procedures. In SSMS, how do I create a login that ONLY has permissions to run stored procedures? That login wouldn't be able to view/edit/create table definitions, etc. It would also only have access to a single specified DB.
The reason I want to create such a login is because I store the SQL Server credentials used by my Winform application in its App.config file. Since the app.config can easily be read, anyone with malicious intent can easily perform unwanted operations on the database if the given login had any other permissions than just stored procedures.
A neat trick in this scenario is to create a separate (custom) SQL Server role that can only execute stored procedures:
CREATE ROLE db_executor;
GRANT EXECUTE TO db_executor;
This role now has the permission to execute any stored procedure in the database in which it's been created - and in addition: that permission will also extend to any future stored procedures you might create later on in this database.
Now create a user in your database and give it only this database role - this user will only be able to execute stored procedures - any and all of them in your database.
If you user should be allowed to execute any and all stored procedures - this is a very convenient way to allow this (and you don't have to constantly update the permissions when new stored procedures are created).
You can use the following query in order to allow stored procedure execute permision to your user
USE [DB]
GRANT EXECUTE ON dbo.procname TO username;
However, in my humble opinion , you should secure the connection string in the app.config.
Maybe , this How to store login details securely in the application config file link can be helped to you.
The access to a specific database is done through creating a user on the database that you want him to operate on. You can find more infos about users here.
If the user is created you can Grant, With Grant and Deny actions for every single item on the database.
The user will then be granted/denied those rights by a grantor, which is the dbo by default.
You can use this to also deny him access to every item on your database that isn't your stored procedure, which is what you're looking for if I understand you correctly.
Try folloiwng approach (grant execute should be repeated for every SP). Note that MyStoredProcedure has to be in MyDatabase :)
-- create login to server
create login test_user with password = 'test';
-- create user mapped to a database
use MyDatabase
go
create user test_user for login test_user;
-- grant permission to execute SP
grant execute on MyStoredProcedure to test_user
I just finished creating a new user for my database in SQL Server. I had 4 tables I wanted to grant Insert, Update, Select and delete permissions. I did this manually in the Securables area of the new user.
Is there a better way to do this that to have to touch each object? if so, how?
Thanks,
rod.
One way is use schemas such that
tables belong to a schema (let's call it data, CREATE SCHEMA)
users belong to a role (CREATE ROLE, sp_addrolemember)
permissions are assigned to the role on the schema (GRANT INSERT ON schema::data to myRole)
Now, you can add new tables or change users without losing/creating permissions
If you want finely granular control over who can do what, I don't think there's a whole lot you can do - you're doing it just fine.
gbn's approach is quite nifty - another approach for "simple" setups (when you don't need a whole lot of different permissions) is to:
grant every user (or a role) the database role db_datareader - this allows read access
(SELECT) on all tables and views
grant every user (or a role) the database role db_datawriter - this allows write access (INSERT, UPDATE, DELETE) on all tables and views
If you also need to grant execution rights on stored procedures, there's unfortunately no predefined role to use. You can however create your own database role and then grant execute permissions to that role. The great thing is: this permission to execute stored procedures also applies to all future stored procedure you might create in your database!
To define your new role, use this:
CREATE ROLE db_executor
GRANT EXECUTE TO [db_executor]
and then you can just assign db_executor to those users who need to be able to execute stored procs and stored functions in your database.
FYI: SQL Server 2005
I have a database user account (user_web) that has the ability to connect to and run queries and stored procedures in my database. Specifically, I have given the user the db_datareader and db_datawriter roles as well as granted them execute permission on the specific stored procedures it needs to be able to run.
In one of the stored procedures, I need to disable a trigger then re-enable it after some specific edits are done. When I attempt to run that stored procedure with the user I get the following error:
Cannot find the object "TableName" because it does not exist or you do not have permissions.
TableName is the table where I am attempting to disable and enable the trigger. My question is what is the least amount of permissions I can give to my user account that will allow it to successfully run the stored procedure.
The user will "at a minimum" require ALTER permissions on the table in question. See: http://technet.microsoft.com/en-us/library/ms182706.aspx
Rather than grant the user ALTER permissions on the table, which could be a security issue, I'd have that particular stored procedure run as a different user that does have those permissions. Use the EXECUTE AS syntax to accomplish this.
http://msdn.microsoft.com/en-us/library/ms188354.aspx