从Hack中保护Delphi连接字符串

时间:2017-02-06 09:42:46

标签: sql-server delphi connection-string ado

我们有一个delphi应用程序,它使用tadoconnection连接sql server数据库 问题是当我们在运行exe文件时打开资源黑客时,它会清楚地显示连接字符串,并且任何人都可以连接到我们的服务器

下面显示的示例代码描述了问题 enter image description here

3 个答案:

答案 0 :(得分:5)

您没有使用资源黑客。如果您的字符串存储在资源中,它们将更容易查找和查看。

您正在使用内存扫描程序,它在程序运行时查看程序的原始内存。最终,您必须在内存中创建一个连接字符串以传递给数据库引擎。如果黑客可以访问应用程序及其内存,则可以使用应用程序访问其数据。具有此类访问权限的专用黑客将能够在使用该字符串时获取该字符串。

但是你可以采取一些措施让随意黑客更加努力。

首先,不要让非管理员用户有权访问您的应用。当用户运行扫描程序应用程序时,它可能会访问由同一用户运行的其他应用程序中的内存。在不同的用户上下文或升级过程中运行您的应用。用户运行的应用程序无法访问其他用户正在运行的其他应用程序的内存,除非明确授予他们权限,或者他/她是管理员。如果黑客有权访问您的应用,则所有投注均已关闭。

此外,将字符串分解为必要时必须连接的较小子字符串。动态构建连接字符串。这样,如果黑客试图在磁盘上查看EXE文件,那么子串就不会存储在一个地方。

无论如何,不会将敏感凭据存储在应用代码中以开始。将它们存储在外部,并保持加密。仅在绝对需要时将它们检索到内存中,并在使用完{Q 3}后使用SecureZeroMemory()安全地销毁该内存的内容。如果您需要在内存中保留凭据或连接字符串超过几毫秒,请考虑在不使用它时使用CryptProtectMemory()加密该内存。

将连接字符串拼凑起来所花费的工作越多,休闲黑客也必须做的工作越多。但是一个专职的黑客只会等到你完成所有的工作,然后在使用它的时候从内存中拔出最后的字符串,所以你必须尽你所能来防止首先访问内存。

答案 1 :(得分:4)

  1. 确保您的密码单独存储,并且安全地符合您的要求。*
  2. 确保不要在连接时保留密码。即Connection.Properties必须包含'Persist Security Info=False'
  3. Connection. LoginPrompt设为True
  4. OnLogin事件实施处理程序,您可以在其中加载,解密并提供Connection.Open来电的密码。
  5. 作为最后的安全步骤,请确保从内存中删除解密的密码。
  6.   

    *有很多简单的加密库可供您加密密码。微软的Data Protection API值得考虑。请记住,如果有人可以访问您的应用程序,则无法实现完美的安全性。您可以做的最好的事情是添加混淆层,使黑客更难破解您的数据库登录凭据。

    一些示例代码

    procedure TAbc.HandleOnLogin(Sender: TObject; Username, Password: string);
    var
      LPassword: string;
    begin
      LPassword := GetDecryptedPassword; //Your choice how you do this
      Connection.Open(Username, LPassword);
      //The next line ensures memory is erased before it is deallocated
      //which would otherwise leave the password hanging around.
      SecureZeroMemory(Pointer(LPassword), Length(LPassword) * SizeOf(Char));
    end;
    

答案 2 :(得分:0)

除了其他答案之外,您可以执行的操作是删除读取进程内存的权限,如果您控制用户权限(例如在企业环境中),这将非常有用。

在流程开始时调用以下过程可防止非管理员用户读取进程内存。 (您将需要JEDI API单元来丢失API调用)

uses JwaWinNT, JwaWinBase, JwaAclApi, JwaAccCtrl;

//...
{$SCOPEDENUMS ON}
function ProtectProcess(): DWORD;
type
  TSidType = (Everyone, CurrentUser, System, Admin);
var
  // Released on exit
  ProcessToken: THandle;
  TokenInfo: PVOID;

  SidCurUser: PSID;
  SidEveryone: PSID;
  SidSystem: PSID;
  SidAdmins: PSID;

  ACL: PACL;
  SecDesc: PSECURITY_DESCRIPTOR;
  Size: DWORD;
  TokenSize: DWORD;
  BResult: Bool;

  SIDAuthEveryone: SID_IDENTIFIER_AUTHORITY;
  SIDAuthSystem: SID_IDENTIFIER_AUTHORITY;
  SIDAuthAdministrators: SID_IDENTIFIER_AUTHORITY;

  SIDArray: array[TSidType] of PSID;
  I: TSidType;
