はじめに
PHPでCookieを送信するとき、多くの場面では setcookie() を使います。しかし setcookie() はCookieの値を自動的にURLエンコード(urlencode())します。JWT・Base64・JSON形式のデータなど、エンコードされると意味が変わってしまう値をそのまま送りたい場合に登場するのが setrawcookie() です。
setrawcookie() は値を一切エンコードせずにCookieヘッダを送信します。値のフォーマットを完全にコントロールしたい場面、RFC準拠の特定フォーマットが必要な場面で欠かせない関数です。
関数の概要
| 項目 | 内容 |
|---|
| 関数名 | setrawcookie() |
| 所属 | PHP ネットワーク関数 |
| 導入バージョン | PHP 5.0以降 |
| PHP 8.x | 対応済み |
構文
配列形式(PHP 7.3以降・推奨)
setrawcookie(
string $name,
string $value = "",
array $options = []
): bool
引数形式(後方互換)
setrawcookie(
string $name,
string $value = "",
int $expires_or_options = 0,
string $path = "",
string $domain = "",
bool $secure = false,
bool $httponly = false
): bool
戻り値
- 成功時:
true
- 失敗時:
false(ヘッダが既に送信済みの場合など)
setrawcookie() vs setcookie() の核心的な違い
<?php
$value = 'hello world+foo=bar';
// setcookie() → 自動的に urlencode() する
setcookie('test', $value);
// 送信されるヘッダ: Set-Cookie: test=hello+world%2Bfoo%3Dbar
// setrawcookie() → そのまま送信する
setrawcookie('test', $value);
// 送信されるヘッダ: Set-Cookie: test=hello world+foo=bar
// ※ スペースや特殊文字がそのまま入るため、値は事前に適切なエンコードが必要
| 観点 | setcookie() | setrawcookie() |
|---|
| 値のエンコード | 自動で urlencode() | 一切エンコードしない |
| 受信時のデコード | $_COOKIE に届く際 urldecode() 済み | $_COOKIE に届く際もそのまま |
| 適した値 | 一般的なテキスト・数値 | JWT / Base64 / 独自フォーマット |
| RFC 準拠 | 基本的なケースで問題なし | 値フォーマットを完全制御したい場合 |
配列形式のオプション(PHP 7.3以降)
setrawcookie('token', $value, [
'expires' => time() + 3600,
'path' => '/',
'domain' => '.example.com',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax', // 'Strict' / 'Lax' / 'None'
]);
| オプションキー | 型 | 説明 |
|---|
expires | int | Unix タイムスタンプ(0 でセッションCookie) |
path | string | Cookieが有効なパス |
domain | string | Cookieが有効なドメイン |
secure | bool | HTTPS のみで送信 |
httponly | bool | JavaScript からアクセス不可 |
samesite | string | Strict / Lax / None |
基本的な使い方
<?php
// JWT をそのまま送信(setcookie だと . が %2E にエンコードされる恐れがある)
$jwt = 'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0Mn0.abc123';
setrawcookie('auth_token', $jwt, [
'expires' => time() + 3600,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Strict',
]);
// Base64エンコード済みデータをそのまま送信
$data = json_encode(['user_id' => 42, 'role' => 'admin']);
$encoded = rtrim(base64_encode($data), '='); // パディングを除去
setrawcookie('user_data', $encoded, [
'expires' => time() + 86400,
'path' => '/',
'httponly' => true,
'samesite' => 'Lax',
]);
実践的なクラスベースの使用例
例1:JWT Cookie 管理クラス
<?php
/**
* JWT をCookieに安全に格納するクラス
* setrawcookie() を使い JWT のドット区切りフォーマットを保持する
*
* JWT は "header.payload.signature" の形式で、
* setcookie() だと . が %2E にエンコードされる実装が存在するため
* setrawcookie() で値を保護する
*/
class JwtCookieManager
{
private const COOKIE_NAME = 'auth_token';
public function __construct(
private readonly string $secret,
private readonly int $ttl = 3600,
private readonly string $domain = '',
private readonly bool $secure = true
) {}
/**
* ペイロードから JWT を生成して Cookie にセットする
*/
public function issue(array $payload): string
{
$jwt = $this->createJwt($payload);
$result = setrawcookie(self::COOKIE_NAME, $jwt, [
'expires' => time() + $this->ttl,
'path' => '/',
'domain' => $this->domain,
'secure' => $this->secure,
'httponly' => true, // XSS対策:JS からアクセス不可
'samesite' => 'Strict', // CSRF対策
]);
if (!$result) {
throw new \RuntimeException('Cookie の送信に失敗しました(ヘッダー送信済みの可能性)');
}
echo "JWT Cookie 発行: " . substr($jwt, 0, 20) . "...\n";
return $jwt;
}
/**
* Cookie から JWT を取得して検証する
*/
public function verify(): ?array
{
$jwt = $_COOKIE[self::COOKIE_NAME] ?? null;
if ($jwt === null) {
return null;
}
return $this->validateJwt($jwt);
}
/**
* JWT Cookie を削除する(ログアウト)
*/
public function revoke(): void
{
setrawcookie(self::COOKIE_NAME, '', [
'expires' => time() - 86400,
'path' => '/',
'domain' => $this->domain,
'secure' => $this->secure,
'httponly' => true,
'samesite' => 'Strict',
]);
echo "JWT Cookie を削除しました\n";
}
private function createJwt(array $payload): string
{
$header = $this->base64UrlEncode(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$payload = $this->base64UrlEncode(json_encode(
array_merge($payload, ['exp' => time() + $this->ttl, 'iat' => time()])
));
$sig = $this->base64UrlEncode(
hash_hmac('sha256', "{$header}.{$payload}", $this->secret, true)
);
return "{$header}.{$payload}.{$sig}";
}
private function validateJwt(string $jwt): ?array
{
$parts = explode('.', $jwt);
if (count($parts) !== 3) {
return null;
}
[$header, $payload, $sig] = $parts;
$expected = $this->base64UrlEncode(
hash_hmac('sha256', "{$header}.{$payload}", $this->secret, true)
);
if (!hash_equals($expected, $sig)) {
echo "JWT 署名検証失敗\n";
return null;
}
$data = json_decode($this->base64UrlDecode($payload), true);
if (($data['exp'] ?? 0) < time()) {
echo "JWT 有効期限切れ\n";
return null;
}
return $data;
}
private function base64UrlEncode(string $data): string
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
private function base64UrlDecode(string $data): string
{
return base64_decode(strtr($data, '-_', '+/') . str_repeat('=', 3 - (3 + strlen($data)) % 4));
}
}
$manager = new JwtCookieManager(secret: 'my-secret-key-32bytes-long!!!!!');
// $token = $manager->issue(['user_id' => 42, 'role' => 'admin']);
// $payload = $manager->verify();
例2:Base64URL エンコード Cookie クラス
<?php
/**
* 任意のデータを Base64URL エンコードして Cookie に保存するクラス
* setrawcookie() で + や = などの特殊文字をそのまま送信できる
* 構造化データ(配列など)を安全に Cookie に格納する
*/
class Base64CookieStorage
{
public function __construct(
private readonly string $prefix = 'app_',
private readonly int $defaultTtl = 86400,
private readonly bool $secure = false // ローカル開発用デフォルト
) {}
/**
* 任意のデータを Base64URL エンコードして Cookie にセットする
*/
public function set(string $name, mixed $data, ?int $ttl = null): bool
{
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
$encoded = $this->base64UrlEncode($json);
$result = setrawcookie(
$this->prefix . $name,
$encoded,
[
'expires' => time() + ($ttl ?? $this->defaultTtl),
'path' => '/',
'secure' => $this->secure,
'httponly' => true,
'samesite' => 'Lax',
]
);
echo "Cookie セット: {$this->prefix}{$name} = " . substr($encoded, 0, 20) . "...\n";
return $result;
}
/**
* Cookie から Base64URL デコードしてデータを取得する
*/
public function get(string $name, mixed $default = null): mixed
{
$encoded = $_COOKIE[$this->prefix . $name] ?? null;
if ($encoded === null) {
return $default;
}
$json = $this->base64UrlDecode($encoded);
if ($json === false) {
return $default;
}
return json_decode($json, true) ?? $default;
}
/**
* Cookie を削除する
*/
public function delete(string $name): bool
{
return setrawcookie(
$this->prefix . $name, '',
[
'expires' => time() - 86400,
'path' => '/',
'secure' => $this->secure,
'httponly' => true,
'samesite' => 'Lax',
]
);
}
/**
* Cookie が存在するか確認する
*/
public function has(string $name): bool
{
return isset($_COOKIE[$this->prefix . $name]);
}
private function base64UrlEncode(string $data): string
{
// + → -、/ → _、= を除去(URL安全な Base64)
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
private function base64UrlDecode(string $data): string|false
{
$decoded = base64_decode(strtr($data, '-_', '+/'), true);
return $decoded;
}
}
$storage = new Base64CookieStorage(prefix: 'myapp_', secure: false);
// $storage->set('user_prefs', ['theme' => 'dark', 'lang' => 'ja', 'tz' => 'Asia/Tokyo']);
// $prefs = $storage->get('user_prefs');
// print_r($prefs);
例3:署名付き改ざん防止 Cookie クラス
<?php
/**
* Cookie の値に HMAC 署名を付与して改ざんを検出するクラス
* setrawcookie() で署名を含む値をエンコードなしで送信する
*
* 値の形式: base64url(json_data).hmac_signature
*/
class SignedCookieManager
{
public function __construct(
private readonly string $signingKey,
private readonly string $algo = 'sha256',
private readonly int $ttl = 3600,
private readonly bool $secure = true
) {
if (strlen($signingKey) < 32) {
throw new \InvalidArgumentException('署名キーは32文字以上にしてください');
}
}
/**
* 署名付き Cookie をセットする
*/
public function set(string $name, mixed $value, ?int $ttl = null): bool
{
$payload = $this->base64UrlEncode(json_encode([
'data' => $value,
'exp' => time() + ($ttl ?? $this->ttl),
]));
$signature = $this->sign($payload);
$cookieVal = "{$payload}.{$signature}";
return setrawcookie($name, $cookieVal, [
'expires' => time() + ($ttl ?? $this->ttl),
'path' => '/',
'secure' => $this->secure,
'httponly' => true,
'samesite' => 'Lax',
]);
}
/**
* 署名を検証して値を取得する
*/
public function get(string $name, mixed $default = null): mixed
{
$raw = $_COOKIE[$name] ?? null;
if ($raw === null) {
return $default;
}
$parts = explode('.', $raw, 2);
if (count($parts) !== 2) {
echo "Cookie フォーマット不正: {$name}\n";
return $default;
}
[$payload, $signature] = $parts;
// 署名検証(タイミング攻撃対策に hash_equals を使用)
if (!hash_equals($this->sign($payload), $signature)) {
echo "Cookie 署名検証失敗: {$name}(改ざんの可能性)\n";
return $default;
}
$decoded = json_decode($this->base64UrlDecode($payload), true);
if (!$decoded || ($decoded['exp'] ?? 0) < time()) {
echo "Cookie 有効期限切れ: {$name}\n";
return $default;
}
return $decoded['data'] ?? $default;
}
/**
* Cookie を削除する
*/
public function delete(string $name): bool
{
return setrawcookie($name, '', [
'expires' => time() - 86400,
'path' => '/',
'secure' => $this->secure,
'httponly' => true,
'samesite' => 'Lax',
]);
}
private function sign(string $payload): string
{
return $this->base64UrlEncode(
hash_hmac($this->algo, $payload, $this->signingKey, true)
);
}
private function base64UrlEncode(string $data): string
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
private function base64UrlDecode(string $data): string
{
return base64_decode(strtr($data, '-_', '+/'));
}
}
$cookieManager = new SignedCookieManager(
signingKey: 'super-secret-signing-key-32chars',
secure: false // 開発環境
);
// $cookieManager->set('cart', ['items' => ['A', 'B'], 'total' => 3200]);
// $cart = $cookieManager->get('cart');
// print_r($cart);
例4:多言語・タイムゾーン設定 Cookie クラス
<?php
/**
* ユーザーの言語・タイムゾーン・UI 設定を Cookie に保存するクラス
* 設定値には記号(Asia/Tokyo, ja_JP.UTF-8 など)が含まれるため
* setrawcookie() で Base64URL エンコードして送信する
*/
class UserPreferenceCookieHandler
{
private const COOKIE_NAME = 'user_prefs';
private const VERSION = 1;
private array $defaults = [
'lang' => 'ja',
'timezone' => 'Asia/Tokyo',
'theme' => 'light',
'currency' => 'JPY',
'date_fmt' => 'Y/m/d',
];
public function __construct(
private readonly int $ttl = 365 * 86400, // 1年
private readonly bool $secure = false
) {}
/**
* ユーザー設定を Cookie に保存する
*/
public function save(array $prefs): bool
{
$data = array_merge($this->defaults, $prefs, [
'_v' => self::VERSION,
'_ts' => time(),
]);
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
$encoded = $this->encode($json);
$result = setrawcookie(self::COOKIE_NAME, $encoded, [
'expires' => time() + $this->ttl,
'path' => '/',
'secure' => $this->secure,
'httponly' => false, // JS からも読む必要がある場合は false
'samesite' => 'Lax',
]);
echo "ユーザー設定保存: lang={$data['lang']}, tz={$data['timezone']}\n";
return $result;
}
/**
* Cookie からユーザー設定を読み込む
*/
public function load(): array
{
$encoded = $_COOKIE[self::COOKIE_NAME] ?? null;
if ($encoded === null) {
return $this->defaults;
}
$json = $this->decode($encoded);
if ($json === false) {
return $this->defaults;
}
$data = json_decode($json, true);
if (!is_array($data)) {
return $this->defaults;
}
// バージョンチェック
if (($data['_v'] ?? 0) !== self::VERSION) {
echo "設定のバージョンが異なります。デフォルトを使用します。\n";
return $this->defaults;
}
return array_merge($this->defaults, $data);
}
/**
* 個別設定を更新する
*/
public function update(string $key, mixed $value): bool
{
$current = $this->load();
$current[$key] = $value;
return $this->save($current);
}
/**
* Cookie を削除してデフォルトに戻す
*/
public function reset(): bool
{
return setrawcookie(self::COOKIE_NAME, '', [
'expires' => time() - 86400,
'path' => '/',
'secure' => $this->secure,
'samesite' => 'Lax',
]);
}
private function encode(string $data): string
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
private function decode(string $data): string|false
{
return base64_decode(strtr($data, '-_', '+/'));
}
}
$handler = new UserPreferenceCookieHandler(secure: false);
// $handler->save(['lang' => 'en', 'timezone' => 'America/New_York', 'theme' => 'dark']);
// $prefs = $handler->load();
// print_r($prefs);
例5:Cookie セキュリティ監査クラス
<?php
/**
* setrawcookie() と setcookie() を比較検証し、
* Cookie の値が正確に送信されるかをテストするクラス
*/
class CookieEncodingAuditor
{
private array $testCases = [];
/**
* テストケースを追加する
*/
public function addCase(string $name, string $value, string $description): void
{
$this->testCases[] = compact('name', 'value', 'description');
}
/**
* 各テストケースで setcookie と setrawcookie の出力を比較する
*/
public function runComparison(): void
{
echo "=== Cookie エンコード比較テスト ===\n\n";
foreach ($this->testCases as $case) {
echo "【{$case['description']}】\n";
echo " 元の値 : {$case['value']}\n";
echo " setcookie : " . $this->simulateSetCookie($case['value']) . "\n";
echo " setrawcookie: " . $case['value'] . " (そのまま)\n";
$needsRaw = $this->needsRawCookie($case['value']);
echo " 推奨 : " . ($needsRaw ? "✅ setrawcookie()" : "✅ setcookie()") . "\n";
echo "\n";
}
}
/**
* setcookie() が適用する urlencode を模倣する
*/
private function simulateSetCookie(string $value): string
{
return urlencode($value);
}
/**
* 値に URLエンコードで変化する文字が含まれるか確認する
*/
private function needsRawCookie(string $value): bool
{
return urlencode($value) !== $value;
}
/**
* 推奨される Cookie 送信方法を診断する
*/
public function recommend(string $value): string
{
if (!$this->needsRawCookie($value)) {
return 'setcookie() で問題ありません';
}
// JWT の形式か確認(header.payload.signature)
if (preg_match('/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/', $value)) {
return 'setrawcookie() を推奨(JWT フォーマット)';
}
// Base64URL の形式か確認
if (preg_match('/^[A-Za-z0-9_-]+=*$/', $value)) {
return 'setrawcookie() を推奨(Base64URL フォーマット)';
}
return 'setrawcookie() を推奨(特殊文字を含む)';
}
}
$auditor = new CookieEncodingAuditor();
$auditor->addCase('normal', 'hello world', '通常テキスト(スペースあり)');
$auditor->addCase('jwt', 'eyJ.eyJ.abc', 'JWT トークン');
$auditor->addCase('base64', 'aGVsbG8=', 'Base64エンコード値(= を含む)');
$auditor->addCase('json_enc', 'name%3Dalice', 'URLエンコード済み文字列');
$auditor->addCase('plain', 'alice42', '英数字のみ');
$auditor->runComparison();
$testValues = [
'eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0Mn0.abc123',
'hello world',
'user=alice&role=admin',
];
echo "=== 推奨診断 ===\n";
foreach ($testValues as $v) {
printf(" %-50s → %s\n", substr($v, 0, 45), $auditor->recommend($v));
}
/*
出力例:
=== Cookie エンコード比較テスト ===
【通常テキスト(スペースあり)】
元の値 : hello world
setcookie : hello+world
setrawcookie: hello world (そのまま)
推奨 : ✅ setrawcookie()
【JWT トークン】
元の値 : eyJ.eyJ.abc
setcookie : eyJ.eyJ.abc
setrawcookie: eyJ.eyJ.abc (そのまま)
推奨 : ✅ setcookie()
...
*/
例6:Cookie ファクトリクラス
<?php
/**
* Cookie の値の種類に応じて setcookie() と setrawcookie() を
* 自動的に使い分けるファクトリクラス
*/
class SmartCookieFactory
{
private array $defaultOptions;
public function __construct(array $defaultOptions = [])
{
$this->defaultOptions = array_merge([
'expires' => 0,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => true,
'samesite' => 'Lax',
], $defaultOptions);
}
/**
* 値の内容に応じて最適な関数で Cookie を送信する
*/
public function send(string $name, string $value, array $options = []): bool
{
$opts = array_merge($this->defaultOptions, $options);
$method = $this->selectMethod($value);
echo "Cookie 送信: {$name} (メソッド: {$method})\n";
return $method === 'raw'
? setrawcookie($name, $value, $opts)
: setcookie($name, $value, $opts);
}
/**
* JWT 専用の送信(setrawcookie + セキュリティ設定強化)
*/
public function sendJwt(string $name, string $jwt, int $ttl = 3600): bool
{
$this->validateJwtFormat($jwt);
return setrawcookie($name, $jwt, array_merge($this->defaultOptions, [
'expires' => time() + $ttl,
'secure' => true,
'httponly' => true,
'samesite' => 'Strict',
]));
}
/**
* Base64URL エンコード済みデータの送信
*/
public function sendBase64(string $name, mixed $data, int $ttl = 86400): bool
{
$encoded = rtrim(strtr(base64_encode(json_encode($data)), '+/', '-_'), '=');
return setrawcookie($name, $encoded, array_merge($this->defaultOptions, [
'expires' => time() + $ttl,
]));
}
/**
* Cookie を削除する
*/
public function delete(string $name): bool
{
// 削除時はどちらの関数でも問題ないが統一のため setrawcookie を使用
return setrawcookie($name, '', array_merge($this->defaultOptions, [
'expires' => time() - 86400,
]));
}
/**
* 値の種類からどちらの関数を使うか判断する
*/
private function selectMethod(string $value): string
{
// JWT の形式(header.payload.signature)
if (preg_match('/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/', $value)) {
return 'raw';
}
// Base64URL の形式
if (preg_match('/^[A-Za-z0-9_\-]+=*$/', $value) && strlen($value) > 20) {
return 'raw';
}
// + や = や特殊文字が含まれるか
if (urlencode($value) !== $value) {
return 'raw';
}
return 'normal';
}
private function validateJwtFormat(string $jwt): void
{
$parts = explode('.', $jwt);
if (count($parts) !== 3) {
throw new \InvalidArgumentException('JWT の形式が正しくありません(header.payload.signature)');
}
}
}
$factory = new SmartCookieFactory([
'secure' => false, // 開発環境
'httponly' => true,
'samesite' => 'Lax',
]);
// $factory->send('username', 'alice'); // → setcookie()
// $factory->send('token', 'eyJ.eyJ.abc'); // → setrawcookie()
// $factory->sendBase64('prefs', ['theme' => 'dark', 'lang' => 'ja']);
// $factory->delete('old_cookie');
setcookie() と setrawcookie() の選択フローチャート
Cookie に送りたい値は?
│
├─ JWT(header.payload.signature 形式)
│ → setrawcookie() ✅
│
├─ Base64 / Base64URL エンコード済みデータ
│ → setrawcookie() ✅
│
├─ 独自の署名付きトークン(特殊文字含む)
│ → setrawcookie() ✅
│
├─ 特殊文字を含まない一般テキスト・数値
│ → setcookie() ✅
│
└─ 日本語など非ASCII文字
→ setcookie()(自動エンコード)または
setrawcookie(urlencode($value))(手動エンコード)
よくある落とし穴
<?php
// ❌ NG:setrawcookie() に生の特殊文字を渡すとヘッダーが壊れる
setrawcookie('data', 'hello world'); // スペースが入ってヘッダー不正
// ✅ 送る前に適切なエンコードをする
setrawcookie('data', rawurlencode('hello world')); // → hello%20world
// または
setrawcookie('data', base64_encode('hello world')); // → aGVsbG8gd29ybGQ=
<?php
// ❌ よくある誤解:setrawcookie() は「より安全」というわけではない
// エンコードしないということは「不正な文字がそのまま送られる」リスクがある
setrawcookie('raw', $_POST['input']); // ← ユーザー入力を直接渡すのは危険!
// ✅ 値は必ず自前でエンコード・バリデーション済みにする
$safe = base64_encode(json_encode(['data' => $_POST['input']]));
setrawcookie('safe', $safe, ['httponly' => true, 'samesite' => 'Lax']);
<?php
// ❌ NG:setcookie() で送った値を setrawcookie() で削除しようとすると
// 名前のエンコードが一致しない場合がある
setcookie('my cookie', 'value'); // Set-Cookie: my+cookie=value
setrawcookie('my cookie', ''); // Set-Cookie: my cookie= ← 異なるCookieとして扱われることがある
// ✅ 送受信で一貫して同じ関数を使う
setrawcookie('my_cookie', 'value'); // アンダースコアを使って名前に特殊文字を含めない
setrawcookie('my_cookie', '', ['expires' => time() - 86400]); // 削除も setrawcookie で統一
まとめ
| 項目 | 内容 |
|---|
| 関数名 | setrawcookie(string $name, string $value, array|int $options): bool |
| 主な用途 | 値をURLエンコードせずにCookieを送信する |
setcookie() との違い | 値の自動 urlencode() を行わない |
| 適した値 | JWT / Base64URL / 独自署名トークン / エンコード済み文字列 |
| 配列形式 | PHP 7.3以降で SameSite を含む全オプションを配列で指定可能 |
| 注意点 | 値に改行・スペース・制御文字が含まれるとヘッダーが壊れる |
| ユーザー入力 | 直接渡さず必ず事前にエンコード・バリデーションする |
setrawcookie() は「Cookie の値を自分でコントロールしたい」すべての場面で活躍します。JWT・Base64・署名付きトークンなど、現代の認証・認可の実装では setrawcookie() の出番が多く、setcookie() との適切な使い分けがセキュアな実装の鍵になります。
参考リンク