ADD SIGNATURE (Transact-SQL)

将数字签名添加到存储过程、函数、程序集或触发器。此外,还将副署添加到存储过程、函数、程序集或触发器。

主题链接图标Transact-SQL 语法约定

语法

ADD [ COUNTER ] SIGNATURE TO module_class::module_name 
    BY <crypto_list> [ ,...n ]

<crypto_list> ::=
    CERTIFICATE cert_name
    | CERTIFICATE cert_name [ WITH PASSWORD = 'password' ]
    | CERTIFICATE cert_name WITH SIGNATURE = signed_blob 
    | ASYMMETRIC KEY Asym_Key_Name
    | ASYMMETRIC KEY Asym_Key_Name [ WITH PASSWORD = 'password'.]
    | ASYMMETRIC KEY Asym_Key_Name WITH SIGNATURE = signed_blob

参数

  • module_class
    向其中添加签名的模块的类。架构范围内的模块的默认类为 OBJECT。

  • module_name
    要签名或副署的存储过程、函数、程序集或触发器的名称。

  • CERTIFICATE cert_name
    对存储过程、函数、程序集或触发器进行签名或副署时所用证书的名称。

  • WITH PASSWORD = 'password'
    是对证书私钥或非对称密钥进行解密时所需的密码。仅当数据库主密钥不保护私钥时才需要该子句。

  • SIGNATURE = signed_blob
    指定模块的已签名二进制大型对象 (BLOB)。如果不提供私钥,而只提供模块时,则该子句非常有用。当使用该子句时,只需要模块、签名和公钥便可将已签名的二进制大型对象添加到数据库。signed_blob 是以十六进制格式表示的 Blob 自身。

  • ASYMMETRIC KEY Asym_Key_Name
    对存储过程、函数、程序集或触发器进行签名或副署时所用非对称密钥的名称。

注释

要签名或副署的模块以及用来对模块进行签名的证书或非对称密钥必须已经存在。模块中的每个字符都包括在签名计算中。这包括前导回车符和换行符。

可以使用任意数量的证书和非对称密钥对模块进行签名和副署。

当更改模块时,便会删除模块的签名。

如果模块包含 EXECUTE AS 子句,则也会将主体的安全 ID (SID) 作为签名过程的一部分包括在内。

注意事项注意

模块签名应仅用于授予权限,从不用于拒绝或撤消权限。

可以在 sys.crypt_properties 目录视图中看到有关签名的信息。

副署

当执行签名的模块时,签名将临时添加到 SQL 标记,但如果模块执行其他模块或如果模块终止执行,则将丢失签名。副署是一种特殊形式的签名。副署本身并不授予任何权限,但是,它允许将由同一证书或非堆成密钥建立的签名保留对副署的对象进行调用的期间。

例如,假设用户 Alice 调用过程 ProcSelectT1ForAlice,此过程将调用过程 procSelectT1,而后者从表 T1 中进行选择。Alice 对 ProcSelectT1ForAlice 和 procSelectT1 具有 EXECUTE 权限,但它对于 T1 不具备 SELECT 权限,并且在此整个链中不涉及任何所有权链。Alice 无法访问表 T1,无论是直接访问,还是通过使用 ProcSelectT1ForAlice 和 procSelectT1。因为我们希望 Alice 始终使用 ProcSelectT1ForAlice 进行访问,所以我们不希望授予其执行 procSelectT1 的权限。如何实现呢?

  • 如果我们对 procSelectT1 进行签名,以便 procSelectT1 可以访问 T1,则 Alice 可以直接调用 procSelectT1,而不必调用 ProcSelectT1ForAlice。

  • 我们可以对 Alice 拒绝授予针对 procSelectT1 的 EXECUTE 权限,但是,Alice 也无法通过 ProcSelectT1ForAlice 调用 procSelectT1。

  • 对 ProcSelectT1ForAlice 签名本身也不会起作用,因为此签名在调用 procSelectT1 时将丢失。

然而,通过使用用于对 ProcSelectT1ForAlice 进行签名的相同证书来副署 procSelectT1,SQL Server 将在整个调用链中保留此签名,并允许访问 T1。如果 Alice 试图直接调用 procSelectT1,她将无法访问 T1,因为副署不授予任何权限。下面的示例 C 显示了此示例的 Transact-SQL。

权限

需要对对象拥有 ALTER 权限,并且对证书或非对称密钥拥有 CONTROL 权限。如果关联的私钥受密码保护,则用户还必须具有相应的密码。

示例

A. 通过使用证书对存储过程进行签名

以下示例使用证书 HumanResourcesDP 对存储过程 HumanResources.uspUpdateEmployeeLogin 进行签名。

USE AdventureWorks2008R2;
ADD SIGNATURE TO HumanResources.uspUpdateEmployeeLogin 
    BY CERTIFICATE HumanResourcesDP;
GO

B. 通过使用已签名的 BLOB 对存储过程进行签名

下面的示例创建一个新数据库,并创建要在示例中使用的证书。该示例创建一个简单的存储过程并且对该存储过程进行签名,并且从 sys.crypt_properties 检索该签名 BLOB。然后删除后再重新创建该过程。该示例通过使用 WITH SIGNATURE 语法对该工程进行签名。

