我有一个客户安全策略合规要求加密磁盘上的某些文件. 最明显的方法是设备映射器和 AES 加密模块然而,当前已经存在的系统被设置为生成加密的单个文件。
基本上,他们有一个使用 SSL 和基本/摘要式身份验证的 apache 服务器,并且他们需要在磁盘上解密来自 AES 的加密文件,然后再使用 SSL 重新加密它们。(显然我可以使用 mod_php、mod_perl,但是我们的想法是只在这个盒子上保存静态文件)
我是否有任何选项可以在 apache 中即时解密文件?
我看到mod_ssl和mod_session_crypto进行加密/解密或类似操作,但不是我所追求的,因为它们是在线加密,而我正在寻找磁盘加密。
我可以想象 PerlSetOutputFilter 可以与配置的合适的 Perl 脚本一起工作,并且我还看到 mod_ext_filter,因此我可以分叉一个 unix 命令并解密文件,但它们都感觉像是黑客攻击。
我有点惊讶没有可用的 mod_crypto......或者我在这里忽略了一些明显的东西?
从资源角度来看,perl 过滤器可能是最佳选择?
答案1
这是一个有趣的问题。我曾经遇到过类似的情况,需要以错误的方式解决问题。话虽如此,我不知道有通用的加密模块可以让你在提供静态文件时对其进行解密。我搜索了几个小时,但没有找到任何东西。这并不奇怪,因为它似乎是一个相当专业的问题。
尽管如此,我看到了几种选择。
- 编写您自己的 C 模块并将其实现为过滤器处理程序。
- 使用mod_ext_filter以及一个外部可执行文件1来解密文件。
- 如果所有文件都有特定的扩展名,添加处理程序调用 CGI 脚本来解密并提供文件。例如,
Action decrypt /cgi-bin/decrypt.pl
和AddHandler decrypt .html
将导致所有以 .html 结尾的文件的请求调用 /cgi-bin/decrypt.pl,而后者需要读取、解密并提供文件。请参阅http://httpd.apache.org/docs/current/handler.html#examples。 - 使用 mod_php 和mod_rewrite从一个或多个特定位置透明地解密并提供任何类型的静态文件。
我冒昧地实现了第四种选择的概念验证,我将在这里分享。我在这个例子中选择 PHP 是因为它无处不在,它通常包含必要的加密函数,而且它的速度很快,因为它是一个用 C 编写的 Apache 模块。
假设所有加密文件都存储在一个名为的目录中data
并使用相同的密钥加密,我在文档根目录中创建了该目录,然后使用 OpenSSL 创建了一个加密文件。
cd /var/www
mkdir data
echo 'This is my plaintext content.' | openssl aes-256-cbc -a -k secret -out data/test.txt
然后我decrypt.php
在我的文档根目录中创建了以下内容:
<?php
# NOTE: This is proof-of-concept code. You should audit it for security before
# using it in a production environment.
# The key to use to decrypt the files
$key = 'secret';
$filename = $_SERVER["DOCUMENT_ROOT"] . htmlspecialchars($_SERVER['REQUEST_URI']);
$data = file_get_contents($filename);
$plaintext = decrypt($key, $data);
# Determine the MIME type of the decrypted content
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$content_type = finfo_buffer($finfo, $plaintext);
finfo_close($finfo);
header("Content-type: $content_type");
print $plaintext;
function decrypt($password, $encrypted_data) {
$encrypted_data = base64_decode($encrypted_data);
$salt = substr($encrypted_data, 8, 8);
$cyphertext = substr($encrypted_data, 16);
$password = $password . $salt;
$md5_hash = array();
$md5_hash[0] = md5($password, true);
$result = $md5_hash[0];
$rounds = 3;
for ($i = 1; $i < $rounds; $i++) {
$md5_hash[$i] = md5($md5_hash[$i - 1] . $password, true);
$result .= $md5_hash[$i];
}
$key = substr($result, 0, 32);
$iv = substr($result, 32,16);
return openssl_decrypt($cyphertext, 'aes-256-cbc', $key, true, $iv);
}
?>
如果您的内容不是 base64 编码的,那么您可以删除解密函数中的第一行$encrypted_data = base64_decode($encrypted_data);
。
最后,在我的 Apache 配置中我添加了以下条目:
<directory /var/www/>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^data/ /decrypt.php [L]
</directory>
现在,任何请求的文件http://www.example.com/data
都将被解密并提供,包括来自子目录的文件。由于重写条件将重写限制为文件,因此目录索引仍然有效。我让 PHP 脚本确定解密内容的 MIME 类型并相应地更新标头,以便我可以提供加密的图像、文档、网页等。
这可能不会像自定义 Apache 模块那样快,但应该很接近。如果您要提供大量加密内容,并且性能是一个问题,您可以通过安装来加快速度替代 PHP 缓存因此 PHP 页面不需要为每个请求都进行编译。
请记住,如果您要解密大文件,则可能需要增加 PHP 内存使用量的设置。
1您说您觉得这个选项有点儿像 hack。为什么?尽管结果可能很慢,但 mod_ext_filter 是一个受支持的核心模块,只要您编写一个过滤程序供它使用,它就会完成工作。
2我借用了 decrypt() 函数https://www.php.net/manual/en/function.openssl-decrypt.php#107210。
答案2
如果您需要保护文件,并提供安全的贸易路线,您可能需要研究使用 mod_ssl 的安全 webdav:
- http://httpd.apache.org/docs/2.0/mod/mod_dav.html
- http://tldp.org/HOWTO/Apache-WebDAV-LDAP-HOWTO/ssl.html
但是,这不会将文件编码为加密档案。它只会传输加密文件,并为文件访问提供一定程度的身份验证和授权。