【问题标题】:File permission to a specific application特定应用程序的文件权限
【发布时间】:2021-09-28 03:07:33
【问题描述】:

我在 Delphi 中制作了应用程序,它在每个用户的 Windows Server 2019 上运行。这些用户通过远程桌面连接到用户会话(组策略)并运行应用程序。

是否可以使用我的应用程序打开共享网络地图上的配置文件,而不是使用记事本?

更笼统地说。存储实际上对用户保密的配置数据的最佳方式是什么?我正在考虑将敏感数据仅放在数据库中,但仍然很好的是不要将例如服务器信息放在配置文件中而不是“烘焙”中。

这是我的第一篇文章,我知道它介于编程和服务器配置之间。否则我的翻译量表似乎没有受到“仅应用程序打开文件”的影响。如果这篇文章不完美,我的借口。

【问题讨论】:

  • 如果您不希望配置文件可读,您应该在程序中加密和解密数据
  • Windows 安全性是基于用户的,而不是基于应用程序的。如果给定用户可以访问该文件,并且该用户同时运行您的应用和记事本,那么这两个应用都可以访问该文件。
  • 记事本是一个文本编辑器。因此,如果您想阻止用户使用文本编辑器查看或修改您的配置文件,您不应该以文本格式存储您的配置。相反,您应该以原始二进制格式存储您的配置。如果您的配置的一部分是字符串,例如远程服务器的域名,那么即使在原始二进制文件中,通过使用像记事本这样的纯文本编辑器打开它,这些文本仍然可以读取。但没有其他值可以直接读取。
  • 您可以不将应用程序存储在文件中,而是将其存储在 Windows 注册表中。这意味着用户读取配置的唯一方法是使用Regedit 应用程序。如果我没记错的话,可以阻止远程桌面用户启动Regedit,但我不确定。在 Registry 中存储配置的另一个优点是您可以使用 Group-policy 配置对其进行特定更改。
  • @SilverWarior Registry 并不比任何文件都好。

标签: windows delphi file-permissions windows-server group-policy


【解决方案1】:

我看到了几种可能性:

1° 如果您不希望用户“看到”您的数据,那么您必须对文件内容进行加密。有很多 Delphi 加密/解密库。我建议您从 GitHub 上免费提供的Delphi Encryption Compendium 开始。

您可以将数据存储在内存中的结构中,例如XMLJSON(Delphi 具有处理 XML 和 JSON 的内置例程)。在写入光盘之前,您对其进行加密,并在重新加载加密文件后,在以标准方式访问它之前对其进行解密。

2° 使用可从另一个帐户访问的文件,并在需要访问该文件时让您的程序模拟该帐户。

我写了一些代码来简化和演示这种方式。我创建了一个类 TImpersonateUser 有两个方法 LogonLogoff 这将使程序连接到给定的用户帐户并断开与它的连接。

要进行测试,首先使用另一个帐户登录并在某处创建一个文件,例如在文档中。然后登录回您的普通用户代码并启动演示程序(代码如下)。填写用户名、域和密码(对于域,“.”将仅在本地计算机上进行身份验证)。使用您之前创建的文件的完整路径填写文件名。点击“文件访问”。它应该回答“找不到文件”。然后单击“模拟”并再次单击“文件访问”。现在您应该可以访问另一个帐户中的文件。点击“恢复为自己”,然后重试“文件访问”,它应该会再次失败。

总之,对于您的问题,用户看不到的数据必须在另一个帐户下创建,并且您的应用程序在需要访问数据时模拟该其他帐户。不要忘记在您的程序中以某种方式隐藏用户名和密码。

注意:一旦获得句柄(打开的文件或流),您可以 RevertToSelf 并仍然使用该句柄(或流)。它保留安全上下文(使用的帐户)直到关闭。这意味着您可以在打开文件之前调用 Logon,在打开(或打开失败)后立即调用 logoff 并继续访问文件。

编辑:我用more code写了一个blog post

unit ImpersonateUser;

interface

uses
    Winapi.Windows, System.Classes;

const
    LOGON32_LOGON_NEW_CREDENTIALS  = 9;    // Missing in Delphi

type
    TImpersonateUser = class(TComponent)
    protected
        FUserToken : THandle;
        FErrorCode : DWORD;
    public
        destructor Destroy; override;
        function  Logon(const UserName : String;
                        const Domain   : String;
                        const Password : String) : Boolean;
        procedure Logoff();
        property ErrorCode : DWORD read FErrorCode;
    end;

implementation

{ TImpersonateUser }

destructor TImpersonateUser.Destroy;
begin
    if FUserToken <> 0 then begin
        CloseHandle(FUserToken);
        FUserToken := 0;
    end;

    inherited Destroy;
end;

procedure TImpersonateUser.Logoff;
begin
    if FUserToken <> 0 then begin
        RevertToSelf();   // Revert to our user
        CloseHandle(FUserToken);
        FUserToken := 0;
    end;
end;

function TImpersonateUser.Logon(
    const UserName : String;
    const Domain   : String;
    const Password : String): Boolean;
var
    LoggedOn : Boolean;
