安装 SQL Server 2000 桌面引擎 (MSDE 2000) 时如何指定 SA 强密码

文章翻译 文章翻译
文章编号: 814463 - 查看本文应用于的产品
展开全部 | 关闭全部

概要

本文介绍安装 SQL Server 桌面引擎(即 MSDE 2000)时如何指定 sa 强密码。

更多信息

在安装 SQL Server 2000 桌面引擎 (MSDE 2000) 的任一实例过程中,必须为 sa 帐户指定强密码。即使该实例使用“Windows 身份验证模式”,也必须进行这一操作。在“Windows 身份验证模式”下运行时,任何用户都不能使用 sa 帐户;但是,以后可以将实例切换到“混合模式”,这时 sa 帐户会成为活动登录帐户。

当 MSDE 2000 的实例切换到“混合模式”时,如果 sa 帐户无密码,或者有空白的、简单的或广为人知的密码,MSDE 实例即可被未授权的用户访问。不能丢弃 sa 帐户,且必须始终使用强密码进行保护以限制未授权的访问。使用 sa 帐户获得 MSDE 2000 实例的访问权的任何用户,都会获得对该 MSDE 实例的完全控制权限,并且能够访问 MSDE 服务帐户所拥有的任何资源。默认情况下,MSDE 服务帐户为“LocalSystem”内置安全帐户。

有关强密码的详细信息,请访问下面的 Microsoft 网站:

安全规则

可以使用自定义的应用程序代码安装 MSDE。应用程序代码必须使用以下两种方法之一来设置 sa 密码:
  • 如果用户准备以“混合模式”安装 MSDE,并且准备使用 sa 帐户,则要求用户为 sa 帐户提供强密码。安装 MSDE 时使用该密码。
  • 如果不使用 sa 帐户,则生成一个随机字符串,然后将该字符串作为 sa 密码在 MSDE 安装时使用。

为提高安全性,不要对安装时指定的 sa 密码进行硬编码,使之作为 Setup.ini 文件中的一个参数、作为命令 (.cmd) 文件中的命令提示符开关、作为一项属性将其加入 MSI 文件中,或者以其他任何会将密码暴露为纯文本的方式使用。密码应由应用程序安装程序在运行时动态生成,并且应以下列方式之一传递到 MSDE 安装进程:
  • 通过应用程序安装代码运行 MSDE setup.exe,且在参数中指定 SAPWD 值。

    例如,使用 .NET Framework Process 类运行安装程序,然后在 ProcessStartInfo Arguments 属性中指定 SAPWD,或者使用 Win32 CreateProcess 函数运行安装程序,然后在 lpCommandLine 参数中指定 SAPWD。


    有关 SAPWD 命令行参数的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
    810826 INF:MSDE Service Pack 2 安装程序中的新参数
  • 在基于自定义 Windows 安装程序的安装过程中使用 MSDE 合并模块时,可执行自定义操作来传递强密码。

注意:在使用“Windows 身份验证模式”安装 MSDE 2000 的过程中,不能为 sa 帐户设置密码。在此情况下,必须在安装结束后设置密码。Microsoft 极力建议使用最新的 Service Pack 安装 MSDE 2000。

Microsoft 推荐用于生成随机密码的方法是使用 Crypto API 函数,如下所示:
  • CryptAcquireContext
  • CryptGenRandom
  • CryptCreateHash
  • CryptHashData

如果使用本机代码,请使用 CryptReleaseContext

如果使用托管代码,请使用 System.Security.Cryptography.RNGCryptoServiceProvider 以获取随机编码的字符串,然后对使用 System.Security.Cryptography.SHA1 类的 ComputeHash 方法返回的值进行哈希处理。随机字符串必须有可变的长度,介于 7 到 20 个字符之间。

如果忘记了 sa 密码,或者不知道 sa 是什么,并且实例转换为“混合模式”,则 sysadmin 固定服务器角色的成员可在不知道以前密码的情况下重新设置 sa 密码。默认情况下,所有本地管理员组的成员用户都是 sysadmin 角色的成员。sysadmin 角色的成员可以将 MSDE 实例由“Windows 身份验证模式”更改为“混合模式”,反之亦然,并可更改 sa 密码。因此,为安全起见,可能需要从 sysadmin 角色中删除管理员组。

有关如何将管理员组从 sysadmin 角色中删除的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
263712 如何禁止 Windows NT 管理员管理群集 SQL Server


有关更改 sa 帐户密码的其他信息,请单击下面的文章编号,以查看 Microsoft 知识库中相应的文章:
322336 如何验证和更改 MSDE 系统管理员密码

注意:本文所列举的在安装过程中更改 sa 密码的方法仅适用于新的 MSDE 安装。

