Indy 〜 10.1.16 :
331: function TIdNetscapeCookie.IsValidCookie
begin
//....(省略)
Result := FDomain = RightStr(AServerHost, Length(FDomain));
修正
var ACookieDomain : String;
begin
//....(省略)
// Result := FDomain = RightStr(AServerHost, Length(FDomain));
// bad code : FDomain = RightStr(AServerHost, Length(FDomain));
// FDomain AServerHost
// xxxx.yyyy.com , zxxxx.yyyy.com : is not valid
// xxxx.yyyy.com , xxxx.yyyy.com : is valid
// .xxxx.yyyy.com , xxxx.yyyy.com : is valid
// .xxxx.yyyy.com , yxxxx.yyyy.com : is not valid
if (Copy(FDomain,1 , 1)='.') then
ACookieDomain := FDomain
else
ACookieDomain := '.' + FDomain;
Result := ( ACookieDomain = RightStr('.' + AServerHost, Length(ACookieDomain)) );
Indy10.2.3 :
- Result := TextEndsWith(AServerHost, FDomain);
+ // bad code : Result := TextEndsWith(AServerHost, FDomain);
+ // TextEndsWith('noexample.com', 'example.com') = true : is not valid
+ if (Copy(FDomain,1 , 1)='.') then
+ Result := TextEndsWith('.' + AServerHost, FDomain)
+ else
+ Result := TextEndsWith('.' + AServerHost, '.' + FDomain);
IdCookieManager.pas
ドメインの比較にPos関数を使ってはいけません。
135: function TIdCookieManager.GenerateCookieList
begin
//....(省略)
if IndyPos(LCookiesByDomain[i], URL.Host) > 0 then
修正
TempCookiesDomain : String;
begin
//....(省略)
// if IndyPos(LCookiesByDomain[i], URL.Host) > 0 then
TempCookiesDomain := LCookiesByDomain[i];
if (Copy(TempCookiesDomain,1 , 1) <> '.') then TempCookiesDomain := '.' + TempCookiesDomain;
if (TempCookiesDomain = RightStr('.' + URL.Host, Length(TempCookiesDomain))) then
注意
Netscapeの仕様書では、/で終わらない場合、最後のパス名は部分一致になっています
IE7もそういう動作をしているようです。
firefox2では、部分一致にはなっていません。
firefox2風に、/で終わるパスと同様の動作にするには以下のようにします。
function TIdCookieManager.GenerateCookieList
if Pos(LCookieList.Cookies[j].Path, URL.Path) = 1 then
-->
// if Pos(LCookieList.Cookies[j].Path, URL.Path) = 1 then
if (RightStr(LCookieList.Cookies[j].Path,1) = '/') then
begin
if Pos(LCookieList.Cookies[j].Path, URL.Path) <> 1 then continue;
end
else
begin
if Pos(LCookieList.Cookies[j].Path + '/', URL.Path) <> 1 then continue;
end;
procedure TIdCookieManager.DoAdd
if Length(ACookie.Domain) = 0 then LDomain := AHost
else LDomain := ACookie.Domain;
-->
if Length(ACookie.Domain) = 0 then LDomain := AHost
else LDomain := ACookie.Domain;
while (copy(LDomain, 1 , 1)='.') do LDomain := copy(LDomain, 2 , Length(LDomain));
if (LDomain ='') then exit;
(省略)の部分は、ここでわかりやすくするために
省略しただけで誤解しないようにお願いします
Indy 9
IdCookie.pas
単純に切り落とすと間違った判定をします。
function TIdNetscapeCookie.IsValidCookie
begin
//....(省略)
if IsHostname(AServerHost) then
begin
if IsHostName(FDomain) then
result := FDomain = RightStr(AServerHost,Length(FDomain))
else
result := FDomain = RightStr(DomainName(AServerHost),Length(FDomain));
end
修正
var ACookieDomain : String;
begin
//....(省略)
if IsHostname(AServerHost) then
begin
// if IsHostName(FDomain) then
// result := FDomain = RightStr(AServerHost,Length(FDomain))
// else
// result := FDomain = RightStr(DomainName(AServerHost),Length(FDomain));
if (Copy(FDomain,1 , 1)='.') then
ACookieDomain := FDomain
else
ACookieDomain := '.' + FDomain;
Result := ( ACookieDomain = RightStr('.' + AServerHost, Length(ACookieDomain)) );
end
IdCookieManager.pas
ドメインの比較にPos関数を使ってはいけません。
function TIdCookieManager.GenerateCookieList
begin
//....(省略)
if IndyPos(Uppercase(LCookiesByDomain[i]), Uppercase(URL.Host + URL.path)) > 0 then {FLX}
修正
ACookieDomain : String;
begin
//....(省略)
// if IndyPos(Uppercase(LCookiesByDomain[i]), Uppercase(URL.Host + URL.path)) > 0 then {FLX}
ACookieDomain := LCookiesByDomain[i];
if (Copy(ACookieDomain ,1 , 1) <> '.') then ACookieDomain := '.' + ACookieDomain;
if (ACookieDomain = RightStr('.' + URL.Host, Length(ACookieDomain))) then
注意
Netscapeの仕様書では、/で終わらない場合、最後のパス名は部分一致になっています
IE7もそういう動作をしているようです。
firefox2では、部分一致にはなっていません。
firefox2風に、/で終わるパスと同様の動作にするには以下のようにします。
if Pos(LCookieList.Cookies[j].Path, URL.Path) = 1 then
-->
// if Pos(LCookieList.Cookies[j].Path, URL.Path) = 1 then
if (RightStr(LCookieList.Cookies[j].Path,1) = '/') then
begin
if Pos(LCookieList.Cookies[j].Path, URL.Path) <> 1 then continue;
end
else
begin
if Pos(LCookieList.Cookies[j].Path + '/', URL.Path) <> 1 then continue;
end;
if Length(ACookie.Domain) = 0 then LDomain := AHost
else LDomain := ACookie.Domain;
-->
if Length(ACookie.Domain) = 0 then LDomain := AHost
else LDomain := ACookie.Domain;
LDomain := Sys.Trim(LDomain);
if ((LDomain <> '')and(IsHostname(LDomain))) then
if (Copy(LDomain,1,1) <> '.') then LDomain := '.' + LDomain;
(省略)の部分は、ここでわかりやすくするために
省略しただけで誤解しないようにお願いします
バグの確認のためのコード DOS版FORM版
※コピーは、IEのみ動作します
バグの確認のためのコード DOS版
program testCookieManager;
{$APPTYPE CONSOLE}
uses
IdCookieManager,IdURI, IdGlobal;
var TestCount : integer = 0;
function CookieManager_check(Cookie, HostName , AccessURL : string;
Accept : Boolean; mesg : string) : Integer;
var
aURI : TIdURI;
IdCookieManager1 : TIdCookieManager;
s : string;
isError : boolean;
begin
Result := 0;
aURI := TIdURI.Create(AccessURL);
IdCookieManager1 := TIdCookieManager.Create(nil);
try
try
IdCookieManager1.CookieCollection.Clear;
IdCookieManager1.AddCookie(Cookie, HostName);
IdCookieManager1.AddCookie('Set-Cookie: name2=value2; path=/tea/; domain=dummy', 'dummy');
// aURI.URI := AccessURL;
s := IdCookieManager1.GenerateCookieList(aURI,False);
if (Accept) then
isError := (s = '')
else
isError := (s <> '');
if (isError) then
begin
Result := 1;
Inc(TestCount);
Writeln(' _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ ' ,TestCount);
Writeln(mesg);
Writeln(' Cookie '+Cookie);
Writeln(' CookieHostName '+HostName);
Writeln(' AccessURL '+AccessURL);
Writeln(' GenerateCookieList('+aURI.URI+') = ' + s);
end;
except
Result := -1;
Writeln('internal error');
end;
finally
aURI.Free;
IdCookieManager1.Free;
end;
end;
function TestCookie() : Integer;
var
r : integer;
begin
Result := 0;
// r := CookieManager_check('Set-Cookie: name1=value1; path=/; domain=localhost'
r := CookieManager_check('Set-Cookie: name1=value1;'
, 'abc.test'
, 'http://testabc.test/index'
, false
, 'bug: send other domain''s cookie');
if (r<>0) then Result := r;
r := CookieManager_check('Set-Cookie: name1=value1;'
, 'deny.abc.abc'
, 'http://nodeny.abc.abc/index'
, false
, 'bug: send other domain''s cookie');
if (r<>0) then Result := r;
r := CookieManager_check('Set-Cookie: name1=value1;'
, 'all.abc'
, 'http://all.abc/index'
, true
, 'bug: can not get own cookie');
if (r<>0) then Result := r;
r := CookieManager_check('Set-Cookie: name1=value1; domain=.all.abc'
, 'all.abc'
, 'http://all.abc/index'
, true
, 'bug: can not get own cookie');
if (r<>0) then Result := r;
r := CookieManager_check('Set-Cookie: name1=value1; path=/;'
, 'denyall.abc'
, 'http://denyall.abc.localhost/index'
, false
, 'bug: send other domain''s cookie');
if (r<>0) then Result := r;
r := CookieManager_check('Set-Cookie: name1=value1; path=/;'
, 'denyall.abc'
, 'http://denyall.bc/index'
, false
, 'bug: send other domain''s cookie');
if (r<>0) then Result := r;
if (Result = 0) then Writeln(' ok : testCookieManager');
end;
begin
Writeln(gsIdProductName + ' ' + gsIdVersion);
Halt(TestCookie());
end.
バグの再現コードです。 (上のphpとセットで使用します)
-----------------------------------------------
example code
-----------------------------------------------
procedure TForm1.Button1Click(Sender: TObject);
var i:Integer;
begin
Memo1.Text := IdHTTP1.Get(Edit1.Text);
Memo2.Clear;
for i := 0 to IdCookieManager1.CookieCollection.Count-1 do
Memo2.Lines.Add(IdCookieManager1.CookieCollection.Items[i].CookieText);
end;
Indy OpenSSL now uses the standard OpenSSL libraries
for pre-compiled win32 dlls, see:
http://www.openssl.org/related/binaries.html
recommended v0.9.8a or later