これはなに?
業務でCookieを用いた実装することがあったので備忘録として書きました。
記事の下の方ではSet-Cookieの各属性について説明したのでCookieを調べている方の一助になれば幸いです。
この記事のゴール
以下の理解が得られることをゴールとします
- NestJSでリクエストからCookieを取得できること
- NestJSでレスポンスにCookieを詰められること
- Set-Cookieの各属性について理解できること
手順
0. NestJSの導入
導入がまだの方は以前書いたこちらの記事を参考にしてもらえると理解が早いと思います。
1. cookie-parserの導入
今回は公式でも書かれているcookie-parser
を利用します。
2022年1月時点で最新のものを利用しています。
パッケージ名 | バージョン |
---|---|
cookie-parser | v1.4.6 |
@types/cookie-parser | v1.4.2 |
$ npm i cookie-parser
$ npm i -D @types/cookie-parser
2. ルートモジュールに適用する
ルートモジュールにcookieを利用できるように記載します。
公式では以下のように書かれています。
import * as cookieParser from 'cookie-parser';
// somewhere in your initialization file
app.use(cookieParser());
今回NestJSのプロジェクトでは以下のように書きました。
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import * as cookieParser from 'cookie-parser'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.use(cookieParser())
await app.listen(3000)
}
bootstrap()
3. リクエストでcookieを取得する
import { Controller, Get, Req } from '@nestjs/common'
import { AppService } from './app.service'
import { Request } from 'express'
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get('/')
getHello(
@Req() request: Request,
): string {
console.log('cookie: ', request.cookies)
return this.appService.getHello()
}
}
cookie: [Object: null prototype] {}
もちろんなにも設定していないのでcookieの中身は何もありません
4. Cookieに任意の値を設定する
import { Controller, Get, Req, Res } from '@nestjs/common'
import { AppService } from './app.service'
import { Response } from 'express'
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get('/')
getHello(@Res({ passthrough: true }) response: Response): string {
response.cookie('key', 'value', {
signed: false,
expires: new Date('2022-12-31'),
httpOnly: true,
path: '/',
domain: 'localhost',
secure: true,
sameSite: 'lax',
})
return this.appService.getHello()
}
}
cookie: { key: 'value' }
今回はkeyにkey
、valueにvalue
を設定しました。
出力結果から分かるように設定されていることがわかります。
response.cookie('key', 'value', options)
の部分で実際にCookieを設定しています。
singed, expires...等々ありますがそこはoptionsの設定になります。
optionsは以下の記事を参考にしながら設定しました。
optionsの各属性の説明は以下になります。
maxAge
型: number
- Cookieの有効期限を秒数で表している
- Set-Cookieの仕様では秒数だが、実際に指定する際はミリ秒なので注意が必要
- 負の数値だった場合は即時期限切れになる
- 下でも登場するexpiresと両方設定されている場合はmaxAgeが優先される
signed
型: boolean
- 署名付きCookieを利用するかどうかを設定可能
- 利用する場合はcookieParser()にsecretを設定する必要がある
expires
型: Date
- Cookieの有効期限を日時タイムスタンプで表している。
- 指定されなかった場合はセッションと同じ有効期限になる
httpOnly
型: boolean
- HTTPテキスト内のスクリプトでCookieにアクセスできなくするもの
- これによってサイトがXSSの脆弱性を持っていても、Cookieの流出を防ぐことができる
path
型: string
- Cookieを送信するパスの条件
- 上で記載した例だと
/
をpathに含んだページ(同じドメイン内でほぼ全てのページ)に対してCookieが送信される -
/docs
と設定するとhogehoge.com/docs
以下のページにのみCookieが送信される
domain
型: string
- Cookieの送信先を設定できる
- 指定されなかった場合は自動的に現在のURLのホスト名が入る
secure
型: boolean
- HTTPS通信でのみCookieを送信する
- 安全でないサイト(http:)との通信を防ぐ役割をしている
encode
型: ((val: string) => string)
- valueで設定した値をどのようにエンコードするかを設定できる
sameSite
型: boolean | "lax" | "strict" | "none"
- 他のドメインへのリクエストを送る際にCokieの情報を付与するかどうかを設定できる
-
strict
では付与しない -
lax
ではGETメソッドであれば付与される -
none
では全てのリクエストに付与する
-
- これによりCSRFのような脆弱性を生むのを防ぐことができる
余談
全然関係ないのですが昨日参加したQiitaアドベントカレンダーのイベントでちょまどさんの言っていた「記事を書くことが恩返しになると思っている」と言うのを聞いてすごい良い考えだなと思いました。
自分じゃどうしようもない課題に直面した際に、調べて出てきた記事は全て救世主みたいですもんね。
そんな風に困っている誰かのためになれれば良いなと言うモチベーションで来週も書いていこうと思います。
ではまた!
参考記事