1. About Cookies
Cookies are a mechanism for storing persistent data
on the client.
As HTTP is a stateless protocol, cookies provide a way to maintain
information between client requests.
In a sense, a cookie may be though of as a small data area
on the client.
A cookie has the following properties:
Name |
mandatory |
Identifies the cookie (as if it were the name of a data area)
|
Value |
mandatory |
The contents of the cookie (as if it were the value of a data area).
Note that Mozilla browser (Firefox) does not support
blanks in the cookie value.
If that happens, the Set-Cookie string is trimmed right
at the first blank met.
Therefore it may be needed to substitute all the blanks
in a cookie value with some other character before creating
the cookie; in such a case, the opposite operation
should be performed after retrieving the cookie.
|
Expiration |
optional |
The date until which the cookie should survive.
If the expiration is not specified, the cookie expires when
the user session ends.
|
Domain |
optional |
The domain under which the cookie can be retrieved.
If the domain is not specified, the web browser
should assume the host name of the server generating
the cookie.
| Path |
optional |
The path under which the cookie can be retrieved.
If the path is not specified, the web browser
should assume the path of the page generating
the cookie.
|
HttpOnly |
optional |
HttpOnly is an additional flag included in a Set-Cookie HTTP response header.
Using the HttpOnly flag when generating a cookie helps mitigate the risk that a malicious client side
script (usually written in XSS) -if supported by the browser - to access the cookie
thus stealing security sensible information (example: passwords).
For more information, see
this page.
The use of this feature is highly recommended.
|
Secure |
optional |
Secure is another additional flag included in a Set-Cookie HTTP response header.
When specified, the cookie can be accessed only through a "secure" HTTP server (HTTPS).
|
Cookies are stored and retrieved by the web browser.
Whenever a web page is loaded, the web browser makes available
all the unexpired cookies that:
- match the domain of the page
(for instance, www.ibm.com or 195.183.8.2)
- are in the path of the page
(for instance, to page /cgidev2o/exhibiu8.htm
are made available all the cookies with path "/"
and all the cookies with path "/cgidev2o").
For a comprehensive guide about creating and using cookies,
see Website Cookies: Everything You Need To Know.
2. Creating a Cookie with a CGI - Basic approach
To create a cookie you must provide, in the external html,
a Set-Cookie http header, as follow:
/$top
Content-type: text/html
Set-Cookie:
name=value
[;EXPIRES=dateValue]
[;DOMAIN=domainName]
[;PATH=pathName]
[;SECURE]
[;HTTPONLY]
|
(mandatory blank line)
|
<html>
... etc. ...
|
|
|
For detail explanation about the Set-Cookie http header,
please refer to the following
page.
3. Retrieving a cookie in a CGI - Basic approach
A CGI can retrieve all the available cookies
through the environment variable HTTP_COOKIE.
The cookies made available are all those compliant with
the domain name and the path of the CGI.
The following example illustrates the value returned
from the environment variable HTTP_COOKIE
in a case where two cookies were found:
- the first cookie has name=IBM
and value=HG1V432
- the second cookie has name=Easy400
and value=JohnVincentBrown
IBM=HG1V432; Easy400=JohnVincentBrown
|
|
|
Note 1.
As all the available cookies are returned in a single string,
it is a program responsibility to retrieve from this string
the cookies it might be interested in.
Note 2.
The value of a cookie may contain escaped characters.
An escaped character is the ASCII hexadecimal representation
of an ASCII character. For instance,
%3D
is an escaped character and is the ASCII hexadecimal representation
of ASCII character "=".
Escaped characters are generated by the web browser when
storing a cookie. This is done to eliminate conflicts with
regulare string separator and control characters.
Now, it is a responsibility of the CGI program to convert
any ASCII escaped characters --in the value of a retrieved cookie--
to the corresponding EBCDIC characters.
See our example
about creating and retrieving a cookie in a CGI through this approach.
4. Creating/retrieving a cookie in a CGI - Advanced approach
Service program cgidev2/cgisrvpgm2
provides two subprocedures to help managing cookies in a CGI:
- crtCookie
allows for an easier construction of the
Set-Cookie http header
- getCookieByName
retrieves a given cookie from the
HTTP_COOKIE
environment variable.
/$top
Content-type: text/html
Expires: 0
/%setmycookie%/
<html> ... etc. ...
|
|
|
** Variables used to build the http header "Set-Cookie"
** through subprocedure "CrtCookie"
D SetMyCookie s 1000 varying
D CookieNam s 1000 varying
D CookieVal s 4000 varying
D RetCode s 10i 0
D Domain s 1000 varying
D Path s 1000 varying
D Secure s n
D Expires s z
D HttpOnly s n
D xdocloc s 512
** Other variables
D TimeNow s z
D r1 s 10i 0
D r2 s 10i 0
*=====================================================================
* Main line
*=====================================================================
/free
// Get broswer input
nbrVars=zhbgetinput(savedquerystring:qusec);
// Load external html, if not loaded yet
gethtml('DEMOHTML':'CGIDEV2':'COOKIE2':'/$');
// Create the Set-Cookie header
exsr CrtMyCook;
// Start the output html
updhtmlvar('setmycookie':SetMyCookie);
wrtsection('top');
// Retrieve cookie current value and display it
exsr RtvMyCook;
updHtmlVar('cookienam':CookieNam);
updHtmlVar('cookieval':CookieVal);
if CookieVal=' ';
wrtsection('cookieno');
else;
wrtsection('cookieyes');
endif;
// End the output html
UpdHtmlVar('timenow':%trim(%char(TimeNow)));
wrtsection('endhtml *fini');
return;
/end-free
*=====================================================================
* Create a cookie
* Name: ThreeMonths
* Value: current timestamp
* Domain: current CGI domain
* Path: /
* Secure: no
* Expires: three months from now
* HttpOnly:yes
*=====================================================================
/free
Begsr CrtMyCook;
//Retrieve the server domain into variable "Domain"; trim off the port number
exsr RtvDomain;
//Reset the domain to blank. The WEB browser assumes the host name of the server
// generating the cookie
Domain=' ';
//Set cookie name, cookie value and cookie path
CookieNam='ThreeMonths';
CookieVal=randomString(10);
Path='/';
//Set cookie expiration date & time
TimeNow=%timestamp;
Expires=TimeNow+%months(3);
//Set HttpOnly flag
//In this way the cookie can be accessed (created or read) only from the HTTP server.
//This prevents access from malicious javascripts (XSS).
HttpOnly=*on;
//In this example the "secure" flag is not used.
//It may be used to restrict the cookie access only to HTTPS
//(HTTP would not be able to access the cookie).
//Create the Set-Cookie header
SetMyCookie=CrtCookie(CookieNam:CookieVal:RetCode:Domain:
Path:*off:Expires:HttpOnly);
Endsr;
/end-free
*=====================================================================
* Retrieve the server domain
* The server domain is the one the URL of a document starts with,
* As an example, in the URL
* http://www.easy400.net/easy400p/maindown.html
* the server domain is
* www.easy400.net
*
* HOW TO SET THE DOMAIN OF THE COOKIE
* 1-APPROACH NUMBER ONE (deprecated)
* Usually, there is no easy way through which your CGI can find out
* what the server domain is.
* One way I found, is to have the document URL retrieved from some javascript
* and have it passed in an input variable of the form invoking the CGI.
* Example:
* <form name=cookie2 method=post action="/cgidev2p/cookie2.pgm">
* <script language=javascript>
* document.write("<input type=hidden name=xdocloc value='"+document.location+"'>")
* </script>
* ....
* </form>
* In this way the document URL is passed in the input variable "xdocloc".
* NOTE, however, that if a port number is specified, the port number is returned
* with the URL and it should not be part of the domain.
* 2-APPROACH NUMBER TWO (suggested)
* The easiest way is to specify no domain for the cookie. When this is done, the
* WEB browser assumes as domain of the cookie the name of the host creating the cookie.
*
* Though this subroutine uses approach number ONE to retrieve the domain name for the
* cookie, the program sets the domain name for the cookie to blank, thus making the
* WEB browser default the cookie domain to the name of the host creating the cookie.
*
*=====================================================================
/free
Begsr RtvDomain;
Domain=' ';
xdocloc=zhbgetvar('xdocloc'); //document location ("http://domain:port/...")
//Remove the URI ("/...") and the port number (if any)
r1=%scan('http://':xdocloc);
if r1=1;
r2=%scan('/':xdocloc:8);
if r2>8;
Domain=%subst(xdocloc:8:r2-8);
r1=%scan(':':Domain);
if r1>1;
Domain=%subst(Domain:1:r1-1);
endif;
endif;
endif;
Endsr;
/end-free
*=====================================================================
* Retrieve a cookie of given name
* Returns a string containing the current value of the cookie,
* or blanks if cookie not found
*=====================================================================
/free
Begsr RtvMyCook;
CookieNam='ThreeMonths';
CookieVal=GetCookieByName(CookieNam);
Endsr; |
See our example
about creating and retrieving a cookie in a CGI through this approach.
5. About privacy
Sending a cookie to a client device could be interpreted as a privacy violation.
This is why, when a site works with cookies, it is highly recommended to;
- notify the user that cookies will be used
- specify what kind of use they are for
- disable further process until the user checks a button after reading
(European contries have been given a regulation on this subject in September 2016,
see EU legislation on cookies).
For some information about different types of cookies and user defenses from them,
see How cookies track you around the web and how to stop them.
|