SecureValue API
SecureValue 型は機密データを安全に保存するために使用され、メモリロック、自動ゼロクリア、マスク機能を提供します。
スレッドセーフ
SecureValue のすべてのメソッドはスレッドセーフで、複数の goroutine で並行使用可能です:
- 読み取りメソッド(
String()、Bytes()、Length()、Masked())は読み取りロックを使用し、並行読み取りをサポート - クローズメソッド(
Close()、Release())は書き込みロックを使用し、安全なゼロクリアを保証 - 状態チェック(
IsClosed()、IsMemoryLocked())はアトミック操作を使用
secret := env.GetSecure("API_KEY")
if secret != nil {
defer secret.Release()
// 並行読み取りは安全
go func() { fmt.Println(secret.Masked()) }()
go func() { fmt.Println(secret.Length()) }()
}注意
Close() と Release() は 1 回のみ呼び出すべきです。重複呼び出しは安全ですが無効です。
作成
NewSecureValue
func NewSecureValue(value string) *SecureValueセキュア値ラッパーを作成します。
パラメータ:
value- 保護する文字列値
戻り値:
*SecureValue- セキュア値オブジェクト
動作:
- オブジェクトプールを使用してアロケーションを削減
- GC ファイナライザーを設定して自動ゼロクリア
- メモリロックが有効な場合、メモリのロックを試行(失敗時はサイレントに無視)
secret := env.NewSecureValue("my-secret-password")
defer secret.Release() // または Close()NewSecureValueStrict
func NewSecureValueStrict(value string) (*SecureValue, error)セキュア値を作成し、メモリロック失敗時にエラーを返します。
パラメータ:
value- 保護する文字列値
戻り値:
*SecureValue- セキュア値オブジェクトerror- メモリロックエラー(厳格モードのみ)
env.SetMemoryLockEnabled(true)
env.SetMemoryLockStrict(true)
secret, err := env.NewSecureValueStrict("my-secret")
if err != nil {
// メモリロック失敗
log.Printf("Warning: %v", err)
}
if secret != nil {
defer secret.Release()
}GetSecure (Loader メソッド)
func (l *Loader) GetSecure(key string) *SecureValueローダーからセキュア値を取得します。
パラメータ:
key- キー名
戻り値:
*SecureValue- セキュア値の防御的コピー、呼び出し側が解放を担当;キーが存在しないまたはローダーがクローズされた場合は nil
secret := loader.GetSecure("API_KEY")
if secret != nil {
defer secret.Release()
// secret を使用
}防御的コピー
GetSecure は元の値のコピーを返し、親 Loader から独立しています。呼び出し側が Release() または Close() の呼び出しを担当します。
メソッド
String
func (sv *SecureValue) String() stringマスク表現を返します。ログやフォーマットに安全に使用できます。fmt.Stringer インターフェースを実装し、fmt.Printf、log.Println、エラーラッピング経由でのキーの意図しない漏洩を防止します。
戻り値:
string- マスク表現(例:[SECURE:32 bytes])、nil の場合は[NIL]
secret := env.GetSecure("PASSWORD")
if secret != nil {
log.Printf("Password: %s", secret) // 安全、マスク表現を出力
// log.Printf("Password: %s", secret.Masked()) と同等
}注意
String() が返すのはマスク表現で、平文値ではありません。平文値を取得するには Reveal() を使用してください。
Reveal
func (sv *SecureValue) Reveal() string平文値を返します。呼び出し側は返された文字列の安全な取り扱いに責任を持ちます —— ログ記録、シリアライズ、永続化位置への保存を避けてください。暗号化操作、API 呼び出しなどの実際の値を必要とする安全な処理にのみ使用してください。
戻り値:
string- 平文値、クローズ済みまたは nil の場合は空文字列
secret := env.GetSecure("API_KEY")
if secret != nil {
defer secret.Release()
plaintext := secret.Reveal() // 平文値を取得
// plaintext を API 呼び出しなどの安全な操作に使用
_ = plaintext
}危険
Reveal() は平文字列を返します。Go の文字列は不変で、手動ゼロクリアができません。必要な時のみ使用し、戻り値をログに記録したり保存したりしないでください。
Bytes
func (sv *SecureValue) Bytes() []byte値のバイトスライスコピーを返します。呼び出し側は ClearBytes でゼロクリアする責任を持ちます。
戻り値:
[]byte- 値のバイトコピー、クローズ済みの場合は nil
secret := env.GetSecure("API_KEY")
if secret != nil {
data := secret.Bytes()
defer env.ClearBytes(data) // 使用後にゼロクリア
// data を使用
}Length
func (sv *SecureValue) Length() int値の長さを返します。内容を露出しません。
戻り値:
int- 値の長さ、クローズ済みの場合は 0
secret := env.GetSecure("API_KEY")
if secret != nil {
fmt.Printf("API Key length: %d\n", secret.Length())
}Masked
func (sv *SecureValue) Masked() stringマスクされた値を返します。ログ出力に使用します。
戻り値:
string- マスク表現
出力フォーマット:
- クローズ済み:
[CLOSED] - 空値:
[SECURE:0 bytes] - 正常:
[SECURE:N bytes]または[SECURE:N bytes locked]または[SECURE:N bytes lock-failed]または[SECURE:N bytes unlocked]
secret := env.GetSecure("API_KEY")
if secret != nil {
log.Printf("API Key: %s", secret.Masked())
// 出力:API Key: [SECURE:32 bytes]
// 注:メモリロックが有効(SetMemoryLockEnabled(true))でロック成功時のみ、
// マスクに " locked" サフィックスが追加されます(他に " lock-failed" / " unlocked")
}MarshalJSON
func (sv *SecureValue) MarshalJSON() ([]byte, error)json.Marshaler インターフェースを実装します。マスク表現を返し、json.Marshal などのリフレクションベースのシリアライザー経由でのキーの意図しない漏洩を防止します。平文は JSON 出力に決して現れません。
戻り値:
[]byte- JSON セーフなマスク文字列(例:"[SECURE:32 bytes]")、nil の場合は"null"error- 常に nil を返す
type Response struct {
APIKey *env.SecureValue `json:"api_key"`
}
resp := Response{APIKey: env.NewSecureValue("sk-1234567890")}
data, _ := json.Marshal(resp)
// {"api_key":"[SECURE:16 bytes]"}
// 平文は出力に現れないセキュリティ設計
MarshalJSON は SecureValue が構造体に埋め込まれ JSON シリアライズされても平文を漏洩しないことを保証します。出力は String() / Masked() と一致します。
MarshalText
func (sv *SecureValue) MarshalText() ([]byte, error)encoding.TextMarshaler インターフェースを実装します。String() と一致するマスク表現を返し、encoding/xml、text/template、構造化ログなどのテキストベースのエンコーダー経由でのキーの意図しない漏洩を防止します。
戻り値:
[]byte- マスク文字列(例:"[SECURE:32 bytes]")、nil の場合は"[NIL]"error- 常に nil を返す
type Config struct {
Token *env.SecureValue `xml:"token"`
}
cfg := Config{Token: env.NewSecureValue("Bearer xyz")}
data, _ := xml.Marshal(cfg)
// <Config><token>[SECURE:10 bytes]</token></Config>Close
func (sv *SecureValue) Close() errorメモリを安全にゼロクリアしオブジェクトをクローズします。
戻り値:
error- 常に nil を返す
動作:
- 内部データを安全にゼロクリア
- クローズ済みとマーク
- オブジェクトプールに返却しない
secret := env.GetSecure("TOKEN")
if secret != nil {
defer secret.Close()
// Close 後メモリはゼロクリアされる
}Release
func (sv *SecureValue) Release()メモリをゼロクリアしオブジェクトプールに返却します。
動作:
- 内部データを安全にゼロクリア
- GC ファイナライザーをクリア
- オブジェクトプールに返却し再利用可能に
secret := env.GetSecure("KEY")
if secret != nil {
defer secret.Release()
// Release 後メモリはゼロクリアされ、オブジェクトはプールに返却
}Close vs Release
Close()- ゼロクリアのみ、プールに返却しないRelease()- ゼロクリア + プール返却(高頻度シーンに推奨)
IsClosed
func (sv *SecureValue) IsClosed() boolオブジェクトがクローズ済みかチェックします。
戻り値:
bool- クローズ済みか
if secret.IsClosed() {
// オブジェクトはクローズ済み、使用不可
}IsMemoryLocked
func (sv *SecureValue) IsMemoryLocked() boolメモリがロックされているか(ディスクへのスワップを防止)チェックします。
戻り値:
bool- ロック済みか
if secret.IsMemoryLocked() {
fmt.Println("Memory is locked, protected from swapping")
}MemoryLockError
func (sv *SecureValue) MemoryLockError() errorメモリロック試行のエラー(ある場合)を返します。
戻り値:
error- ロックエラー、成功または未試行の場合は nil
if err := secret.MemoryLockError(); err != nil {
log.Printf("Memory lock failed: %v", err)
}メモリロック設定
SetMemoryLockEnabled
func SetMemoryLockEnabled(enabled bool)メモリロックをグローバルに有効化/無効化します。新しく作成されるすべての SecureValue に影響します。
パラメータ:
enabled- 有効化するか
package main
import "github.com/cybergodev/env"
func main() {
// アプリ起動時に有効化
env.SetMemoryLockEnabled(true)
// 以降のすべての SecureValue がロックを試行
}IsMemoryLockEnabled
func IsMemoryLockEnabled() boolメモリロックが有効かチェックします。
戻り値:
bool- 有効か
if env.IsMemoryLockEnabled() {
// メモリロックが有効
}SetMemoryLockStrict
func SetMemoryLockStrict(strict bool)厳格モードを設定します。有効化すると、NewSecureValueStrict はロック失敗時にエラーを返します。
パラメータ:
strict- 厳格モードを有効化するか
env.SetMemoryLockEnabled(true)
env.SetMemoryLockStrict(true)
secret, err := env.NewSecureValueStrict("sensitive-data")
if err != nil {
// ロック失敗
}IsMemoryLockStrict
func IsMemoryLockStrict() bool厳格モードかチェックします。
戻り値:
bool- 有効か
strict := env.IsMemoryLockStrict()IsMemoryLockSupported
func IsMemoryLockSupported() bool現在のプラットフォームがメモリロックをサポートするかチェックします。
戻り値:
bool- サポートするか
| プラットフォーム | サポート |
|---|---|
| Linux | ✅ |
| macOS | ✅ |
| Windows | ✅ |
| FreeBSD | ✅ |
| wasm | ❌ |
注意
true はプラットフォームがサポートすることを示すだけで、プロセスに十分な権限があることを示すわけではありません。Linux は CAP_IPC_LOCK または root 権限が必要です。
if env.IsMemoryLockSupported() {
env.SetMemoryLockEnabled(true)
}セキュリティユーティリティ関数
ClearBytes
func ClearBytes(b []byte)バイトスライスを安全にゼロクリアします。使用後ただちに機密データをゼロクリアします。
パラメータ:
b- ゼロクリアするバイトスライス
sensitive := []byte("secret-data")
// 使用...
env.ClearBytes(sensitive)
// sensitive はすべて 0 になるIsSensitiveKey
func IsSensitiveKey(key string) boolキー名が機密パターンにマッチするかチェックします。
パラメータ:
key- キー名
戻り値:
bool- 機密か
if env.IsSensitiveKey("DB_PASSWORD") {
// 機密キー、安全な方法で処理
secret := env.GetSecure("DB_PASSWORD")
if secret != nil {
defer secret.Release()
}
}機密パターン: password, secret, token, key, api_key, credential など
MaskValue
func MaskValue(key, value string) stringキーの機密性に基づいてマスク値を返します。
パラメータ:
key- キー名value- 元の値
戻り値:
string- マスク後の値
// 機密キー - [MASKED:N chars] 形式を返す
masked := env.MaskValue("API_KEY", "secret123")
// 戻り値:[MASKED:9 chars]
// 非機密キー - 元の値を返す(20 文字超の場合は切り詰め)
masked := env.MaskValue("APP_NAME", "myapp")
// 戻り値:myappMaskKey
func MaskKey(key string) stringログ用にキー名をマスクします。
パラメータ:
key- キー名
戻り値:
string- マスク後のキー名
masked := env.MaskKey("DB_PASSWORD")
// 戻り値:DB***SanitizeForLog
func SanitizeForLog(s string) string文字列内の機密キーと値のペア情報をクリーンアップします。key=value 形式の機密値を自動検出してマスクします。
パラメータ:
s- 元の文字列
戻り値:
string- クリーンアップ後の文字列
// 機密キーと値のペアを自動マスク
msg := "Connected with password=secret123 api_key=abc123"
clean := env.SanitizeForLog(msg)
// 戻り値:"Connected with password=[MASKED] api_key=[MASKED]"MaskSensitiveInString
func MaskSensitiveInString(s string) string文字列内の潜在的に機密な内容をマスクします。50 文字を超える文字列を切り詰めます。
パラメータ:
s- 元の文字列
戻り値:
string- マスク後の文字列
// 長い文字列は切り詰められる(最初の 47 文字を保持し "..." を追加)
long := "This is a very long string that exceeds 50 characters"
clean := env.MaskSensitiveInString(long)
// 戻り値:"This is a very long string that exceeds 50 char..."使用シーン
機密データを含む可能性のある長い文字列の切り詰めに使用します。機密キーと値のペアを自動マスクするには SanitizeForLog を使用してください。
完全な例
package main
import (
"fmt"
"log"
"github.com/cybergodev/env"
)
func main() {
// メモリロックをチェックして有効化
if env.IsMemoryLockSupported() {
env.SetMemoryLockEnabled(true)
fmt.Println("Memory locking enabled")
}
// 環境変数を読み込み
if err := env.Load(".env"); err != nil {
log.Printf("Warning: %v", err)
}
// 機密値を安全に取得
apiKey := env.GetSecure("API_KEY")
if apiKey == nil {
log.Fatal("API_KEY not found")
}
defer apiKey.Release()
// 安全に使用
fmt.Printf("API Key length: %d\n", apiKey.Length())
fmt.Printf("API Key (masked): %s\n", apiKey.Masked())
// メモリロック状態をチェック
if apiKey.IsMemoryLocked() {
fmt.Println("Memory is locked")
}
// ロックエラーをチェック
if err := apiKey.MemoryLockError(); err != nil {
fmt.Printf("Memory lock warning: %v\n", err)
}
// 他の関数に渡す
connectAPI(apiKey.Reveal())
// セキュリティユーティリティ関数を使用
logMessage := "Processing with API_KEY=secret"
safeMessage := env.SanitizeForLog(logMessage)
fmt.Println(safeMessage) // Processing with API_KEY=[MASKED]
}
func connectAPI(key string) {
// キーを使用して接続...
fmt.Printf("Connecting with key of length %d\n", len(key))
}内部実装
オブジェクトプール
SecureValue は sync.Pool を使用してメモリアロケーションを削減します:
var secureValuePool = sync.Pool{
New: func() interface{} {
return &SecureValue{}
},
}GC ファイナライザー
作成時に GC ファイナライザーを設定し、ガベージコレクション時に自動ゼロクリアを保証:
runtime.SetFinalizer(sv, (*SecureValue).finalize)安全なゼロクリア
unsafe.Pointer を使用してコンパイラ最適化を防止(sv.mu ロック保持中に呼び出す必要あり):
func (sv *SecureValue) clearDataLocked() {
if len(sv.data) == 0 {
return
}
// メモリのアンロック(ロック済みの場合)
if sv.locked {
internal.UnlockMemory(sv.data)
sv.locked = false
}
dataPtr := unsafe.Pointer(&sv.data[0])
for i := range sv.data {
*(*byte)(unsafe.Pointer(uintptr(dataPtr) + uintptr(i))) = 0
}
runtime.KeepAlive(sv.data)
sv.data = nil
sv.lockErr = nil
}関連ドキュメント
- 定数とエラー - 禁止キー、機密キーパターン、エラー型
- セキュリティ概要 - セキュリティアーキテクチャとコア機能
- 本番チェックリスト - 本番稼働前のセキュリティチェック
- Loader API - GetSecure メソッド