begin
    Result := FALSE;
    if FUserToken <> 0 then
        Logoff();

    if UserName = '' then begin // Must at least provide a user name
        FErrorCode := ERROR_BAD_ARGUMENTS;
        Exit;
    end;

    if Domain <> '' then
        LoggedOn := LogonUser(PChar(UserName),
                              PChar(Domain),
                              PChar(Password),
                              LOGON32_LOGON_INTERACTIVE,
                              LOGON32_PROVIDER_DEFAULT,
                              FUserToken)
    else
        LoggedOn := LogonUser(PChar(UserName),
                              PChar(Domain),
                              PChar(Password),
                              LOGON32_LOGON_NEW_CREDENTIALS,
                              LOGON32_PROVIDER_WINNT50,
                              FUserToken);
    if not LoggedOn then begin
        FErrorCode := GetLastError();
        Exit;
    end;

    if not ImpersonateLoggedOnUser(FUserToken) then begin
        FErrorCode := GetLastError();
        Exit;
    end;

    FErrorCode := S_OK;
    Result     := TRUE;
end;

end.

简单演示:

unit ImpersonateUserDemoMain;

interface

uses
    Winapi.Windows, Winapi.Messages,
    System.SysUtils, System.Variants, System.Classes,
    Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
    ImpersonateUser;

type
    TImpersonateUserMainForm = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    UserNameEdit: TEdit;
    DomainEdit: TEdit;
    PasswordEdit: TEdit;
    ImpersonateButton: TButton;
    Label4: TLabel;
    FileNameEdit: TEdit;
    RevertToSelfButton: TButton;
    FileAccessButton: TButton;
    procedure FileAccessButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ImpersonateButtonClick(Sender: TObject);
    procedure RevertToSelfButtonClick(Sender: TObject);
    private
        FImpersonate : TImpersonateUser;
    end;

var
    ImpersonateUserMainForm: TImpersonateUserMainForm;

implementation

{$R *.dfm}

procedure TImpersonateUserMainForm.FileAccessButtonClick(Sender: TObject);
var
    Stream : TFileStream;
begin
    try
        if not FileExists(FileNameEdit.Text) then
            ShowMessage('File not found')
        else begin
            Stream := TFileStream.Create(FileNameEdit.Text, fmOpenRead);
            try
                ShowMessage('File opened');
            finally
                Stream.Free;
            end;
        end;
    except
        on E:Exception do
            ShowMessage(E.Classname + ': ' + E.Message);
    end;
end;

procedure TImpersonateUserMainForm.FormCreate(Sender: TObject);
begin
    UserNameEdit.Text := 'YourUsername';
    DomainEdit.Text   := '.';
    PasswordEdit.Text := 'YourPassword';
    FilenameEdit.Text := 'C:\Users\AnotherUser\Documents\HelloWorld.txt';
    FImpersonate      := TImpersonateUser.Create(Self);
end;

procedure TImpersonateUserMainForm.ImpersonateButtonClick(Sender: TObject);
begin
    if not FImpersonate.Logon(UserNameEdit.Text,
                              DomainEdit.Text,
                              PasswordEdit.Text) then begin
        ShowMessage(Format('Failed with error 0x%X', [FImpersonate.ErrorCode]));
    end
    else
        ShowMessage('Logon OK');
end;

procedure TImpersonateUserMainForm.RevertToSelfButtonClick(Sender: TObject);
begin
    FImpersonate.Logoff;
    ShowMessage('Reverted to self');
end;

end.

DFM 文件:

object ImpersonateUserMainForm: TImpersonateUserMainForm
  Left = 0
  Top = 0
  Caption = 'ImpersonateUserMainForm'
  ClientHeight = 142
  ClientWidth = 331
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 16
    Top = 20
    Width = 49
    Height = 13
    Caption = 'UserName'
  end
  object Label2: TLabel
    Left = 16
    Top = 48
    Width = 35
    Height = 13
    Caption = 'Domain'
  end
  object Label3: TLabel
    Left = 12
    Top = 76
    Width = 46
    Height = 13
    Caption = 'Password'
  end
  object Label4: TLabel
    Left = 16
    Top = 104
    Width = 16
    Height = 13
    Caption = 'File'
  end
  object UserNameEdit: TEdit
    Left = 80
    Top = 16
    Width = 121
    Height = 21
    TabOrder = 0
    Text = 'UserNameEdit'
  end
  object DomainEdit: TEdit
    Left = 80
    Top = 44
    Width = 121
    Height = 21
    TabOrder = 1
    Text = 'DomainEdit'
  end
  object PasswordEdit: TEdit
    Left = 80
    Top = 72
    Width = 121
    Height = 21
    TabOrder = 2
    Text = 'PasswordEdit'
  end
  object ImpersonateButton: TButton
    Left = 232
    Top = 14
    Width = 75
    Height = 25
    Caption = 'Impersonate'
    TabOrder = 3
    OnClick = ImpersonateButtonClick
  end
  object FileNameEdit: TEdit
    Left = 80
    Top = 99
    Width = 121
    Height = 21
    TabOrder = 4
    Text = 'FileNameEdit'
  end
  object RevertToSelfButton: TButton
    Left = 232
    Top = 45
    Width = 75
    Height = 25
    Caption = 'Revert to self'
    TabOrder = 5
    OnClick = RevertToSelfButtonClick
  end
  object FileAccessButton: TButton
    Left = 232
    Top = 76
    Width = 75
    Height = 25
    Caption = 'File access'
    TabOrder = 6
    OnClick = FileAccessButtonClick
  end
end

【讨论】:

  • 优秀的答案,+1。
  • @Rogier:是的,请将 fpiette 的回答标记为已接受。这告诉未来的读者,答案已被证明是有用的。
猜你喜欢
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 2011-02-01
  • 2013-01-19
相关资源
最近更新 更多