web-dev-qa-db-ja.com

コマンドライン引数として渡されたパスワードを非表示にする方法は?

私は、たとえば次のようないくつかの機能のロックを解除するために、パスフレーズを入力する特定のアクションを必要とするソフトウェアデーモンを実行しています。

$ darkcoind masternode start <mypassphrase>

これで、ヘッドレスのdebianサーバーにセキュリティ上の懸念が生じました。

たとえばCtrl+Rを使用してbash履歴を検索すると、この強力なパスワードが表示されます。今、私は自分のサーバーが危険にさらされており、侵入者がシェルにアクセスでき、単純にCtrl+Rを使用して履歴からパスフレーズを見つけることができると想像します。

パスフレーズを入力せずに、bashの履歴、ps/procなどに表示する方法はありますか?


Update 1:デーモンにパスワードを渡さないと、エラーがスローされます。これはオプションではありません。


アップデート2:ソフトウェアや、開発者をぶら下げるなどの役立つヒントを削除するように言わないでください。私はこれがベストプラクティスの例ではないことを知っていますが、このソフトウェアは bitcoin に基づいており、すべてのビットコインベースのクライアントは、これらのコマンドをリッスンするjson rpcサーバーの一種であり、既知のセキュリティ問題はまだ議論されています( abc )。


Update 3:デーモンはすでに起動しており、コマンドで実行されています

$ darkcoind -daemon

psを実行すると、起動コマンドのみが表示されます。

$ ps aux | grep darkcoin
user     12337  0.0  0.0  10916  1084 pts/4    S+   09:19   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:48 darkcoind -daemon

したがって、パスフレーズを使用してコマンドを渡しても、psまたは/procにはまったく表示されません。

$ darkcoind masternode start <mypassphrase>
$ ps aux | grep darkcoin
user     12929  0.0  0.0  10916  1088 pts/4    S+   09:23   0:00 grep darkcoin
user     21626  0.6  0.3 1849716 130292 ?      SLl  May02   6:49 darkcoind -daemon

これは、歴史がどこに現れるのかという疑問を残しますか? .bash_historyのみ

43
Afr

本当に、これはアプリケーション自体で修正する必要があります。そして、そのようなアプリケーションはオープンソースである必要がありますので、アプリ自体で問題を修正することはオプションです。この種の間違いを犯すセキュリティ関連のアプリケーションは他の間違いもするかもしれないので、私はそれを信用しません。

シンプルなインターポーザー

しかし、別の方法を求めていたので、ここに1つあります。

#define _GNU_SOURCE
#include <dlfcn.h>

