security context problem when accessing through synonyms - sql-server

There is a user and two databases on server (db1 and db2).
User can connect to server having default database db1 where he can exec sp.
In sp syntax we use synonyms for db2 tables under dbo scheme.
All that is done in order to allow user just connect and exec one stored procedure. It worked noraml but now The server principal "user" is not able to access the database "db2" under the current security context.
User gets output from sp when code does not touch synonyms to db2.
What should be updated? I cant grant select to user for db2 objects.

I know the question is old, but still relevant
the procedure has to have permission on the synonym object
the procedure has to have permission on the object the synonym is targeting
you have to correctly setup trustworthy database property
By default, the procedure executes under the caller account, but it can be changed with execute as clause.

Related

Do I need to grant permission on the database if I already granted it to stored procedure?

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.

How to create a login that ONLY has access to run stored procedures?

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

Sql Server Agent job failing to execute stored procedure

I have a stored procedure that I can execute in SSMS with a non domain SQL Server user.
This stored procedure selects data from tables in one database (DB1) truncates and selects into a table in DB2.
The user has datareader,datawriter and dbowner for both databases.
Problem:
When I execute the stored procedure via SS Agent with execute as the user I get the following error
The server principal [user] is not able to access the database [DB1]
under the current security context.
Actions taken So far:
I have tried to resolve this so far by:
Turning on db chaining for both databases
Deleted the user from DB1 and added again
Checked using EXEC sp_change_users_login #Action=’Report’ to see if user orphaned. As this is a database that is a restore of a live one. However I added the user after the restore. The user was not listed as orphaned
A possible workaround if you don't want to have the owner be sa is to have the user be a member of msdb and grant the the SQLAgentOperatorRole in msdb. See if that works.
But to be honest, either use sa or a dedicated service account with enough permissions. It's better if the job runs under that context.

Which privileges does a user need to query used size in SQL Azure database?

I'm trying to query the consumed size of a SQL Azure database using code from this answer:
SELECT SUM(reserved_page_count)*8.0/1024 FROM sys.dm_db_partition_stats;
That query runs just fine under the database admin, but not under another user - I get
The user does not have permission to perform this action.
and when I try to GRANT SELECT permission I get this error message:
Permissions on server scoped catalog views or system stored procedures or extended stored procedures can be granted only when the current database is master.
If I log to master and try to GRANT there I get this message:
Permissions for system stored procedures, server scoped catalog views, and extended stored procedures cannot be changed in this version of SQL Server.
So it looks like users other than database admin can't get the used space.
How do I query the used space in the SQL Azure database under a user other than database admin?
I seem to recall that we had to grant the login "VIEW DATABASE STATE" and "VIEW DEFINITION" in order to run that query.

Question on schemas and security

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.

Resources