Cookie - Cookie Object with Encryption or Signature
use Cookie; my $c = Cookie->new( name => 'my-cookie', domain => 'example.com', value => 'sid1234567', path => '/', expires => '+10D', # or alternatively maxage => 864000 # to make it exclusively accessible by regular http request and not javascript http_only => 1, same_site => 'Lax', # should it be used under ssl only? secure => 1, ); # make the cookie expired # Sets the expiration datetime to Thu, 01 Jan 1970 09:00:00 GMT $c->elapse; # Get cookie as an hash reference my $hash = $c->as_hash; print $c->as_string, "\n"; # or print "$c\n"; # If expires is set, we can use its underlying DateTime object my $now = DateTime->now; if( $c->expires && $c->expires > $now ) { # ok, we're good } # Unset expiration, effectively transforming it into a session cookie $c->expires( undef ); print "Is session cookie? ", $c->is_session ? 'yes' : 'no', "\n"; $c->match_host( 'www.example.com' ); # Set max-age (in seconds) that takes precedence over expiration $c->max_age( 86400 ); # Make it expired to tell the http client to remove it: $c->max_age(0) # or $c->max_age(-1) # Unset max-age $c->max_age( undef ); print "Is it same? ", $c->same_as( $other ) ? 'yes' : 'no', "\n"; # Conveniently set port, path and domain in one go, but not the secure flag $c->uri( 'https://www.example.com:8080/some/where' ); # Create encrypted cookie # You can generate a key or type one as long as it meets the size requirement use Bytes::Random::Secure (); my $c = Cookie->new( name => 'my-cookie', domain => 'example.com', value => 'sid1234567', path => '/', expires => '+10D', # or alternatively maxage => 864000 # to make it exclusively accessible by regular http request and not ajax http_only => 1, same_site => 'Lax', # should it be used under ssl only? secure => 1, # Encryption parameters key => Bytes::Random::Secure::random_bytes(32), algo => 'AES', encrypt => 1, ); print( "My encrypted cookie: $c\n" ); # Sign cookie only my $c = Cookie->new( name => 'my-cookie', domain => 'example.com', value => 'sid1234567', path => '/', expires => '+10D', # or alternatively maxage => 864000 # to make it exclusively accessible by regular http request and not ajax http_only => 1, same_site => 'Lax', # should it be used under ssl only? secure => 1, # Encryption parameters # No size constraint for signature, but obviously the longer the better key => Bytes::Random::Secure::random_bytes(32), sign => 1, ); print( "My signed cookie: $c\n" );
v0.1.11
This is a powerful and versatile package to create and represent a cookie compliant with the latest standard as set by rfc6265. This can be used as a standalone module, or can be managed as part of the cookie jar Cookie::Jar
The object is overloaded and will call "as_string" upon stringification and can also be used in comparison with other cookie object:
if( $cookie1 eq $cookie2 ) { # do something }
This module does not die upon error, but instead returns undef and sets an error, so you should always check the return value of a method.
undef
See also the Cookie::Jar package to manage server and client side handling of cookies:
use Cookie::Jar; # Possibly passing the cookie repository the Apache2::RequestRec object my $jar = Cookie::Jar->new( $r ); my $c = $jar->make( name => 'my_cookie', value => 'some value', domain => 'example.org', path => '/', secure => 1, http_only => 1, ) || die( $jar->error ); # Set it in the server response C<Set-Cookie> header: $jar->set( $c ) || die( $jar->error );
Provided with an hash or hash reference of parameters, and this initiates a new cookie object and return it. Each of the following parameters has a corresponding method.
Optional. If set with a positive integer, this will activate verbose debugging message
Other extra parameters not directly related to the cookie standard:
Set or get the datetime of the cookie object last accessed.
According to rfc6265, section 5.3.12.3, when deciding which cookies to remove, for those who have equal removal priority:
"If two cookies have the same removal priority, the user agent MUST evict the cookie with the earliest last-access date first."
This set or get the the algorithm used to encrypt the cookie value.
It can be any of AES, Anubis, Blowfish, CAST5, Camellia, DES, DES_EDE, KASUMI, Khazad, MULTI2, Noekeon, RC2, RC5, RC6, SAFERP, SAFER_K128, SAFER_K64, SAFER_SK128, SAFER_SK64, SEED, Skipjack, Twofish, XTEA, IDEA, Serpent or simply any <NAME> for which there exists Crypt::Cipher::<NAME>
See also Stackoverflow on the choice of encryption algorithm
By default, the algorithm is set to AES
AES
If the algorithm set is unsupported, this method returns an error
It returns the current value as a scalar object
Provided with an hash ore hash reference of cookie parameter, and this will apply them to each of their equivalent method.
$c->apply( expires => 'now', secure => 1, http_only => 1, );
In the example above, this will call methods "expires", "secure" and "http_only" passing them the relevant values.
It returns the current object.
Returns an hash reference of the cookie value.
The hash reference returned will contain the following keys: name value comment commentURL domain expires http_only implicit max_age path port same_site secure version
name
value
comment
commentURL
domain
expires
http_only
implicit
max_age
path
port
same_site
secure
version
Returns a string representation of the object.
my $cookie_string = $cookie->as_string; # or my $cookie_string = "$cookie"; my-cookie="sid1234567"; Domain=example.com; Path=/; Expires=Mon, 09 Jan 2020 12:17:30 GMT; Secure; HttpOnly
If encryption is enabled with "encrypt", the cookie value will be encrypted using the key provided with "key" and the Initialisation Vector. If the latter was not provided, it will be generated automatically. The resulting encrypted value is then encoded in base64 and escaped. For example:
my $cookie_value = "toc_ok=1"; my $key = Bytes::Random::Secure::random_bytes(32); # result: # session=PyJTlRJniAYVJJF6%2FswuPw%3D%3D; Path=/; SameSite=Lax; Secure; HttpOnly
If cookie signature is enabled for integrity protection with "sign", an sha256 hmac will be generated using the key provided with "key" and the resulting hash appended to the cookie value separated by a dot. For example:
my $cookie_value = "toc_ok=1"; my $key = "hard to guess key"; # I2M4/rh/TiNV5RZDSBJkhLblBvrN5k9448G6w/gp/jg= my $signature = Crypt::Mac::HMAC::hmac_b64( $key, $cookie_value ); # result: toc_ok=1.I2M4/rh/TiNV5RZDSBJkhLblBvrN5k9448G6w/gp/jg= # ultimately the cookie value sent will be: # toc_ok%3D1.I2M4%2Frh%2FTiNV5RZDSBJkhLblBvrN5k9448G6w%2Fgp%2Fjg%3D
The returned value is cached so the next time, it simply return the cached version and not re-process it. You can reset it by calling "reset".
$cookie->comment( 'Some comment' ); my $comment = $cookie->comment;
Sets or gets the optional comment for this cookie. This was used in version 2 of cookies but has since been deprecated.
Returns a Module::Generic::Scalar object.
$cookie->commentURL( 'https://example.com/some/where.html' ); my $comment = $cookie->commentURL;
Sets or gets the optional comment URL for this cookie. This was used in version 2 of cookies but has since been deprecated.
Returns an URI object.
Set or get the datetime of the cookie object created. This value is primarily used by Cookie::Jar, as per the rfc6265, when setting the http request header Cookie to differentiate two cookies that share the same domain and path. The cookie that has their creation datetime earlier are set first:
Cookie
"Among cookies that have equal-length path fields, cookies with earlier creation-times are listed before cookies with later creation-times." (rfc6265, section 5.4.2)
This returns the cookie decrypted value. If it used on a non-encrypted cookie, this would return undef and set an error
It takes an optional hash or hash reference of parameters:
The algorithm to use for encryption. Defaults to the value set with "algo". See this method for more information on acceptable values.
The Initialisation Vector used for encryption and decryption. Default to the value set with "initialisation_vector"
The encryption key. Defaults to the value set with "key"
Boolean. Set or get this value to true to flag this cookie to be discarded, whatever that means to you the user. This is not a standard protocol property.
This method is used in "save_as_lwp" in Cookie::Jar and "save_as_netscape" in Cookie::Jar with the option skip_discard
skip_discard
It returns the current value as a Module::Generic::Boolean object.
$cookie->domain( 'example.com' ); my $dom = $cookie->domain;
Sets or gets the domain for this cookie.
Returns the current value as a Module::Generic::Scalar object.
Note that you can also call it using the alias method host
host
Set the expires value for this cookie to 0, which, in turn, will set it to Thu, 01 Jan 1970 09:00:00 GMT
0
Thu, 01 Jan 1970 09:00:00 GMT
When sent to the http client, this will have the effect of removing the cookie.
See rfc6265 for more information.
Set or get the boolean value. If true, the this will tell "as_string" to encrypt the cookie value.
To use this feature, an encryption key must be set and the module Crypt::Cipher must be installed.
You can read more about the differences between sign and encryption at Stackoverflow
Sets or gets the expiration date and time for this cookie.
The value provided can be one of:
For example: 01 Nov 2021 08:42:17 GMT
01 Nov 2021 08:42:17 GMT
For example: 1631099228
1631099228
For example: 30s (30 seconds), 5m (5 minutes), 12h (12 hours), 30D (30 days), 2M (2 months), 1Y (1 year)
30s
5m
12h
30D
2M
1Y
However, this is not sprintf, so you cannot combine them, thus you cannot do this: 5m1D
5m1D
now
Special keyword
Ultimately, a DateTime will be derived from those values, or undef will be returned and an error will be set.
The DateTime object will be set with a formatter to allow a stringification that is compliant with rfc6265.
And you can use "max_age" alternatively.
See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Date
Note that a cookie without an expiration datetime is referred as a session cookie, so setting the cookie expiration change a cookie from being a session cookie to being a more permanent cookie.
session cookie
As documented, if expiration is "unspecified, the cookie becomes a session cookie. A session finishes when the client shuts down, after which the session cookie is removed."
Returns an array object of cookie fields available. This is essentially used by "apply"
Alias for "domain"
This is an alias for "implicit". It has been added to comply with the language of rfc6265, section 5.3.6
If the domain attribute was not provided by the server for this cookie, then: "set the cookie's host-only-flag to true." and "set the cookie's domain to the canonicalized request-host"
Returns the current value as a Module::Generic::Boolean object (that is stringifyable).
Sets or gets the boolean for httpOnly
httpOnly
Returns a Module::Generic::Boolean object.
Alias for "http_only"
This boolean is set to true if the domain was not initially set and has been derived from the current host.
Set or get the Initialisation Vector used for cookie encryption. If you do not provide one, it will be automatically generated. If you want to provide your own, make sure the size meets the encryption algorithm size requirement.
To find the right size for the Initialisation Vector, for example for algorithm AES, you could do:
perl -MCrypt::Cipher::AES -lE 'say Crypt::Cipher::AES->blocksize'
which would yield 16
16
Returns true if this cookie has an expiration datetime set and it has expired, i.e. the expiration datetime is in the past. Otherwise, it returns false.
Return value is in the form of a Module::Generic::Boolean object that stringifies to 1 or 0;
Boolean. This returns true if the cookie sent from the server is not a session cookie, i.e. it has an "expires" value set.
See rfc62655, section 5.3.3
Returns true if this is a session cookie, i.e. it has no expiration datetime nor any "max_age" set, otherwise, it returns false.
Sets or gets the boolean value. This is a legacy method of old cookie module, but not used anymore.
This takes an optional hash or hash reference of parameters.
It returns true if the cookie was signed and the signature is valid, or false otherwise.
If an error occurred, this method returns undef and sets an error instead, so check the return value.
my $rv = $c->is_valid; die( $c->error ) if( !defined( $rv ) ); print( "Cookie is valid? ", $rv ? 'yes' : 'no', "\n" );
Possible parameters are:
The encryption key to use to sign and verify the cookie signature. Defaults to the value set with "key"
This is an alias for "initialisation_vector"
Set or get the encryption key used to encrypt the cookie value. This is used when "encrypt" or "sign" are set to true.
When used for cookie encryption, make sure the key size is big enough to satisfy the encryption algorithm requirement, which you can check with, say for AES:
perl -MCrypt::Cipher::AES -lE 'say Crypt::Cipher::AES->keysize'
In this case, it will yield 32. Replace above AES, byt whatever algorithm you have chosen.
32
perl -MCrypt::Cipher::Blowfish -lE 'say Crypt::Cipher::Blowfish->keysize'
would yield 56 for Blowfish
56
Blowfish
You can use "random_bytes" in Bytes::Random::Secure to generate a random key:
# will generate a 32 bytes-long key my $key = Bytes::Random::Secure::random_bytes(32);
Provided with an host name and this returns true if this cookie domain either is a perfect match or if the "implicit" flag is on and the cookie domain is a subset of the host provided.
Otherwise this returns false.
Sets or gets the integer value for Max-Age
Max-Age
This value should be an integer representing the number of seconds until this cookie expires.
As per the rfc6265, Max-Age takes precedence over Expires when set, so if you set this, any value set with "expires" will be discarded.
Expires
Returns a Module::Generic::Number object.
Alias for "max_age"
Sets or gets the cookie name.
As per the Mozilla documentation, a cookie name cannot contain any of the following charadcters:
\(\)\<\>\@\,\;\:\\\"\/\[\]\?\=\{\}
Sets or gets the path.
Sets or gets the port number.
Set the reset flag to true, which will force "as_string" to recompute the string value of the cookie.
Provided with another object and this returns true if it has the same property values, false otherwise.
This is used in overloaded object comparison, such as:
print( "Same cookie\n" ) if( $c1 eq $c2 ); # or print( "Same cookie\n" ) if( $c1 == $c2 );
Sets or gets the boolean value for Same-Site.
Same-Site
The proper values should be Relaxed, Strict or None, but this module does not enforce the value you set. Setting a proper value is your responsibility.
Relaxed
Strict
None
See Mozilla documentation for more information.
If set to None, secure should be set to true.
See rfc 6265 for more information.
Alias for "same_site".
Sets or gets the boolean value for Secure.
Secure
Set or get the boolean value. If true, then the cookie value will be signed. The way this works, is that "hmac_b64" in Crypt::Mac::HMAC will create a SHA256 encrypted digest using the encryption key you provided with "key" and attach the signature to the cookie value separated by a dot. For example:
SHA256
my $cookie_value = "toc_ok=1"; my $key = "hard to guess key"; my $signature = Crypt::Mac::HMAC::hmac_b64( $key, $cookie_value ); # signature is I2M4/rh/TiNV5RZDSBJkhLblBvrN5k9448G6w/gp/jg= # cookie resulting value before uri encoding: # toc_ok%3D1.I2M4/rh/TiNV5RZDSBJkhLblBvrN5k9448G6w/gp/jg=
So, you need to have the module Crypt::Mac installed to be able to use this feature.
Signature are used to ensure data integrity protection for content that are not secret.
For more secret content, use "encrypt".
You can read more about the difference between sign and encryption at Stackoverflow
If a value is provided, it will be transformed into a URI object, and its port, path and host components will be used to set the values for "port", "path" and "domain" respectively.
Otherwise, with no value provided, this will form an URI object based on the cookie secure flag, domain, port, and path
$c->uri( 'https://www.example.com:8080/some/where?q=find+me' ); # sets host to www.example.com, port to 8080 and path to /some/where my $uri = $c->uri; # get an uri based on cookie properties value, such as: # https://www.example.com:8080/some/where
Sets or gets the value for this cookie.
Sets or gets the cookie version. This was used in version 2 of the cookie standard, but has since been deprecated by rfc6265.
Given a DateTime object, or by default will instantiate a new one, and this will set its formatter to DateTime::Format::Strptime with the appropriate format to ensure the stringification produces a rfc6265 compliant datetime string.
This method is used so that if the cookie object is part of some data encoded into json, this will convert the cookie data properly to be used by JSON
As shown in the "SYNOPSIS" you can sign cookies effortlessly. This package has taken all the hassle of doing it for you.
To use this feature you need to have installed Crypt::Mode::CBC which is part of CryptX
The methods available to use for cookie integrity protection are: "key", "sign" to enable cookie signature, "is_valid" to check if the signature is valid.
Cookie signature is performed by CryptX, which is an XS module, and thus very fast.
As shown in the "SYNOPSIS" you can encrypt cookies effortlessly. This package has taken all the hassle of doing it for you.
The methods available to use for cookie encryption are: "algo" to set the desired algorithm, "key", "encrypt" to enable encryption, "decrypt" to decrypt the cookie value, and optionally "initialisation_vector".
Cookie encryption is performed by CryptX, which is an XS module, and thus very fast.
As usual, to install this module, you can do:
perl Makefile.PL make make test sudo make install
If you have Apache/modperl2 installed, this will also prepare the Makefile and run test under modperl.
The Makefile.PL tries hard to find your Apache configuration, but you can give it a hand by specifying some command line parameters. See Apache::TestMM for available parameters or you can type on the command line:
perl -MApache::TestConfig -le 'Apache::TestConfig::usage()'
For example:
perl Makefile.PL -apxs /usr/bin/apxs -port 1234 # which will also set the path to httpd_conf, otherwise perl Makefile.PL -httpd_conf /etc/apache2/apache2.conf # then make make test sudo make install
See also modperl testing documentation
But, if for some reason, you do not want to perform the mod_perl tests, you can use NO_MOD_PERL=1 when calling perl Makefile.PL, such as:
NO_MOD_PERL=1
perl Makefile.PL
NO_MOD_PERL=1 perl Makefile.PL make make test sudo make install
Jacques Deguest <jack@deguest.jp>
Cookie::Jar, Apache2::Cookies, APR::Request::Cookie
rfc6265
Latest tentative version of the cookie standard
Copyright (c) 2019-2021 DEGUEST Pte. Ltd.
You can use, copy, modify and redistribute this package and associated files under the same terms as Perl itself.
To install Cookie, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Cookie
CPAN shell
perl -MCPAN -e shell install Cookie
For more information on module installation, please visit the detailed CPAN module installation guide.