25

Inspirado por diversas cuestiones relacionadas con el esquema que he visto ...Servidor SQL: ¿cómo permisos de esquemas?

Ownership chaining me permite el privilegio GRANT EXECUTE en un procedimiento almacenado sin permisos explícitos en las mesas que uso, tanto si el procedimiento almacenado y las tablas están en el mismo esquema.

Si utilizamos esquemas separados, entonces tendría que CONCEDER explícitamente XXX en las tablas de diferentes esquemas. El ejemplo de encadenamiento de propiedad demuestra eso. Esto significa que el usuario que ejecuta el proceso almacenado puede leer/escribir sus tablas directamente.

Esto sería como tener acceso directo a las variables de su instancia en una clase, evitando getter/setters, rompiendo la encapsulación.

También utilizamos seguridad a nivel de fila para restringir lo que alguien ve y aplicamos esto en los procedimientos almacenados.

Entonces, ¿cómo podemos mantener la separación del esquema y evitar el acceso directo a la tabla?

Por supuesto, la pregunta no se aplicará si usa un ORM o no utiliza procs almacenados. Pero no estoy preguntando si debería utilizar un ORM o procedimiento almacenado en caso de que alguien se siente la necesidad de que me ilumine ...

Editar, ejemplo

CREATE USER OwnsMultiSchema WITHOUT LOGIN 
GO 
CREATE SCHEMA MultiSchema1 AUTHORIZATION OwnsMultiSchema 
GO 
CREATE SCHEMA MultiSchema2 AUTHORIZATION OwnsMultiSchema 
GO 

CREATE USER OwnsOtherSchema WITHOUT LOGIN 
GO 
CREATE SCHEMA OtherSchema AUTHORIZATION OwnsOtherSchema 
GO 

CREATE TABLE MultiSchema1.T1 (foo int) 
GO 
CREATE TABLE MultiSchema2.T2 (foo int) 
GO 
CREATE TABLE OtherSchema.TA (foo int) 
GO 

CREATE PROC MultiSchema1.P1 
AS 
SELECT * FROM MultiSchema1.T1 
SELECT * FROM MultiSchema2.T2 
SELECT * FROM OtherSchema.TA 
Go 
EXEC AS USER = 'OwnsMultiSchema' 
GO 
--gives error on OtherSchema 
EXEC MultiSchema1.P1 
GO 
REVERT 
GO 

CREATE PROC OtherSchema.PA 
AS 
SELECT * FROM MultiSchema1.T1 
SELECT * FROM MultiSchema2.T2 
SELECT * FROM OtherSchema.TA 
Go 
GRANT EXEC ON OtherSchema.PA TO OwnsMultiSchema 
GO 
EXEC AS USER = 'OwnsMultiSchema' 
GO 
--works 
EXEC OtherSchema.PA 
GO 
REVERT 
GO 

Edición 2:

  • no utilizamos "encadenamiento de propiedad entre bases de datos"
  • seguridad a nivel de
  • Fila es una pista falsa e irrelevante: no usamos todas partes
+2

¿Sería posible proporcionar un ejemplo codificado del escenario de esquema separado que está describiendo para mayor claridad, por favor? En su escenario, ¿los dos esquemas separados tienen el mismo propietario? –

+0

¿Por qué votar cerrar para serverfault? Es para los monos de código, no para los administradores de sistemas ... – gbn

+0

@John Sansom: sí, lo haré. – gbn

Respuesta

21

Temo que sea su descripción o su concepción del encadenamiento de propiedad no está clara, así que vamos a empezar con eso:

"encadenamiento de propiedad" se refiere simplemente al hecho de que cuando se ejecuta un procedimiento almacenado (o vista) en SQL Server, el lote que se está ejecutando actualmente adquiere los derechos/permisos del propietario del sProc (o el propietario del esquema de sProc) mientras ejecuta ese código SQL. Entonces, en el caso de un sProc, el usuario no puede usar esos privs para hacer algo que el código de sProc no implemente para ellos. Tenga en cuenta especialmente que nunca adquiere Identidad del Propietario, solo sus derechos, temporalmente (sin embargo, EJECUTAR COMO ... lo hace).

lo tanto, el enfoque típico para aprovechar esto para la seguridad es:

  1. poner todas las tablas de datos (y todas las vistas no son de seguridad también) en su propio esquema, vamos a llamarlo [datos ] (aunque típicamente [dbo] se usa porque ya está allí y es demasiado privilegiado para el esquema del Usuario). Asegúrese de que ningún usuario, esquema o propietario existente tenga acceso a este esquema [de datos].

  2. Crear un esquema denominado [exec] para todos los procedimientos almacenados (y/o posiblemente cualquier punto de vista de seguridad). Asegúrese de que el propietario de este esquema tenga acceso al esquema [data] (esto es fácil si hace que dbo sea el propietario de este esquema).

  3. Crear un nuevo DB-rol denominado "Usuarios" y darle el acceso de ejecución al esquema [exec]. Ahora agregue todos los usuarios a este rol. Asegúrese de que sus usuarios solo tengan derechos de conexión y no tengan acceso otorgado a ningún otro esquema, incluido [dbo].

Ahora sus usuarios solo pueden acceder a los datos ejecutando los sProcs en [exec]. No pueden acceder a ningún otro dato ni ejecutar ningún otro objeto.

No estoy seguro de si esto responde a su pregunta (porque estaba seguro de lo que la pregunta era exactamente), por lo que me siento libre para redirigir.


