bash 在 Unix 上隐藏命令行参数的秘密

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3830823/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 19:42:40  来源:igfitidea点击:

Hiding secret from command line parameter on Unix

bashunix

提问by Kerby82

I've a script that launches inside of itself a command with a parameter that is a secret. For example:

我有一个脚本,它在自身内部启动一个带有秘密参数的命令。例如:

#!/bin/bash
command-name secret

While running the command I can read through ps -ef | grep command-namewhich is the secret.

在运行命令时,我可以通读ps -ef | grep command-name其中的秘密。

Is there any way of hiding the secret in a way that through ps -ef, the command line parameter is obfuscated?

有没有办法通过ps -ef混淆命令行参数来隐藏秘密?

回答by VasiliNovikov

  1. First, you can NOT hide command line arguments. They will still be visible to other users via ps auxand cat /proc/$YOUR_PROCESS_PID/cmdlineat the time of launching the program (before the program has a chance to do run-time changes to arguments). Good news is that you can still have a secret by using alternatives:

  2. Use environment variables. If your program can read them, do this:

    mySecret='hello-neo' myCommand
    
  3. Use standard input:

    mySecret='hello-neo' printenv mySecret | myCommand
    
  4. Use temporary file descriptor:

    myCommand <( mySecret='hello-neo' printenv mySecret )
    
  1. 首先,您不能隐藏命令行参数。他们仍然会通过对其他用户不可见ps aux,并cat /proc/$YOUR_PROCESS_PID/cmdline在启动程序的时间(之前的程序有机会做运行时更改参数)。好消息是,您仍然可以通过使用替代方案来获得秘密:

  2. 使用环境变量。如果您的程序可以读取它们,请执行以下操作:

    mySecret='hello-neo' myCommand
    
  3. 使用标准输入:

    mySecret='hello-neo' printenv mySecret | myCommand
    
  4. 使用临时文件描述符:

    myCommand <( mySecret='hello-neo' printenv mySecret )
    

In the last case your program will be launched like myCommand /dev/fd/67, where the contents of /dev/fd/67is your secret (hello-neoin this example).

在最后一种情况下,您的程序将像 一样启动myCommand /dev/fd/67,其中的内容/dev/fd/67是您的秘密(hello-neo在本例中)。



In all of the above approaches, be wary of leaving the command in bash command history (~/.bash_history). You can avoid this by either running the command from a script (file), or by interactively prompting yourself for password each time:

在上述所有方法中,请注意不要将命令留在 bash 命令历史记录 ( ~/.bash_history) 中。您可以通过从脚本(文件)运行命令或每次以交互方式提示自己输入密码来避免这种情况:

    read -s mySecret && export mySecret
    myCommand  # approach 2
    printenv mySecret | myCommand  # approach 3
    myCommand <( printenv mySecret )  # approach 4

回答by Kelly S. French

If the secret doesn't change between executions, use a special configuration file, ".appsecrets". Set the permissions of the file to be read-only by owner. Inside the file set an environment variable to the secret. The file needs to be in the home directory of the user running the command.

如果秘密在执行之间没有改变,请使用特殊的配置文件".appsecrets". 将文件的权限设置为所有者只读。在文件内部,为秘密设置一个环境变量。该文件需要位于运行命令的用户的主目录中。

#!/bin/bash  
#filename: .appsecrets
set SECRET=polkalover  

Load the config file so the environment variable gets set.

加载配置文件,以便设置环境变量。

. ~/.appsecrets

What I've seen done:

我所看到的:

1)
echo $SECRET | command

1)
echo $SECRET | command

works if the command prompts for the password from stdin AND if 'echo' is a builtin of your shell. We were using Korn.

如果命令提示输入来自 stdin 的密码并且“echo”是您的 shell 的内置命令,则该命令有效。我们使用的是 Korn。

2)
password=$ENV{"SECRET"};

2)
password=$ENV{"SECRET"};

works if you have control of the code (e.g. in perl or C++)

如果您可以控制代码(例如在 perl 或 C++ 中)

3)
. ./.app.config #sets the environment variables
isql -host [host] -user [user] -password <<SECRET
${SQLPASSWORD}
SECRET

