CGI::Cookie の使い方

Cookie の概要と使い方」で解説した通り、 Cookie の仕組みそのものはさほど難しいわけではありませんが、 Perl の Set-Cookie ヘッダーの生成は意外に面倒です。 ほとんどのウェブシステムでは Cookie を扱うことになります。 しかし、毎度、同じような面倒なコードを書いていては時間の無駄であるだけでなく、 バグの原因にもなりかねません。 そこで、環境変数 $ENV{HTTP_COOKIE} から Cookie 情報を抜き出したり、 Set-Cookie ヘッダーを生成したりできる定番の Perl モジュール「CGI::Cookie」 を紹介いたします。

目次

CGI::Cookie の概要

CGI::Cookie は Perl で Cookie を扱うために使う Perl モジュールの一つです。 CGI を Perl で開発する上で定番の CGI.pm の関連モジュールです。 そのため、CGI.pm を一緒に使うことを想定されているものの、 CGI::Cookie 単独で利用することも可能です。

CGI::Cookie の主な機能は 2 つです。1 つは Set-Cookie ヘッダーの生成、 もう一つは受信した Cookie のパースです。ここではそれぞれについて解説していきます。

Set-Cookie ヘッダーの生成

CGI::Cookie を使って Set-Cookie ヘッダーを生成するには、 次のようなコードを用意します。

use CGI::Cookie;

my $cookie = CGI::Cookie->new(
    -name  => "nickname",
    -value => "Taro"
);
print $cookie; # nickname=Taro; path=/

上記コードは CGI::Cookie を使って Set-Cookie ヘッダーを生成する必要最小限のコードです。 new() の引数に Cookie の名前や値、そして、属性を定義します。 このコードから nickname=Taro; path=/ という文字列が得られます。

ここで注意してほしいのは、path 属性に / が自動的にセットされる点です。 多くのシーンではこれで問題はありませんが、もし、パスを限定したいのであれば、 次のように path 属性を指定します。

my $cookie = CGI::Cookie->new(
    -name  => "nickname",
    -value => "Taro",
    -path  => "/lecture/cookie"
);

これで nickname=Taro; path=/lecture/cookie が得られます。 これを CGI の出力とする場合は、次のようなコードになります。

#!/usr/bin/perl
use strict;
use warnings;
use CGI::Cookie;

my $cookie = CGI::Cookie->new(
    -name  => "nickname",
    -value => "Taro",
    -path  => "/lecture/cookie"
);

print "Set-Cookie: ${cookie}\n";
print "Content-Type: text/plain\n";
print "\n";
print $cookie;

実行する

日本語を扱う

Cookie の概要と使い方」で解説した通り、Cookie 値で日本語を扱いたい場合は、 その値を URL エンコードする必要があります。 しかし、CGI::Cookie は、そのようなことを私たちが意識する必要がないいよう、必要に応じて、 自動的に URL エンコードを行ってくれます。

my $cookie = CGI::Cookie->new(
    -name  => "nickname",
    -value => "太郎",
    -path  => "/lecture/cookie"
);

このコードで nickname=%E5%A4%AA%E9%83%8E; path=/lecture/cookie が得られます。

実行する

属性をセットする

CGI::Cookie は CGI 仕様で規定されているすべての属性を扱うことができます。 以下のコードは、CGI::Cookie ですべての属性を指定したサンプルです。

my $cookie = CGI::Cookie->new(
    -name      => "nickname",
    -value     => "Taro",
    -path      => "/lecture/cookie",
    -expires   => "+1d",
    '-max-age' => "+1d",
    -domain    => ".futomi.com",
    -secure    => 1,
    -httponly  => 1,
    -samesite  => "Lax",
    -priority  => "High"
);

実行する

このコードによって、次のような値が得られます。

nickname=Taro; domain=.futomi.com; path=/lecture/cookie; expires=Tue, 07-Mar-2023 07:02:29 GMT; max-age=86400; secure; HttpOnly; SameSite=Lax; Priority=High