以下步骤使用示例源代码生成随机 sa 密码,然后启动 MSDE 安装。

使用 Microsoft Visual C++ .NET

  1. 单击“开始”,依次指向“所有程序”、“Microsoft Visual Studio .NET”、“Visual Studio .NET 工具”,然后单击“Visual Studio .NET 命令提示”。
  2. 打开记事本。
  3. 将下面的代码粘贴到记事本中:
    #pragma once
    
    #define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers.
    #define UNICODE
    #include <stdio.h>
    #include <windows.h>
    #include <wincrypt.h>
    
    #ifdef UNICODE
    	#define STRNCPY	wcsncpy
    #else
    	#define STRNCPY strncpy
    #endif
    
    #include <atlenc.h>
    
    #define SAPWDSWITCH _T("SAPWD=")
    #define INSTANCENAME _T("INSTANCENAME=MSDETEST")
    
    
    BOOL GenPwd(TCHAR*, int);
    void DisplayError (DWORD);
    
    int main(void)
    {	
    	//Generate random length for password, between 7 and 20 characters.
    	int nPwdLen = ((rand() % 20) + 7) + 1;	//Extra character for null terminator
    	TCHAR* pPwd = new TCHAR[nPwdLen];
    	UINT uRes = 0;
    	DWORD dwRes = 0;
    	if (!GenPwd(pPwd, nPwdLen))
    	{
    		//Failed to generate a password, log the error and return failure.
    		dwRes = GetLastError();
    		DisplayError(dwRes);
    
    		return dwRes;
    	}
    
    
            STARTUPINFO si;
            PROCESS_INFORMATION pi;
    
            ZeroMemory( &si, sizeof(si) );
            si.cb = sizeof(si);
            ZeroMemory( &pi, sizeof(pi) );
    
    	//Allocate a string for the command line.
    	LPTSTR lpCommand = new TCHAR[nPwdLen + _tcslen(INSTANCENAME) + _tcslen(SAPWDSWITCH) + 2];
    
    	_stprintf(lpCommand, _T("%s %s%s"),INSTANCENAME,  SAPWDSWITCH, pPwd);
    // Specify the complete path of Setup.exe.
    	if (!CreateProcess(_T("setup.exe"), lpCommand, NULL, NULL, FALSE, 
    				0, NULL, NULL, &si, &pi))
    	{
    		dwRes = GetLastError();
    		DisplayError(dwRes);
    	}
    	return 0;
    }
    
    
    //Generates a Random string of length nLen - 1.  Buffer ppwd must allocate an extra character for null terminator.
    //Returns TRUE if successful, FALSE if fails.
    //Extended error information can be obtained from GetLastError().
    BOOL GenPwd(TCHAR* ppwd, int nLen)
    {
    	BOOL bResult = FALSE;	//assume failure
    	HCRYPTPROV hProv = NULL;
    	HCRYPTHASH hHash = NULL;
    	
    	//Storage for random string 4 times longer than the resulting password.
    	DWORD dwBufSize = nLen*4;
    	DWORD dwSize = Base64EncodeGetRequiredLength((int)dwBufSize);
    	LPSTR pEncodedString = NULL;
    	LPBYTE pRandomBuf = NULL;
    	TCHAR* pTRandomPwd = NULL;
    	
    	try
    	{
    		pEncodedString = new char[dwSize];
    		pRandomBuf = new BYTE[dwBufSize];
    		
    		// Try to acquire context to Crypto provider.
    		if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT))
    		{
    			if (GetLastError() == NTE_BAD_KEYSET) //Test for non-existent keyset
    			{
    				if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_SILENT | CRYPT_NEWKEYSET))
    					throw(GetLastError());
    			}
    			else
    				throw(GetLastError());
    		}
    
    		//Generate a random sequence.
    		if (!CryptGenRandom(hProv, dwBufSize, pRandomBuf))
    		{
    			throw(GetLastError());
    		}
    
    		//Get a handle to a hash, then hash the random stream.
    		if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
    		{
    			throw(GetLastError());
    		}
    
    		if (!CryptHashData(hHash, pRandomBuf, dwBufSize, NULL))
    		{
    			throw(GetLastError());
    		}
    	
    		//Destroy the hash object.
    		CryptDestroyHash(hHash);
    		//Release Provider context
    		CryptReleaseContext(hProv, 0);	
    	
    		//Encode the hash value to base64.
    		if (!Base64Encode(pRandomBuf, dwBufSize, pEncodedString, (int*) &dwSize, 0))
    		{
    			throw(GetLastError());
    		}
    	
    		//Determine how many tchars you need to convert string to base64.
    		int nTchars = (int) strlen(pEncodedString);
    	
    		pTRandomPwd = new TCHAR[nTchars];
    	
    #ifdef UNICODE
    		if (MultiByteToWideChar(CP_UTF8, 0, pEncodedString, nTchars, pTRandomPwd, nTchars) == 0)
    		{
    			throw(GetLastError());
    		}
    #else
    		STRNCPY( pTRandomPwd, pEncodedString, nLen);
    #endif
    
    		//Copy the first x characters of random string to output buffer.
    		STRNCPY(ppwd, pTRandomPwd, nLen);
    		//Add null terminator to ppwd string.
    		ppwd[nLen] = _T('\0');
    
    		bResult = TRUE;
    
    	}
    	catch (DWORD)
    	{
    		//Set return value to false.
    		bResult = FALSE;
    	}
    	catch (...)
    	{
    		//Unknown error, throw. 
    		throw;
    	}
    
    	//Clean up memory.
    	if (pRandomBuf)
    	{
    		delete pRandomBuf;
    		pRandomBuf = NULL;
    	}
    
    	if (pEncodedString)
    	{
    		delete pEncodedString;
    		pEncodedString = NULL;
    	}
    
    	if (pTRandomPwd)
    	{
    		delete pTRandomPwd;
    		pTRandomPwd = NULL;
    	}
    
    	return bResult;
    }
    
    
    void DisplayError (DWORD dwError)
    {
    	//Resolve the error code to a message string.
    	LPCTSTR MessageBuffer;
    	DWORD dwBufferLength = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
    		NULL, // module to get message from (NULL == system)
    		dwError,
    		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language.
    		(LPTSTR) &MessageBuffer,
    		0,
    		NULL
    		);
    
    	DWORD dwBytesWritten;
    	// Output message string on stderr.
    	WriteFile(
    		GetStdHandle(STD_ERROR_HANDLE),
    		MessageBuffer,
    		dwBufferLength,
    		&dwBytesWritten,
    		NULL
    		);
    }
    
  4. 将文件另存为 StrongSA.cpp
  5. 在命令提示符下,键入以下命令来编译这段代码:

    cl strongSA.cpp
  6. 在命令提示符下,键入以下命令来运行这段代码:

    strongSA.exe