CREATE DATABASE TestSignature ;
GO
USE TestSignature ;
GO
-- Create a CERTIFICATE to sign the procedure.
CREATE CERTIFICATE cert_signature_demo 
    ENCRYPTION BY PASSWORD = 'pGFD4bb925DGvbd2439587y'
    WITH SUBJECT = 'ADD SIGNATURE demo';
GO
-- Create a simple procedure.
CREATE PROC [sp_signature_demo]
AS
    PRINT 'This is the content of the procedure.' ;
GO
-- Sign the procedure.
ADD SIGNATURE TO [sp_signature_demo] 
    BY CERTIFICATE [cert_signature_demo] 
    WITH PASSWORD = 'pGFD4bb925DGvbd2439587y' ;
GO
-- Get the signature binary BLOB for the sp_signature_demo procedure.
SELECT cp.crypt_property
    FROM sys.crypt_properties AS cp
    JOIN sys.certificates AS cer
        ON cp.thumbprint = cer.thumbprint
    WHERE cer.name = 'cert_signature_demo' ;
GO

每次您创建一个过程时,由该语句返回的 crypt_property 签名都将不同。请记录结果以便在本示例的后面使用。对于本示例,演示的结果是:0x831F5530C86CC8ED606E5BC2720DA835351E46219A6D5DE9CE546297B88AEF3B6A7051891AF3EE7A68EAB37CD8380988B4C3F7469C8EABDD9579A2A5C507A4482905C2F24024FFB2F9BD7A953DD5E98470C4AA90CE83237739BB5FAE7BAC796E7710BDE291B03C43582F6F2D3B381F2102EEF8407731E01A51E24D808D54B373。

-- Drop the procedure so that a new version can be created.
DROP PROC [sp_signature_demo] ;
GO
-- Re-create the procedure by using the exact text including spaces.
CREATE PROC [sp_signature_demo]
AS
    PRINT 'This is the content of the procedure.' ;
GO
-- Add the signature. Use the signature BLOB obtained earlier.
ADD SIGNATURE TO [sp_signature_demo] 
    BY CERTIFICATE [cert_signature_demo]
    WITH SIGNATURE = 0x831F5530C86CC8ED606E5BC2720DA835351E46219A6D5DE9CE546297B88AEF3B6A7051891AF3EE7A68EAB37CD8380988B4C3F7469C8EABDD9579A2A5C507A4482905C2F24024FFB2F9BD7A953DD5E98470C4AA90CE83237739BB5FAE7BAC796E7710BDE291B03C43582F6F2D3B381F2102EEF8407731E01A51E24D808D54B373 ;
GO

C. 使用副署访问过程

下面的示例说明副署如何帮助控制对于对象的访问。

-- Create tesT1 database
CREATE DATABASE testDB;
GO
USE testDB;
GO
-- Create table T1
CREATE TABLE T1 (c varchar(11));
INSERT INTO T1 VALUES ('This is T1.');

-- Create a TestUser user to own table T1
CREATE USER TestUser WITHOUT LOGIN;
ALTER AUTHORIZATION ON T1 TO TestUser;

-- Create a certificate for signing
CREATE CERTIFICATE csSelectT
  ENCRYPTION BY PASSWORD = 'SimplePwd01'
  WITH SUBJECT = 'Certificate used to grant SELECT on T1';
CREATE USER ucsSelectT1 FROM CERTIFICATE csSelectT;
GRANT SELECT ON T1 TO ucsSelectT1;

-- Create a principal with low privileges
CREATE LOGIN Alice WITH PASSWORD = 'SimplePwd01';
CREATE USER Alice;

-- Verify Alice cannoT1 access T1;
EXECUTE AS LOGIN = 'Alice';
    SELECT * FROM T1;
REVERT;

-- Create a procedure that directly accesses T1
CREATE PROCEDURE procSelectT1 AS
BEGIN
    PRINT 'Now selecting from T1...';
    SELECT * FROM T1;
END;
GO
GRANT EXECUTE ON procSelectT1 to public;

-- Create special procedure for accessing T1
CREATE PROCEDURE  procSelectT1ForAlice AS
BEGIN
   IF USER_ID() <> USER_ID('Alice')
    BEGIN
        PRINT 'Only Alice can use this.';
        RETURN
    END
   EXEC procSelectT1;
END;
GO;
GRANT EXECUTE ON procSelectT1ForAlice TO PUBLIC;

-- Verify procedure works for a sysadmin user
EXEC procSelectT1ForAlice;

-- Alice still can't use the procedure yet
EXECUTE AS LOGIN = 'Alice';
    EXEC procSelectT1ForAlice;
REVERT;

-- Sign procedure to grant it SELECT permission
ADD SIGNATURE TO procSelectT1ForAlice BY CERTIFICATE csSelectT 
WITH PASSWORD = 'SimplePwd01';

-- Counter sign proc_select_t, to make this work
ADD COUNTER SIGNATURE TO procSelectT1 BY CERTIFICATE csSelectT 
WITH PASSWORD = 'SimplePwd01';

-- Now the proc works. 
-- Note that calling procSelectT1 directly still doesn't work
EXECUTE AS LOGIN = 'Alice';
    EXEC procSelectT1ForAlice;
    EXEC procSelectT1;
REVERT;

-- Cleanup
USE master;
GO
DROP DATABASE testDB;
DROP LOGIN Alice;