以下に、-name, -value, -path を除いた各パラメータを解説します。

-expires

expires 属性を表し、Cookie の有効期限を指定します。 -expires を指定しなければテンポラリ Cookie になります。 指定できる値は Tue, 07-Mar-2023 05:38:04 GMT のように直接 Cookie 仕様に 基づいた日時形式を指定することもできますが、+1d のように「1 日後」といった 現在からの相対時間の表現も受け付けます。指定できる値については後述します。

-max-age

max-age 属性を表し、Cookie の有効期限を現在からの相対時間を指定します。 -expires と同様に、+1d のように「1 日後」といった 現在からの相対時間の表現も受け付けます。 Cookie 仕様では秒で指定することとなっていますが、CGI::Cookie では秒で指定する必要はありません。 指定できる値については後述します。 なお、Cookie の仕様では、expires と max-age の両方が指定された場合、max-age が優先されます。

-domain

domain 属性を表し、Cookie が有効となるドメインを指定します。

-secure

secure 属性を表し、このフラグをセットしたい場合は、-secure1 をセットします。

-httponly

HttpOnly 属性を表し、このフラグをセットしたい場合は、-httponly1 をセットします。

-samesite

SameSite 属性を表します。-samesite には Lax, Strict, None のいずれかをセットします。

-priority

priority 属性を表します。-priority には Low, Medium, High のいずれかをセットします。 なお、この属性は 2023 年 3 月現在、Chromium ベースの Chrome と Edge でのみ有効です。

-samesite-priority は、 古いバージョンの CGI.pm がインストールされた環境ですと動作しない可能性があります。

-expires と -max-age に指定可能な値

-expires-max-age には、+1d のような、 現在から見た相対時間を表す文字を指定することができます。

my $cookie = CGI::Cookie->new(
    -name      => "nickname",
    -value     => "Taro",
    -expires   => "+1d",
    '-max-age' => "+1d"
);

実際に指定可能な値を例に挙げます。

表記例 説明
+30s 今から 30 秒
+10m 今から 10 分
+1h 今から 1 時間
-1d 昨日
now まさに今
+3M 3 ヶ月後
+10y 10 年後

上記の通り、先頭に + を付けると未来を、- を付けると過去を表します。 また、s は秒、m は分、h は時間、d は日、 M は月、y は年を表します。月だけ大文字ですので注意してください。

Cookie の値を読み取る

ブラウザーから送信された Cookie のデータは、環境変数 $ENV{HTTP_COOKIE} から得られます。 Cookie データは、Cookie 名と Cookie 値のペアが複数存在していても、一つの文字列として得られます。 また、日本語などの値は URL エンコードされています。 そこで CGI::Cookie は環境変数 $ENV{HTTP_COOKIE} の値を解析して、 Cookie 名を指定すれば URL デコード済みの Cookie 値を取り出す仕組みを提供してくれます。

次の CGI コードは、上記でセットした Cookie を読み取って画面に表示します。

use strict;
use warnings;
use CGI::Cookie;

print "Content-Type: text/plain; charset=utf-8\n";
print "\n";
my %cookies = CGI::Cookie->fetch;
print $cookies{screenname}->value;

上記コードを試す前に、まずは日本語の Cookie 値をブラウザーに仕込んでおきましょう。

Cookie 値を仕込む

これでブラウザーに Cookie 名 screenname、Cookie 値 山田太郎 のペアがセットされます。 下のボタンを押して、Cookie を読み取ってみましょう。

Cookie を読み取る

ブラウザーには 山田太郎 と表示されたはずです。 この通り、URL エンコード済みの値もデコードしてくれます。

まとめ

CGI::Cookie の基本的な使い方を解説しましたが、Cookie を簡単に扱えるようになることがお分かりいただけたのではないでしょうか。 CGI::Cookie は、このほか、CGI.pm を組み合わせて Set-Cookie ヘッダーを短いコードで出力する機能なども提供しています。 もし興味があれば、英語となりますが 原文のドキュメントをご覧ください。