ADD SIGNATURE (Transact-SQL)

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例

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

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)也包含在签名过程中。

注意

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

无法对数据定义语言(DDL)触发器和内联表值函数进行签名。

有关签名的信息显示在 sys.crypt_properties 目录视图中。

警告

重新创建签名过程时,原始批处理中的所有语句必须与重新创建的批处理匹配。 如果批处理的任何部分不同(即使在空格或注释中)不同,则生成的签名是不同的。

副署

执行签名模块时,签名会暂时添加到 SQL 令牌,但如果模块执行另一个模块,或者模块终止执行,签名将丢失。 反签名 是一种特殊的签名形式。 本身,反签名不会授予任何权限。 但是,它允许同一证书或非对称密钥进行的签名在对反签名对象的调用期间保留。

例如,假设用户 Alice 调用过程 ProcForAlice,即从表 T1中进行选择的过程 ProcSelectT1。 Alice 对 ProcForAlice具有 EXECUTE 权限,但对该 ProcSelectT1SELECTT1权限没有 EXECUTE 权限,并且此整个链中不涉及所有权链接。 Alice 不能直接访问表 T1,也不能通过使用 ProcForAliceProcSelectT1。 由于我们希望 Alice 始终使用 ProcForAlice 进行访问,因此我们不希望授予她执行 ProcSelectT1的权限。 如何实现此方案?

  • 如果我们 ProcSelectT1签名,这样 ProcSelectT1 就可以访问 T1,那么 Alice 可以直接调用 ProcSelectT1,她不必调用 ProcForAlice

  • 我们可以拒绝对 Alice 的 ProcSelectT1EXECUTE 权限,但随后 Alice 无法通过 ProcForAlice调用 ProcSelectT1

  • 签名 ProcForAlice 本身不起作用,因为签名在调用 ProcSelectT1时丢失。

但是,通过使用用于签名 ProcForAlice的同一证书对 ProcSelectT1 进行反签名,签名将保留在调用链中,并允许访问 T1。 如果 Alice 尝试直接调用 ProcSelectT1,则她无法访问 T1,因为反签名不会授予任何权限。 示例 C 显示了此示例的 Transact-SQL。

签名示例的屏幕截图。

权限

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

示例

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

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

USE AdventureWorks2022;

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 basic procedure.
CREATE PROCEDURE [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
     INNER JOIN sys.certificates AS cer
         ON cp.thumbprint = cer.thumbprint
WHERE cer.name = 'cert_signature_demo';
GO

每次创建过程时,此语句返回的 crypt_property 签名都不同。 请记录结果以便在本示例的后面使用。 在此特定情况下,演示的结果为 0x831F5530C86CC8ED606E5BC2720DA835351E46219A6D5DE9CE546297B88AEF3B6A7051891AF3EE7A68EAB37CD8380988B4C3F7469C8EABDD9579A2A5C507A4482905C2F24024FFB2F9BD7A953DD5E98470C4AA90CE83237739BB5FAE7BAC796E7710BDE291B03C43582F6F2D3B381F2102EEF8407731E01A51E24D808D54B373

-- Drop the signature so that it can be signed again.
DROP SIGNATURE FROM [sp_signature_demo]
    BY CERTIFICATE [cert_signature_demo];
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. 使用 countersignature 访问过程

下面的示例说明副署如何帮助控制对于对象的访问。 必须将 <password> 替换为适当的密码。

-- 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 = '<password>'
    WITH SUBJECT = 'Certificate used to grant SELECT on T1';

CREATE USER ucsSelectT1 FOR CERTIFICATE csSelectT;
GRANT SELECT ON T1 TO ucsSelectT1;

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

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

-- 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;
GO

-- Create special procedure for accessing T1
CREATE PROCEDURE ProcForAlice
AS
BEGIN
    IF USER_ID() <> USER_ID('Alice')
        BEGIN
            PRINT 'Only Alice can use this.';
            RETURN;
        END
    EXECUTE ProcSelectT1;
END
GO

GRANT EXECUTE ON ProcForAlice TO PUBLIC;

-- Verify procedure works for a sysadmin user
EXECUTE ProcForAlice;

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

-- Sign procedure to grant it SELECT permission
ADD SIGNATURE TO ProcForAlice
BY CERTIFICATE csSelectT WITH PASSWORD = '<password>';

ADD COUNTER SIGNATURE TO ProcSelectT1
BY CERTIFICATE csSelectT WITH PASSWORD = '<password>';

-- Now the stored procedure works.   
-- Note that calling ProcSelectT1 directly still doesn't work.
EXECUTE AS LOGIN = 'Alice';
EXECUTE ProcForAlice;
EXECUTE ProcSelectT1;
REVERT;

-- Cleanup
USE master;
GO

DROP DATABASE testDB;
DROP LOGIN Alice;