使用 Microsoft C#.NET

  1. 在 Visual Studio .NET 中,新建一个 Visual C# 控制台应用程序项目。
  2. 将以下代码粘贴到含有 Main 函数的类文件中。

    确认这段代码替换了文件中所有现有的代码:
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Resources;
    using Microsoft.Win32;
    using System.Security.Cryptography;
    
    
    class InstMSDE
    {
    	static void Main(string[] args)
    	{
    		try
    		{   
    			
    
    			// Generate random password.
    			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    			byte[] encodedSeed = new byte[512];
    			rng.GetBytes(encodedSeed);
    			SHA1 sha1 = SHA1.Create();
    			byte[] hashval = sha1.ComputeHash(encodedSeed);
    			String base64HashVal = Convert.ToBase64String(hashval);
    			// Trim "=" off the end.
    			base64HashVal = base64HashVal.TrimEnd('=');
    				
    			string msdeINI = "setup.ini";
    
    			// You have to set startInfo parameters values as appropriate for your installation.
    			ProcessStartInfo startInfo = new ProcessStartInfo();
    
    			// Setup.exe for MSDE sp3.
    			startInfo.FileName = "setup.exe"; 
    
    			// Pass the SA password to the setup program.
    			startInfo.Arguments = "/settings \"" + msdeINI + "\"" + " SAPWD=" + base64HashVal + " /qr+ "; 
    			startInfo.WindowStyle = ProcessWindowStyle.Normal;
    			// Substitute the workdir with complete path of installation folder. 
    			startInfo.WorkingDirectory = "c:\\Workingdir";
    				
    			Process.Start(startInfo);
    			
    			
    		}
    		catch (Exception e)
    		{
    			Console.WriteLine("Unable to execute program due to the following error: " + e.Message);
    			return;
    		}  
    	}
    }
    
  3. 按 F5 键编译并运行该程序。

参考

有关如何在自定义应用程序安装程序中嵌入 MSDE 的详细信息,请访问以下 Microsoft 网站:

Embedding MSDE 2000 Setup in the Setup of Custom Applications(将 MSDE 2000 安装程序嵌入到自定义应用程序的安装程序中)

属性

文章编号: 814463 - 最后修改: 2005年7月11日 - 修订: 1.3
这篇文章中的信息适用于:
  • Microsoft SQL Server 2000 Desktop Engine (Windows)
关键字:?
kbsetup kbsecurity kblogin kbauthentication kbsample kbguidelines kbcode kbinfo KB814463
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。
不再更新的 KB 内容免责声明
本文介绍那些 Microsoft 不再提供支持的产品。因此本文按“原样”提供,并且不再更新。

提供反馈

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com