int __libc_start_main(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  )
{
  int (*next)(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  ) = dlsym(RTLD_NEXT, "__libc_start_main");
  ubp_av[argc - 1] = "secret password";
  return next(main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}

これをコンパイルする

gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl

次に、プロセスを実行します

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase

インターポーザライブラリは、アプリケーションのmain関数が実行される前にこのコードを実行します。 mainの呼び出しで、最後のコマンドライン引数を実際のパスワードに置き換えます。ただし、/proc/*/cmdlineに出力される(したがって、psなどのツールで表示される)コマンドラインには、偽の引数が含まれます。明らかに、ソースコードとそれからコンパイルしたライブラリを自分だけが読めるようにする必要があるので、chmod 0700ディレクトリで操作するのが最善です。また、パスワードはコマンド呼び出しの一部ではないため、bashの履歴も安全です。

より高度なインターポーザー

より複雑なことを行う場合は、ランタイムライブラリが適切に初期化される前に__libc_start_mainが実行されることに注意してください。ですから、絶対に必要でない限り、関数呼び出しを避けることをお勧めします。心ゆくまで関数を呼び出すことができるようにしたい場合は、main自体が呼び出される直前に、すべての初期化が完了した後で呼び出してください。次の例では、指摘したGrubermenschに感謝する必要があります コマンドライン引数として渡されたパスワードを非表示にする方法 は、getpassに注目しました。

#define _GNU_SOURCE
#include <dlfcn.h>
#include <unistd.h>

static int (*real_main) (int, char * *, char * *);

static int my_main(int argc, char * * argv, char * * env) {
  char *pass = getpass(argv[argc - 1]);
  if (pass == NULL) return 1;
  argv[argc - 1] = pass;
  return real_main(argc, argv, env);
}

int __libc_start_main(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  )
{
  int (*next)(
    int (*main) (int, char * *, char * *),
    int argc, char * * ubp_av,
    void (*init) (void),
    void (*fini) (void),
    void (*rtld_fini) (void),
    void (* stack_end)
  ) = dlsym(RTLD_NEXT, "__libc_start_main");
  real_main = main;
  return next(my_main, argc, ubp_av, init, fini, rtld_fini, stack_end);
}

これによりパスワードの入力が求められるため、インターポーザーライブラリを秘密にする必要はありません。プレースホルダー引数はパスワードプロンプトとして再利用されるため、次のように呼び出します。

LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "

別の代替手段は、ファイル記述子(たとえばgpg --passphrase-fdが行うような)、またはx11-ssh-askpassなどからパスワードを読み取ります。

70
MvG

歴史だけではありません。 psの出力にも表示されます。

そのソフトウェアを書いた人はだれでも掛けられ、引き分けられ、四分の一にされるべきです。ソフトウェアに関係なく、コマンドラインでパスワードを入力する必要はありません。
デーモンプロセスの場合、それはさらに許されません...

ソフトウェア自体のrm -fの他に、これに対する解決策はわかりません。正直なところ、仕事を成し遂げるために他のソフトウェアを見つけてください。そのようなジャンクを使用しないでください。

28
Tonny

これにより、ps出力がクリアされます。

非常に注意してください:これはアプリケーションを破壊する可能性があります。あなたはここにドラゴンがいると警告されています。

  • 外部プロセスは、プロセスメモリをいじる必要はありません。
  • プロセスがこの領域のパスワードに依存している場合、アプリケーションが壊れる可能性があります。
  • これを行うと、そのプロセスにある作業データが破損する可能性があります。
  • これは非常識なハックです。

今、あなたはこれらの悲惨な警告について正式に通知されています。これにより、psに表示される出力がクリアされます。履歴は消去されず、bashジョブの履歴も消去されません(myprocess myargs &などのプロセスの実行など)。しかし、psは引数を表示しなくなります。

#!/usr/bin/python
import os, sys
import re

PAGESIZE=4096

if __name__ == "__main__":
  if len(sys.argv) < 2:
    sys.stderr.write("Must provide a pid\n")
    sys.exit(1)

  pid = sys.argv[1]

  try:
    cmdline = open("/proc/{0}/cmdline".format(pid)).read(8192)

    ## On linux, at least, argv is located in the stack. This is likely o/s
    ## independent.
    ## Open the maps file and obtain the stack address.
    maps = open("/proc/{0}/maps".format(pid)).read(65536)
    m = re.search('([0-9a-f]+)-([0-9a-f]+)\s+rw.+\[stack\]\n', maps)
    if not m:
      sys.stderr.write("Could not find stack in process\n");
      sys.exit(1)

    start = int("0x"+m.group(1), 0)
    end = int("0x"+m.group(2), 0)

    ## Open the mem file
    mem = open('/proc/{0}/mem'.format(pid), 'r+')
    ## As the stack grows downwards, start at the end. It is expected
    ## that the value we are looking for will be at the top of the stack
    ## somewhere
    ## Seek to the end of the stack minus a couple of pages.
    mem.seek(end-(2*PAGESIZE))

    ## Read this buffer to the end of the stack
    stackportion = mem.read(8192)
    ## look for a string matching cmdline. This is pretty dangerous.
    ## HERE BE DRAGONS
    m = re.search(cmdline, stackportion)
    if not m:
      ## cause this is an example dont try to search exhaustively, just give up
      sys.stderr.write("Could not find command line in the stack. Giving up.")
      sys.exit(1)

    ## Else, we got a hit. Rewind our file descriptor, plus where we found the first argument.
    mem.seek(end-(2*PAGESIZE)+m.start())
    ## Additionally, we'll keep arg0, as thats the program name.
    arg0len = len(cmdline.split("\x00")[0]) + 1
    mem.seek(arg0len, 1)

    ## lastly overwrite the remaining region with nulls.
    writeover = "\x00" * (len(cmdline)-arg0len)
    mem.write(writeover)

    ## cleanup
    mem.close()

  except OSError, IOError:
    sys.stderr.write("Cannot find pid\n")
    sys.exit(1)

chmod +xを保存して、プログラムを起動します。次に、./whatever <pidoftarget>を実行します。これが機能する場合、出力は生成されません。失敗すると、何かについて文句を言い、終了します。

20
Matthew Ife

Rootまたは必要なユーザーのみがアクセスできるファイルから引数を渡すことができますか?

コンソールにパスワードを入力するのは大変ですが、最後の手段として、行をスペースで始めて履歴に表示されないようにしてください。

11
vn.

たぶんこれは動作しますか?

darkcoind masternode start `cat password.txt`
7
Daniele Testa

残念ながら、darkcoindコマンドがパスワードをコマンドライン引数として期待している場合、psなどのユーティリティを介して公開されます。唯一の実際の解決策は 開発者を教育する です。

psの公開は避けられないかもしれませんが、少なくともパスワードがシェルの履歴ファイルに書き出されないようにすることができます。

$ xargs darkcoind masternode start

password

CtrlD

履歴ファイルはxargs darkcoind masternode start、パスワードではありません。

4
200_success

ビットコインの場合、公式の開発者の回答は、提供されているpython contrib/bitrpc/bitrpc.pyのラッパーを使用することです( github ):

たとえば、コマンドwalletpassphraseを使用すると、安全な方法でパスワードを要求します。 bitcoin-cliにインタラクティブ機能を追加する予定はありません。

そして:

bitcoin-cliは現状のままであり、インタラクティブな機能はありません。

出典: #2318

ウォレットのロックを解除します。

$ python bitrpc.py walletpassphrase

パスフレーズを変更:

$ python bitrpc.py walletpassphrasechange

https://github.com/bitcoin/bitcoin/tree/master/contrib/bitrpc

Darkcoinの場合、それはアナログで動作します:

https://github.com/darkcoin/darkcoin/tree/master/contrib/bitrpc

3
Afr

他の人が述べたように、履歴から情報を隠すためにシェルの履歴コントロールを調べてください。

しかし、まだ誰も提案していないように見えることの1つは、hidepidパラメータを使用して/procをマウントすることです。次のように、/proc/etc/fstab行を変更して、hidepidを含めるようにしてください。

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults,hidepid=2        0       0
3
ptman

新しいシェルプロセスからコマンドを実行することにより、シェルの履歴にパスワードが含まれないようにすることができ、その後すぐに終了します。例えば:

bash$ sh
sh$ darkcoind masternode start 'correct horse battery staple'
sh$ exit
bash$

shが設定されていることを確認してくださいnotその履歴をファイルに保存します。

もちろん、これはpsにパスワードが表示されるなど、他の問題には対処しません。 darkcoindプログラム自体がpsから情報を隠す方法はあると思いますが、それは脆弱性の時間枠を短くするだけです。

2
Keith Thompson