TypeScript向けSQLクエリビルダーである Kysely は、型安全で非常に快適にSQLを扱えるライブラリです。
しかし、開発運用の中で 「実行されたSQLとパラメータのログを取りたいけれど、個人情報やパスワードなどの機密情報(PII)がログに残ってしまう」 という課題に直面したことはないでしょうか?
この問題を解決するために、指定したパラメータをログ出力時に判定・マスキングできるようにするライブラリ kysely-sensitive-param を作りました(v1.0.1 をリリースしました)。
- npm: kysely-sensitive-param (v1.0.1)
- GitHub: juner/kysely-sensitive-param (Release v1.0.1)
この記事では、開発背景と具体的な使い方について紹介します。
背景:Kysely のログ出力と「センシティブデータ」のジレンマ
Kysely では、インスタンス生成時に log オプションを指定することで、実行されたクエリのログを取得できます。
import { Kysely, PostgresDialect } from 'kysely'
const db = new Kysely<Database>({
dialect: new PostgresDialect({ ... }),
log(event) {
if (event.level === 'query') {
console.log('SQL:', event.query.sql)
console.log('Params:', event.query.parameters)
}
},
})
パラメータを含めたログを取りたい理由
event.query.sql だけを出力した場合、プレースホルダー($1, $2 など)のみがログに残るため、「実際にどのような値でクエリが実行されたのか」 を追うことが難しくなります。そのため、本番環境のデバッグや障害調査では event.query.parameters もセットでログサービス(Datadog, CloudWatch 等)に送信したくなります。
しかし、そのまま出力すると危険
パラメータをそのまま出力してしまうと、以下のようなセンシティブなデータが平文でログに残る危険性があります。
- ユーザーのパスワードハッシュや平文パスワード
- クレジットカード情報や個人情報(住所・電話番号など)
- 認証トークンやAPIキー
「デバッグのためにパラメータを出したい」ものの「コンプライアンスやセキュリティの観点から平文ログは許容できない」というジレンマが発生します。
解決策:kysely-sensitive-param
kysely-sensitive-param を使うことで、クエリ構築時に特定パラメータを markSensitive() でマークし、ログ出力時にそれを判定・置換できるようになります。
インストール
npm install kysely-sensitive-param
# または pnpm / yarn / bun
基本設定:SensitiveUnwrappingDialect の適用
まず、使用しているDialectを SensitiveUnwrappingDialect でラップしてKyselyインスタンスを生成します。
import { Kysely, PostgresDialect } from 'kysely'
import { markSensitive, SensitiveUnwrappingDialect } from 'kysely-sensitive-param'
const db = new Kysely<Database>({
// Dialectをラップする
dialect: new SensitiveUnwrappingDialect({
dialect: new PostgresDialect({ ... }),
}),
log(event) {
if (event.level === 'query') {
console.log('SQL:', event.query.sql)
// ここでパラメータを処理する(後述)
}
}
})
// クエリ実行例
await db
.insertInto('user')
.values({
name: 'Taro',
password: markSensitive('my-secret-password'), // センシティブな値をマーク
})
.execute()
DB実行時には本来の生のパラメータにアンラップされて正しくクエリが実行されますが、log イベントへ渡ってくるパラメータ上ではマークされた状態のオブジェクトが残ります。
ログの処理方法
パラメータからセンシティブ情報を判定・抽出する方法として、「直接判定して自前でフォーマットする方法」 と 「makeLogging で簡略化する方法」 の2パターンを用意しています。
1. 直接 isSensitive / getSensitiveValue を使って自前でフォーマットする
Kyselyの log 関数内でパラメータを判定・取得できます。マスキング方法を自由に変更したい場合や、既存のログ基盤へ組み込む際に便利です。
import { isSensitive, getSensitiveValue } from 'kysely-sensitive-param'
const db = new Kysely<Database>({
dialect: new SensitiveUnwrappingDialect({ ... }),
log(event) {
if (event.level === 'query') {
// パラメータを個別チェックして伏字化
const safeParams = event.query.parameters.map((param) => {
if (isSensitive(param)) {
// getSensitiveValue(param) で生の入力値を取り出すことも可能
return '[REDACTED]'
}
return param
})
console.log('SQL:', event.query.sql)
console.log('Params:', safeParams)
}
},
})
2. makeLogging でログ処理を簡略化する
この判定や置換処理を毎回書くのが大変な場合は、makeLogging を使うことでシンプルに記述できます。
import { makeLogging, SensitiveUnwrappingDialect } from 'kysely-sensitive-param'
const db = new Kysely<Database>({
dialect: new SensitiveUnwrappingDialect({ ... }),
log: makeLogging({
// ログを有効化
enableLogging: true,
// センシティブ内容は表示しない
enableSensitiveDataLogging: false,
// センシティブプレースホルダー
sensitivePlaceholder: "[REDACTED]"
}),
})
まとめ
ログから漏洩する機密情報のリスクは、事故が起きる前に対策しておくことが重要です。
Kyselyを使っていて「パラメータログは取りたいけれど、特定のデータだけ安全に隠したい」「自前で柔軟にログフォーマットを制御したい」という方は、ぜひ kysely-sensitive-param を試してみてください!
フィードバックや Issue / PR もお待ちしております。