const
  // Mimic Protected Process
  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880%28v=vs.85%29.aspx
  // Protected processes allow PROCESS_TERMINATE, which is
  // probably not appropriate for high integrity software.
  DeniedPermissions =
            {READ_CONTROL |}
            WRITE_DAC or WRITE_OWNER or
            PROCESS_CREATE_PROCESS or PROCESS_CREATE_THREAD or
//            PROCESS_DUP_HANDLE or // this permission is needed for printing
            PROCESS_QUERY_INFORMATION or
            PROCESS_SET_QUOTA or PROCESS_SET_INFORMATION or
            PROCESS_VM_OPERATION or
            PROCESS_VM_READ or PROCESS_VM_WRITE
            // In addition to protected process
//            or PROCESS_SUSPEND_RESUME or PROCESS_TERMINATE
            ;
  // Standard and specific rights not explicitly denied
  AllowedPermissions = ((not DeniedPermissions) and $1FFF) or PROCESS_TERMINATE or SYNCHRONIZE;
begin
  ACL := nil;
  TokenInfo := nil;
  SecDesc := nil;
  try
    TokenSize := 0;
    ProcessToken := 0;
    // If this fails, you can try to fallback to OpenThreadToken
    if (not OpenProcessToken(GetCurrentProcess(), TOKEN_READ, ProcessToken)) then
    begin
      Result := GetLastError();
      Exit;
    end;

    BResult := GetTokenInformation(ProcessToken, TokenUser, nil, 0, TokenSize);
    Result := GetLastError();
    Assert((not BResult) and (ERROR_INSUFFICIENT_BUFFER = Result));
    if(not ((BResult = FALSE) and (ERROR_INSUFFICIENT_BUFFER = Result))) then
    begin
      // failed;
      Exit;
    end;

    if (TokenSize > 0) then
    begin
      TokenInfo := HeapAlloc(GetProcessHeap(), 0, TokenSize);
      Result := GetLastError();
      Assert(Assigned(TokenInfo));
      if (nil = TokenInfo) then
      begin
        // failed;
        Exit;
      end;
    end;

    BResult := GetTokenInformation(ProcessToken, TokenUser, TokenInfo, TokenSize, TokenSize);
    Result := GetLastError();
    Assert(BResult and Assigned(TokenInfo));
    if not (BResult and Assigned(TokenInfo)) then
    begin
      Exit;
    end;

    SidCurUser := (PTokenUser(TokenInfo)).User.Sid;

    SIDAuthEveryone := SECURITY_WORLD_SID_AUTHORITY;
    BResult := AllocateAndInitializeSid(@SIDAuthEveryone, 1,
        SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, SidEveryone);
    Result := GetLastError();
    Assert(BResult and Assigned(SidEveryone));
    if not (BResult and Assigned(SidEveryone)) then
    begin
      Exit;
    end;

    SIDAuthSystem := SECURITY_NT_AUTHORITY;
    BResult := AllocateAndInitializeSid(@SIDAuthSystem, 1,
        SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, SidSystem);
    Result := GetLastError();
    Assert(BResult and Assigned(SidSystem));
    if not (BResult and Assigned(SidSystem)) then
    begin
      Exit;
    end;

    SIDAuthAdministrators := SECURITY_NT_AUTHORITY;
    BResult := AllocateAndInitializeSid(@SIDAuthAdministrators, 2,
        SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
        0, 0, 0, 0, 0, 0, SidAdmins);
    Result := GetLastError();
    Assert(BResult and Assigned(SidAdmins));
    if not (BResult and Assigned(SidAdmins)) then
    begin
      Exit;
    end;

    SIDArray[TSidType.Everyone] := SidEveryone;   // Deny most rights to everyone
    SIDArray[TSidType.CurrentUser] := SidCurUser; // Allow what was not denied
    SIDArray[TSidType.System] := SidSystem;       // Full control
    SIDArray[TSidType.Admin] := SidAdmins;        // Full control

    // Determine required size of the ACL
    Size := SizeOf(ACL);

    // First the DENY, then the ALLOW
    Size := Size + GetLengthSid(SIDArray[TSidType.Everyone]);
    Size := Size + SizeOf(ACCESS_DENIED_ACE) - SizeOf(DWORD);

    for I := TSidType.CurrentUser to High(SIDArray) do
    begin
      // DWORD is the SidStart field, which is not used for absolute format
      Size := Size + GetLengthSid(SIDArray[I]);
      Size := Size + SizeOf(ACCESS_ALLOWED_ACE) - SizeOf(DWORD);
    end;

    Size := Size + SizeOf(DWORD);

    ACL := PACL(HeapAlloc(GetProcessHeap(), 0, Size));
    Result := GetLastError();
    Assert(Assigned(ACL));
    if not Assigned(ACL) then
    begin
      Exit;
    end;

    BResult := InitializeAcl(ACL, Size, ACL_REVISION);
    Result := GetLastError();
    Assert(BResult);
    if not BResult then
    begin
      Exit;
    end;

    BResult := AddAccessDeniedAce(ACL, ACL_REVISION, DeniedPermissions,
      SIDArray[TSidType.Everyone]);
    Result := GetLastError();
    Assert(BResult);
    if not BResult then
    begin
      Exit;
    end;

    BResult := AddAccessAllowedAce(ACL, ACL_REVISION, AllowedPermissions,
      SIDArray[TSidType.CurrentUser]);
    Result := GetLastError();
    Assert(BResult);
    if not BResult then
    begin
      Exit;
    end;

    // Because of ACE ordering, System will effectively have dwAllowed even
    // though the ACE specifies PROCESS_ALL_ACCESS (unless software uses
    // SeDebugPrivilege or SeTcbName and increases access).
    // As an exercise, check behavior of tools such as Process Explorer under XP,
    // Vista, and above. Vista and above should exhibit slightly different behavior
    // due to Restricted tokens.
    BResult := AddAccessAllowedAce(ACL, ACL_REVISION, PROCESS_ALL_ACCESS,
      SIDArray[TSidType.System]);
    Result := GetLastError();
    Assert(BResult);
    if not BResult then
    begin
      Exit;
    end;

    // Because of ACE ordering, Administrators will effectively have dwAllowed
    // even though the ACE specifies PROCESS_ALL_ACCESS (unless the Administrator
    // invokes 'discretionary security' by taking ownership and increasing access).
    // As an exercise, check behavior of tools such as Process Explorer under XP,
    // Vista, and above. Vista and above should exhibit slightly different behavior
    // due to Restricted tokens.
    BResult := AddAccessAllowedAce(ACL, ACL_REVISION, PROCESS_ALL_ACCESS, SIDArray[TSidType.Admin]);
    Result := GetLastError();
    Assert(BResult);
    if not BResult then
    begin
      SiMain.LogWin32Error('AddAccessAllowedAce failed: ', Result);
      Exit;
    end;

    SecDesc := PSECURITY_DESCRIPTOR(HeapAlloc(GetProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH));
    Result := GetLastError();
    Assert(Assigned(SecDesc));
    if not Assigned(SecDesc) then
    begin
      Exit;
    end;

    // InitializeSecurityDescriptor initializes a security descriptor in
    // absolute format, rather than self-relative format. See
    // http://msdn.microsoft.com/en-us/library/aa378863(VS.85).aspx
    BResult := InitializeSecurityDescriptor(SecDesc, SECURITY_DESCRIPTOR_REVISION);
    Result := GetLastError();
    Assert(BResult);
    if not BResult then
    begin
      Exit;
    end;

    BResult := SetSecurityDescriptorDacl(SecDesc, TRUE, ACL, FALSE);
    Result := GetLastError();
    Assert(BResult);
    if not BResult then
    begin
      Exit;
    end;

    SetSecurityInfo(
        GetCurrentProcess(),
        SE_KERNEL_OBJECT, // process object
        OWNER_SECURITY_INFORMATION or DACL_SECURITY_INFORMATION,
        SidCurUser, // Owner SID
        nil, // Group SID
        ACL,
        nil // SACL
        );
    Result := GetLastError();
    Assert(ERROR_SUCCESS = Result);
  finally
    if (nil <> SecDesc) then
    begin
      HeapFree(GetProcessHeap(), 0, SecDesc);
    end;
    if (nil <> ACL) then
    begin
      HeapFree(GetProcessHeap(), 0, ACL);
    end;
    if (SidAdmins <> nil) then
    begin
      FreeSid(SidAdmins);
    end;
    if (SidSystem <> nil) then
    begin
      FreeSid(SidSystem);
    end;
    if (SidEveryone <> nil) then
    begin
      FreeSid(SidEveryone);
    end;
    if (nil <> TokenInfo) then
    begin
      HeapFree(GetProcessHeap(), 0, TokenInfo);
    end;
    if (0 <> ProcessToken) then
    begin
      CloseHandle(ProcessToken);
    end;
  end;
end;