加密密钥的安全持久性和进程间通信(IPC)

huangapple go评论83阅读模式
英文:

Secure persistence and IPC of encryption keys

问题

我正在开发一个简单的命令行实用程序,根据用户提供的口令对小文件进行加密/解密。为了防止用户在短时间内反复输入口令,我需要想出一个临时缓存口令(或派生的对称密钥)的解决方案。这类似于sudossh-agent等程序提供的功能。

到目前为止,我想出的解决方案是一个独立的类似守护进程的缓存程序,用于跟踪加密文件(通常只有一个文件)和相应的密钥。文件通过校验和(SHA-256)进行标识,并且与加密客户端的通信是通过Unix域套接字进行的。

以下是我使用Go语言中的net/rpc包创建的RPC服务的简化摘录:

type Checksum [ChecksumSize]byte

type SumKeyPair struct {
    Sum Checksum
    Key []byte
}

type KeyReply struct {
    Key       []byte
    Available bool
}

type Cache map[Checksum][]byte

// 获取与文件校验和对应的密钥
func (c Cache) RequestKey(sum Checksum, reply *KeyReply) error {
    reply.Key, reply.Available = c[sum]
    return nil
}

// 添加或更新与文件校验和对应的密钥
func (c Cache) SetKey(pair SumKeyPair, reply *bool) error {
    _, *reply = c[pair.Sum]
    c[pair.Sum] = pair.Key
    return nil
}

每当用户想要解密文件时,将调用RequestKey函数。每当提供正确的口令(带有更新的校验和)时,将调用SetKey函数。

像这样在进程之间传输敏感数据是否安全?在Unix域套接字的情况下,保护是否类似于套接字文件的权限?如果有更好的方法,请告诉我。

两个相关的附加问题:

  • 有什么好的跨平台IPC/缓存机制吗?我主要关注Linux,但支持Windows会更好。
  • 我知道将密码/密钥保存在内存中并不是100%安全的,而且有点鸡生蛋的问题。但是,加密缓存的密钥是否会增加安全性?
英文:

I'm working on a simple command-line utility that encrypt/decrypts a small file based on a user-provided passphrase. In order to prevent the user from having to retype his/her passphrase over and over again in short periods of time, I need to come up with a solution that temporarily caches this passphrase (or derived symmetric key). This is similar to functionality provided by programs like sudo and ssh-agent.

The solution I've come up with so far, is a separate daemon-like cache program that keeps track of the encrypted files (though usually there will only be one file) and corresponding keys. The files are identified by a checksum (SHA-256), and the communication with the encryption client is done with Unix domain sockets.

Here's a simplified excerpt of the RPC service that I've created with the help of the net/rpc package in Go:

type Checksum [ChecksumSize]byte

type SumKeyPair struct {
	Sum Checksum
	Key []byte
}

type KeyReply struct {
	Key       []byte
	Available bool
}

type Cache map[Checksum][]byte

// Fetches key that corresponds with file checksum
func (c Cache) RequestKey(sum Checksum, reply *KeyReply) error {
	reply.Key, reply.Available = c[sum]
	return nil
}

// Adds or updates key that corresponds with file checksum
func (c Cache) SetKey(pair SumKeyPair, reply *bool) error {
	_, *reply = c[pair.Sum]
	c[pair.Sum] = pair.Key
	return nil
}

RequestKey will be called every time the user wants to decrypt the file. SetKey will be called every time a correct passphrase is provided (with the updated checksum).

Is it safe to transfer sensitive data between processes like this? In the case of Unix domain sockets, is the protection similar to the permissions on the socket file? If there are better ways, please let me know.

Two quasi-related side questions:

  • What is a good cross-platform IPC/cache mechanism? I'm focusing on Linux, but Windows support would be a plus.
  • I know keeping passwords/keys in memory isn't 100% secure and is kind of a chicken-egg problem. But would encrypting the cached keys increase security?

答案1

得分: 1

如果你的目标是创建一个像ssh-agentgpg-agent一样的守护进程,那么你可能不需要一个RequestKey操作。

这些其他代理程序的设计思路是私钥永远不会被发送到客户端进程中。因此,ssh不是从代理程序中检索私钥以执行基于挑战的身份验证,而是将挑战发送到代理程序,然后代理程序返回已签名的挑战版本,准备发送到服务器。

简单来说,如果代理程序从不通过IPC机制发送私钥,那么IPC机制就无法用于窃听私钥。

如果你想进一步提高代理程序的安全性,并且在Linux上使用UNIX域套接字,你可以利用SO_PEERCRED套接字选项来查询你正在通信的对方的身份:

func getPeerCred(conn net.Conn) (*syscall.Ucred, error) {
    file, err := conn.(*net.UnixConn).File()
    if err != nil {
        return nil, err
    }
    defer file.Close()
    return syscall.GetsockoptUcred(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED)
}

返回的Ucred结构告诉你套接字另一端的进程、用户和组ID。你可以利用这些信息来决定是否要与对方通信。这些信息来自内核,因此客户端无法伪造。

英文:

If you are aiming for a daemon that acts like ssh-agent or gpg-agent, then you probably don't want a RequestKey operation.

Those other agents are designed such that the private key is never sent to the client process. So rather than ssh retrieving the private key from the agent so it can perform challenge based authentication, it sends the challenge to the agent which then returns the signed version of the challenge ready to be sent to the server.

To put it simply, if the agent never sends the private key over the IPC mechanism, then the IPC mechanism can't be used to snoop on the private key.

If you want to further improve the security of your agent, if you are using UNIX domain sockets on Linux you can make use of the SO_PEERCRED socket option to query the identity of who you are talking to:

func getPeerCred(conn net.Conn) (*syscall.Ucred, error) {
	file, err := conn.(*net.UnixConn).File()
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return syscall.GetsockoptUcred(int(file.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED)
}

The returned Ucred structure tells you the process, user, and group IDs of the party at the other end of the socket. You can use this information to decide whether or not you want to communicate with them. The information comes from the kernel, so can not be forged by the client.

huangapple
  • 本文由 发表于 2014年1月3日 08:10:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/20894291.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定