3)
. ./.app.config #sets the environment variables
isql -host [host] -user [user] -password <<SECRET
${SQLPASSWORD}
SECRET

works if the command can accept the secret from std-in. One limitation is that the <<string has to be the last argument given to the command. This might be troublesome if there is a non-optional arg that has to appear after -password

如果命令可以接受来自 std-in 的秘密,则有效。一个限制是<<字符串必须是给命令的最后一个参数。如果在 -password 之后必须出现非可选参数,这可能会很麻烦

The benefit of this approach is you can arrange it so the secret can be hidden in production. Use the same filename in production but it will be in the home directory of the account that runs the command in production. You can then lock down access to the secret like you would access to the root account. Only certain people can 'su' to the prod account to view or maintain the secret while developers can still run the program because they use their own '.appsecret' file in their home directory.

这种方法的好处是您可以安排它,以便在生产中隐藏秘密。在生产中使用相同的文件名,但它将位于在生产中运行命令的帐户的主目录中。然后,您可以像访问 root 帐户一样锁定对机密的访问。只有某些人可以“su”到 prod 帐户以查看或维护机密,而开发人员仍然可以运行该程序,因为他们在其主目录中使用自己的“.appsecret”文件。

You can use this approach to store secured information for any number of applications, as long as they use different environment variable names for their secrets.

您可以使用此方法为任意数量的应用程序存储安全信息,只要它们使用不同的环境变量名称作为其机密即可。

(WRONG WAY)
One old method I saw the DBAs use was to set SYBASE to "/opt/././././././././././././././././././././././././././././././././././sybase/bin". So their commandlines were so long the ps truncated it. But in linux I think you might be able to sniff out the full commandline from /proc.

(错误的方式)
我看到 DBA 使用的一种旧方法是将 SYBASE 设置为"/opt/././././././././././././././././././././././././././././././././././sybase/bin". 所以他们的命令行很长,ps 截断了它。但是在 linux 中,我认为您可以从 /proc 中嗅出完整的命令行。

回答by Jonathan Leffler

The only way to conceal your secret argument from psis not to provide the secret as an argument. One way of doing that is to place the secret in a file, and to redirect file descriptor 3 to read the file, and then remove the file:

隐藏你的秘密论点的唯一方法ps是不要提供秘密作为论点。一种方法是将秘密放在一个文件中,并重定向文件描述符 3 以读取该文件,然后删除该文件:

echo secret > x.$$
command 3<x.$$
rm -f x.$$

It isn't entirely clear that this is a safe way to save the secret; the echo command is a shell built-in, so it shouldn't appear in the 'ps' output (and any appearance would be fleeting). Once upon a very long time ago, echowas not a built-in - indeed, on MacOS X, there is still a /bin/echoeven though it is a built-in to all shells.

尚不完全清楚这是保存秘密的安全方式。echo 命令是一个内置的 shell,所以它不应该出现在 'ps' 输出中(并且任何出现都会转瞬即逝)。很久很久以前,echo不是内置的——事实上,在 MacOS X 上,仍然有一个/bin/echo即使它是所有 shell 的内置。

Of course, this assumes you have the source to commandand can modify it to read the secret from a pre-opened file descriptor instead of from the command line argument. If you can't modify the command, you are completely stuck - the 'ps' listing will show the information.

当然,这假设您拥有源代码command并且可以修改它以从预先打开的文件描述符而不是从命令行参数中读取机密。如果你不能修改命令,你就完全卡住了——'ps' 列表会显示信息。

Another trick you could pull if you're the command owner: you could capture the argument (secret), write it to a pipe or file (which is immediately unlinked) for yourself, and then re-exec the command without the secret argument; the second invocation knows that since the secret is absent, it should look wherever the first invocation hid the secret. The second invocation (minus secret) is what appears in the 'ps' output after the minuscule interval it takes to deal with hiding the secret. Not as good as having the secret channel set up from the beginning. But these are indicative of the lengths to which you have to go.

如果您是命令所有者,您可以使用的另一个技巧:您可以捕获参数(秘密),将其写入管道或文件(立即取消链接),然后在没有秘密参数的情况下重新执行命令;第二次调用知道由于秘密不存在,它应该查看第一次调用隐藏秘密的地方。第二次调用(减去秘密)是在处理隐藏秘密所需的微小间隔之后出现在“ps”输出中的内容。还不如从一开始就设置秘密通道。但这些都表明你必须走多远。

