我目前正在将几个Windows桌面应用程序移植到一个Web站点。
当前的设置包括几个SQL Server后端数据库,只configurationWindows身份validation(SSPI),每个用户/组/angular色对特定对象都有特定的权限。 这很方便,因为应用层不必实现任何访问控制。
我想保持它与Web服务器,Windows机器上的Apache相同的方式。 但是与数据库的每个连接都是使用Apache的帐户进行的。 这是可以理解和预料的,事实上Apache是故意给予公共数据的访问,以便能够提供公共内容。
但是,如果域用户login(login过程已经实现),我希望处理该请求的Apache进程模拟该用户,并因此在整个请求期间充当他们。
起初,我尝试了PHP的fastcgi.impersonate技巧,使用IIS作为Web服务器。 但是我最终放弃了,主要是因为(1)我们必须移植到Apache,(2)它是PHP特有的,事实certificate我们应该把整个Web服务器作为一个整体。
所以我将searchredirect到Apache模块。 几个月的研究没有结果,除了mod_auth_sspi之类,这显然不是我要找的(身份validation和模仿是两个不同的东西)。
最后,我决定做自己的模块。 我能find的大多数“101”例子都是用C语言编写的,但是我在Lazarus / FPC中find了2-3个,这是我一直使用的一段时间,但从来没有这样的任务。
我知道我必须build立一个.dll项目,我知道(或多或less)使用什么单位,我知道像LogonUser()
和ImpersonateLoggedOnUser()
函数应该在我的工具箱中。
有没有人做过类似的事情? 任何人都可以指出我正确的方向?
一个例子是值得赞赏的,即使它是一个简单的概念certificate。 这个问题远非要求最终的,确定的解决scheme。
我最终想出了以下几点:
library mod_winimpersonate; {$mode objfpc}{$H+} uses SysUtils, Windows, httpd, apr, Classes; function DefaultHandler(r: Prequest_rec): Integer; cdecl; Var cookies:TStringList; logindata,username,password:String; p:Integer; begin RevertToSelf; cookies:=TStringList.Create; cookies.Delimiter:=';'; cookies.DelimitedText:=apr_table_get(r^.headers_in,'COOKIE'); logindata:=URLDecode(cookies.Values['WinImpersonate']); If Length(logindata)>0 then Begin p:=Pos(':',logindata); username:=LeftStr(logindata,p-1); password:=RightStr(logindata,Length(logindata)-p); ChangeLoggedInUser(username,password,''); End; Result:=DECLINED; end; procedure RegisterHooks(p: Papr_pool_t); cdecl; begin ap_hook_handler(@DefaultHandler, nil, nil, APR_HOOK_REALLY_FIRST); end; var Themodulee: module; exports Themodulee name 'winimpersonate_module'; begin FillChar(Themodulee, sizeof(Themodulee), 0); STANDARD20_MODULE_STUFF(Themodulee); with Themodulee do begin name := 'mod_winimpersonate.dll'; register_hooks := @RegisterHooks; end; end.
这绝不是最终的解决方案,但这是一个开始。 逻辑如下:
恢复到Apache帐户。 这是必须的 ,以防我们使用先前冒充别人的回收Apache线程。
从名为“WinImpersonate”的cookie中以username:password
的形式检索用户的凭证。 这需要更多的工作(可能是加密证书,或将它们存储在安全的(?)放置在服务器上或更安全的地方)
在windows
单元的帮助下模拟用户。
返回DECLINED
,所以Apache知道我们没有处理请求,它应该继续询问正确的处理程序的模块。
有很多问题需要解决,为了达到一个体面的安全水平。 其中包括保护凭据和浏览器缓存。 但正如我所说,这是一个开始,也是一个概念的证明。
您会注意到上面列表中缺少两个实用程序功能:
URLDecode
解码一个url编码的字符串:
// Convert URLEncoded string to utf8 string function URLDecode(const s: String): String; var sAnsi: String; sUtf8: String; sWide: WideString; i, len: Cardinal; ESC: string[2]; CharCode: integer; c: char; begin sAnsi := PChar(s); SetLength(sUtf8, Length(sAnsi)); i := 1; len := 1; while (i <= Cardinal(Length(sAnsi))) do begin if (sAnsi[i] <> '%') then begin if (sAnsi[i] = '+') then c := ' ' else c := sAnsi[i]; sUtf8[len] := c; Inc(len); end else begin Inc(i); ESC := Copy(sAnsi, i, 2); Inc(i, 1); try CharCode := StrToInt('$' + ESC); c := Char(CharCode); sUtf8[len] := c; Inc(len); except end; end; Inc(i); end; Dec(len); SetLength(sUtf8, len); sWide := UTF8Decode(sUtf8); len := Length(sWide); Result := sWide; end;
ChangeLoggedInUser
尝试使用所提供的凭据登录用户,一旦成功尝试模拟他:
Function ChangeLoggedInUser(username, password, domain: string):Boolean; var creds: Cardinal; begin Result:=False; try if LogonUser(PChar(username) ,PChar(domain) ,PChar(password) ,LOGON32_LOGON_NETWORK_CLEARTEXT ,LOGON32_PROVIDER_DEFAULT ,creds ) then begin ImpersonateLoggedOnUser(creds); Result:=True; end; finally //wipe the memory for security FillChar(username,SizeOf(username),#0); FillChar(password,SizeOf(username),#0); FillChar(domain,SizeOf(username),#0); end; //try-finally end;
希望有人认为这有用。 评论是比欢迎 。