Illya Pavlov
Illya Pavlov

Reputation: 263

How do I set the cookie HTTPOnly flag?

How do I set the HTTPOnly flag? the TCookie does not have any method or property to specify the HTTPOnly flag.

Upvotes: 1

Views: 2229

Answers (2)

Alexandre M
Alexandre M

Reputation: 1216

Please note that original TCookie.GetHeaderValue() method in HTTPApp.pas may generate an incorrect header when expires > -1 and the system/application time separator is other than the colon sign ":". TCookie.GetHeaderValue() method uses the constant sDateFormat for formatting "Expires" field. Turns out that sDateFormat constant is wrongly defined as:

const 
  sDateFormat = '"%s", dd "%s" yyyy hh:nn:ss';

If the user changes the application TimeSeparator variable to something other than the colon sign ":", then the expires time will be formated using another character, and not ":" as specified in RFC 1123 (format “Wdy, DD Mon YYYY HH:MM:SS GMT”). Google Chrome 25+ rejects cookies with time separator other than the colon sign, causing complete failure of web applications using it. So the correct GetHeaderValue() method should be:

function TCookieGenerator.GetHeaderValue: string;
const
  _DateFormat = '"%s", dd "%s" yyyy hh":"nn":"ss'; // this is the correct constant. HTTPApp.pas wrongly declares it as sDateFormat = '"%s", dd "%s" yyyy hh:nn:ss';
begin
  Result := Format('%s=%s; ', [HTTPEncode(FName), HTTPEncode(FValue)]);
  if Domain <> '' then
    Result := Result + Format('domain=%s; ', [Domain]);  { do not localize }
  if Path <> '' then
    Result := Result + Format('path=%s; ', [Path]);      { do not localize }
  if Expires > -1 then
    Result := Result +
      Format(FormatDateTime('"expires="' + _DateFormat + ' "GMT; "', Expires),  { do not localize }
        [DayOfWeekStr(Expires), MonthStr(Expires)]);
  if Secure then Result := Result + 'secure; ';  { do not localize }
  if HttpOnly then Result := Result + 'HttpOnly';  { do not localize }
  if Copy(Result, Length(Result) - 1, MaxInt) = '; ' then
    SetLength(Result, Length(Result) - 2);
end;

I've submited QC #113139 about this.

Upvotes: 0

Robert Love
Robert Love

Reputation: 12581

If you talking about TCookie in HttpApp.pas then there is no built in property to support HttpOnly.

You can look at httpApp.pas at the TCookie.GetHeaderValue: string; implementation to verify.

However a Cookie is just something set in the Header and TWebResponse has a CustomHeaders property. Where you could Call Response.CustomHeaders.Add(MyCookieValue);

The following class is a modified version of TCookie to support HttpOnly that you can use to generate the cookie correctly.

unit CookieGen;

interface
uses
 Sysutils,Classes,HttpApp;
type
  TCookieGenerator = class(TObject)
  private
    FName: string;
    FValue: string;
    FPath: string;
    FDomain: string;
    FExpires: TDateTime;
    FSecure: Boolean;
    FHttpOnly: Boolean;
  protected
    function GetHeaderValue: string;
  public
    property Name: string read FName write FName;
    property Value: string read FValue write FValue;
    property Domain: string read FDomain write FDomain;
    property Path: string read FPath write FPath;
    property Expires: TDateTime read FExpires write FExpires;
    property Secure: Boolean read FSecure write FSecure;
    property HttpOnly : Boolean read FHttpOnly write FHttpOnly;
    property HeaderValue: string read GetHeaderValue;
  end;

implementation

{ TCookieGenerator }

function TCookieGenerator.GetHeaderValue: string;
begin
  Result := Format('%s=%s; ', [HTTPEncode(FName), HTTPEncode(FValue)]);
  if Domain <> '' then
    Result := Result + Format('domain=%s; ', [Domain]);  { do not localize }
  if Path <> '' then
    Result := Result + Format('path=%s; ', [Path]);      { do not localize }
  if Expires > -1 then
    Result := Result +
      Format(FormatDateTime('"expires="' + sDateFormat + ' "GMT; "', Expires),  { do not localize }
        [DayOfWeekStr(Expires), MonthStr(Expires)]);
  if Secure then Result := Result + 'secure; ';  { do not localize }
  if HttpOnly then Result := Result + 'HttpOnly';  { do not localize }
  if Copy(Result, Length(Result) - 1, MaxInt) = '; ' then
    SetLength(Result, Length(Result) - 2);

end;

end.

Upvotes: 5

Related Questions