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" あり)
}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 現在はすべて 0IsSensitiveKey
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 を使用してコンパイラ最適化を防止:
func (sv *SecureValue) clearData() {
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
}関連ドキュメント
- 定数とエラー - 禁止キー、機密キーパターン、エラー型
- セキュリティ概要 - セキュリティアーキテクチャとコア機能
- 本番チェックリスト - リリース前セキュリティチェック
- Loader API - GetSecure 方法