En cuanto a la seguridad a nivel de fila, así es como siempre lo hago con el esquema de seguridad por encima de:

  1. siempre que implementar la seguridad a nivel de fila como una serie de vistas en ese espejo-wrap cada tabla y comparar la identidad del Usuario (generalmente con Suser_Sname() u otro) a una lista de seguridad con clave de un código de seguridad en la misma fila. Estas son las Vistas de Seguridad.

  2. Crear un nuevo esquema denominado [filas], que es dar acceso de propietario al esquema [de datos] y nada más. Coloque todas las Vistas de seguridad en este esquema.

  3. Revoca el acceso del propietario [exec] al esquema [data] y, en su lugar, otórguele acceso de datos al esquema [rows].

Hecho. Ahora la seguridad a nivel de fila se ha implementado deslizándola de forma transparente entre los sProcs y las tablas.


Por último, aquí es un procure almacenado que utilizo para ayudarme a recordar cuánto de este oscuro materia trabaja la seguridad e interactúa consigo misma (Uy, versión corregida de código):

CREATE proc [TestCnxOnly].[spShowProc_Security_NoEX] as 
--no "With Execute as Owner" for this version 
--create User [UserNoLogin] without login 
--Grant connect on database :: TestSecurity to Guest 
--alter database TestSecurity set trustworthy on 

--Show current user context: 
select current_user as current_ 
, session_user as session 
, user_name() as _name 
, suser_name() as [suser (sproc)] 
, suser_sname() as sname 
, system_user as system_ 


--Execute As Login = 'UserNoLogin' 
select current_user as current_ 
, session_user as session 
, user_name() as _name 
, suser_name() as [suser (after exec as)] 
, suser_sname() as sname 
, system_user as system_ 

EXEC('select current_user as current_ 
, session_user as session 
, user_name() as _name 
, suser_name() as [suser (in Exec(sql))] 
, suser_sname() as sname 
, system_user as system_') 

EXEC sp_ExecuteSQL N'select current_user as current_ 
, session_user as session 
, user_name() as _name 
, suser_name() as [suser (in sp_Executesql)] 
, suser_sname() as sname 
, system_user as system_' 

--Revert 
select current_user as current_ 
, session_user as session 
, user_name() as _name 
, suser_name() as [suser (aftr revert)] 
, suser_sname() as sname 
, system_user as system_ 

[EDIT: versión corregida de código)

+0

En general, entiendo el encadenamiento de propiedad con este ejemplo: Un proceso almacenado en las tablas de acceso de schema1 en schema1 = derechos de tabla (incluido DENY) no marcado. Las tablas en, digamos, schema2 están marcadas. Mi ejemplo muestra que con EXEC MultiSchema1.P1 – gbn

+0

Mi propio ejemplo que cruzó con su respuesta. El bit que me faltaba es que si el * propietario * de los esquemas relevantes es el mismo, entonces los derechos no se verifican. Este es el significado de la separación usuario/esquema que realmente debería haber leído. Sin embargo, tenemos el voladizo pre SQL 2005 y no lo hemos pensado en – gbn

+0

Ese ejemplo funciona porque está en el mismo esquema, por lo que el proceso almacenado y la tabla tienen el mismo propietario, por lo que se permite el acceso porque un propietario siempre tiene * default * derechos de acceso a sus propias cosas. Sin embargo, tenga en cuenta que esto no es exclusivo de DENY, etc. Cualquier cosa que pueda detener al propietario del esquema ciertamente detendrá los procesos almacenados que también poseen. – RBarryYoung

4

Puede:

Grant Execute On Schema::[schema_name] To [user_name] 

para permitir al usuario ejecutar cualquier procedimiento en el esquema. Si no desea que pueda ejecutarlos todos, puede denegar explícitamente la ejecución de un procedimiento determinado al usuario. Denegar tendrá prioridad en este caso.

+0

Correcto, pero ¿qué pasa con las tablas en diferentes esquemas utilizados por los procedimientos almacenados? No quiero NIEGAR ningún derecho – gbn

8

Mi 2c: cadena de propiedad es el legado. Data de días en que no había alternativas, y en comparación con las alternativas de hoy es inseguro y grosero.

me dicen que la alternativa no es permisos de esquema, la alternativa es la firma de código. Con la firma de código puede otorgar los permisos necesarios en la firma del procedimiento y otorgar acceso de ejecución amplio en el procedimiento mientras el acceso a los datos está estrechamente controlado. La firma de código ofrece un control más granular y más preciso, y no se puede abusar de la forma en que puede hacerlo el encadenamiento de propiedad. Funciona dentro del esquema, funciona a través del esquema, funciona en toda la base de datos y no requiere que se abra el gran agujero de seguridad del encadenamiento de propiedad de la base de datos cruzada. Y no requiere el secuestro de la propiedad del objeto para fines de acceso: el propietario del procedimiento puede ser cualquier usuario.

En cuanto a su segunda pregunta sobre la seguridad de nivel de fila: la seguridad de nivel de fila realmente no existe en las versiones de SQL Server 2014 y anteriores, como una característica ofrecida por el motor. Tiene varias soluciones alternativas, y esas soluciones funcionan realmente mejor con la firma de código que con el encadenamiento de propiedad. Como sys.login_token contiene las firmas de contexto y las contrafirmas, en realidad puede realizar comprobaciones más complejas de lo que podría hacerlo en un contexto de encadenamiento de propiedad.

Desde la versión 2016 SQL Server es totalmente compatible con row level security.

+0

La seguridad de nivel de fila es en realidad una pista falsa ... también usamos SUSER_SNAME a través de UNIONES desde tablas de seguridad para controlar el acceso. – gbn

+0

Es el bit del "legado" y luego el tiempo/esfuerzo/la pereza por la que no he seguido. + 1 de todos modos. Gracias – gbn

Cuestiones relacionadas