Zapping an argument from inside the program - overwriting with zeroes, for example - does not hide the argument from 'ps'.

从程序内部切换参数 - 例如用零覆盖 - 不会隐藏 'ps' 中的参数。

回答by Jorge Fuentes González

I saw it on another post. This is the easiest way under Linux.

我在另一个帖子上看到的。这是Linux下最简单的方法。

This modifies the memory part of command line that all other programs see.

这会修改所有其他程序看到的命令行的内存部分。

strncpy(argv[1], "randomtrash", strlen(argv[1]));

You can also change the name of the process, but only when read from the command line. Programs like topwill show the real process name:

您还可以更改进程的名称,但仅限于从命令行读取时。像这样的程序top将显示真实的进程名称:

strncpy(argv[0], "New process name", strlen(argv[0]));

Don't forget to copy maximum strlen(argv[0])bytes because probably there's no more space allocated.

不要忘记复制最大strlen(argv[0])字节数,因为可能没有分配更多空间。

I think that arguments can only be found in the portion of the memory that we modify so I think that this works like a charm. If someone knows something accurate about this, please comment.

我认为参数只能在我们修改的内存部分中找到,所以我认为这很有魅力。如果有人对此有所了解,请发表评论。

VasyaNovikovnote:The password can still be intercepted after the program has invoked but before it started doing the changes you described.

VasyaNovikov注意:在程序调用之后但在开始执行您描述的更改之前,仍然可以拦截密码。

回答by Wrikken

The expectlibrary was created partially for these kind of things, so you can still provide a password / other sensitive information to a process without having to pass it as an argument. Assuming that when 'secret' isn't given the program asks for it of course.

expect库部分是为这些事情创建的,因此您仍然可以向进程提供密码/其他敏感信息,而无需将其作为参数传递。假设当没有给出“秘密”时,程序当然会要求它。

回答by Daenyth

If the script is intended to run manually, the best way is to read it in from STDIN

如果脚本打算手动运行,最好的方法是从 STDIN 读入

#!/bin/bash
read -s -p "Enter your secret: " secret

command "$secret"

回答by dogbane

There's no easy way. Take a look at this question I asked a while ago:

没有简单的方法。看看我前段时间问的这个问题:

Hide arguments from ps

隐藏来自 ps 的参数

Is commandyour own program? You could try encrypting the secret and have the commanddecrypt it before use.

command你自己的程序吗?您可以尝试加密秘密并command在使用前对其进行解密。

回答by MvG

You can use LD_PRELOADto have a library manipulate the command line arguments of some binary within the process of that binary itself, where psdoes not pick it up. See this answer of mineon Server Fault for details.

您可以使用LD_PRELOAD库在该二进制文件本身的进程中操作某些二进制文件的命令行参数,而ps不是在那里选择它。有关详细信息,请参阅在服务器故障上的这个答案

回答by rouble

Here is one way to hide a secret in an environment variable from ps:

这是在 ps 中隐藏环境变量中的秘密的一种方法:

#!/bin/bash
read -s -p "Enter your secret: " secret

umask 077 # nobody but the user can read the file x.$$ 
echo "export ES_PASSWORD=$secret" > x.$$
. x.$$ && your_awesome_command
rm -f x.$$ # Use shred, wipe or srm to securely delete the file


In the ps output you will see something like this:


在 ps 输出中,您将看到如下内容:

$ps -ef | grep your_awesome_command
root     23134     1  0 20:55 pts/1    00:00:00  . x.$$ && your_awesome_command

Elastalert and Logstash are examples of services that can access passwords via environment variables.

Elastalert 和 Logstash 是可以通过环境变量访问密码的服务示例。

回答by user2068018

may be you can do like this:

也许你可以这样做:

#include <boost/algorithm/string/predicate.hpp>
void hide(int argc, char** argv, std::string const & arg){
    for(char** current = argv; current != argv+ argc ;++current){
        if(boost::algorithm::starts_with(*current, "--"+arg)){
            bzero(*current, strlen(*current));
        }
    }
}
int main(int argc, char** argv){
   hide(argc,  argv, "password");
}