强制 cURL 从环境中获取密码

Forcing cURL to get a password from the environment

这个 question about using cURL with a username and password 对我来说不是最佳答案:

  1. curl -u "user:pw" https://example.com 将 pw 放入进程列表
  2. curl "https://user:pw@example.com" 将 pw 放入进程列表
  3. curl -u "user:$(cat ~/.passwd)" https://example.com 将 pw 放入进程列表
  4. curl -u user https://example.com 提示输入密码
  5. curl --netrc-file ~/.netrc https://example.com 需要一个文件

#4 是安全的,但我可能 运行 这个命令一天数百次,所以很乏味。 #5 接近安全,但具有根访问权限的人可以读取该文件。

cURL man page 说(注意粗体文本):

-u/--user <user:password>

Specify the user name and password to use for server authentication. Overrides -n/--netrc and --netrc-optional.

If you just give the user name (without entering a colon) curl will prompt for a password.

If you use an SSPI-enabled curl binary and do NTLM authentication, you can force curl to pick up the user name and password from your environment by simply specifying a single colon with this option: -u :.

我已经尝试在环境中设置 $USER$PASSWORD(以及 $CURLOPT_PASSWORD 和其他),但是当调用 curl -u : https://example.com(没有 -u : 也不起作用)。

我不是在做 NTLM,所以这不起作用。除非我遗漏了什么。

有没有办法仅通过环境将凭据传递给 curl

(解决方法移至答案)

Is there a way to pass credentials to curl solely through the environment?

不,我认为没有。

我认为 CURLOPT_USERPWD 文档描述了您的需要,但这是一个可以使用其他语言的 curl 库的选项。 PHP、Perl、C 等

你 运行 来自 shell 的 curl 二进制文件只是该库的另一个前端,但是像 CURLOPT_USERPWD 这样的东西通过 curl 二进制文件传递到库的方式是通过在二进制文件上使用命令行选项。

理论上您可以编写自己的二进制文件作为 curl 库的前端,并支持环境变量。

您可以交替破解环境支持,因为您希望将它看到现有的 curl 二进制文件,并使用本地函数编译您自己的文件。

注意,不过,即使是环境变量也可能被您的 shell 泄露到进程 table 中。 (当你运行ps ewwp $$时你看到了什么?)

也许使用受限权限的 .netrc 文件是最安全的方式。也许您需要生成一个临时的 .netrc 文件以供 --netrc-file curl 选项使用。

我认为你要么必须为你的环境选择风险最小的解决方案,要么用真正的语言编写一些能够正确保证安全性的东西。

这个 bash 解决方案似乎最符合我的需要。它非常安全、便携且速度快。

#!/bin/bash
SRV="example.com"
URL="https://$SRV/path"
curl --netrc-file <(cat <<<"machine $SRV login $USER password $PASSWORD") "$URL"

这使用 process substitution (<( command ) runs command in a sub-shell to populate a file descriptor to be handed as a "file" to the parent command, which in this case is curl). The process substitution contains a here-string (cat <<< text, a variant of echo text that won't put anything into your process list), creating a file descriptor for the netrc file 将凭据传递到远程 Web 服务器。

进程替换提供的安全性实际上非常可靠:它的文件描述符 不是 临时文件,甚至在同一 shell 实例中的其他调用也不可用, 所以这个 appears secure in this context; an adversary would have to dig through memory or launch a complicated attack to find its contents. Since the $PASSWORD environment variable is also in memory, this should not increase the attack surface.

只要您没有使用过 export PASSWORD,像 ps ewwp $$ 这样的技巧就不会泄露密码(如 中所述)。使用一些不太明显的变量名也是明智的。

下面是上述代码的一个简化的不安全版本,可能有助于解释其工作原理:

#!/bin/sh
# INSECURE VERSION, DO NOT USE
SRV=example.com
URL="https://$SRV/path"
TMP=$(mktemp)
printf "machine %s login %s password %s\n" "$SRV" "$USER" "$PASSWORD" > "$TMP"
curl --netrc-file "$TMP" "$URL"
rm -f "$TMP"

这个不安全的版本有很多缺陷,所有这些都在以前的版本中解决了:

  • 它将密码存储在一个文件中(尽管该文件只有您可读)
  • 它非常简单地在命令行中包含密码
  • 临时文件一直保留到 curl 退出
  • 之后
  • Ctrl+c 将退出而不删除临时文件

其中一些可以通过以下方式解决:

#!/bin/sh
SRV=example.com
URL="https://$SRV/path"
TMP=$(mktemp /dev/shm/.XXXXX)  # assumes /dev/shm is a ramdisk
trap "rm -f $TMP" 0 18
cat << EOF > "$TMP"
machine $SRV login $USER password $PASSWORD
EOF
(sleep 0.1; rm -f "$TMP") &  # queue removing temp file in 0.1 seconds
curl --netrc-file "$TMP" "$URL"

我认为这个版本是混乱的、次优的,而且可能不太安全(尽管它更便携)。它还需要理解小数的 sleep 版本(如果系统负载很重,0.1 秒可能太快了)。


我最初发布了一个解决方法,在我的问题中包含一个 perl 一行,然后(使用 )我研究了一些更好的方法,然后才选择了这个字符串方法,它既轻便(更快)又更便携。

在这一点上,它足够优雅,我认为它是 "answer" 而不是 "ugly workaround,",所以我将其迁移为这个官方答案。我给出了 @ghoti a +1 for ,它正确地说明了 cURL 的命令行程序无法自行执行我想要的操作,但我不是 "accepting" 那个答案,因为它无助于解决问题.

用户 "Tom, Bom" 在这里提供了一个不错的解决方案:https://coderwall.com/p/dsfmwa/securely-use-basic-auth-with-curl

curl --config - https://example.com <<< 'user = "username:password"'

这可以防止密码出现在进程列表中,尽管这并没有具体解决 OP 的原始问题:

Is there a way to pass credentials to curl solely through the environment?

我仍然给@ghoti 打分,因为他给出了更全面和翔实的答案。

以前的答案是正确的,最好的选择是使用 -n 进行卷曲(假设你在 linux):

  1. 创建一个文件(使用您自己喜欢的编辑器)

vi ~YOUR_USER_NAME/.netrc

  1. 添加以下内容

machine example.com login YOUR_USER_NAME password THE_ACTUAL_PASSWORD

  1. 运行

curl -n https://example.com/some_end_point

Curl 在解析 netrc 文件中的标记时使用 SPACE 和 TAB 作为分隔符:

https://github.com/curl/curl/blob/bc5a0b3e9f16a431523ae54822adc38c3a396a26/lib/netrc.c#L122

因此 --netrc-file 方法无法处理密码中的 SPACE 或 TAB。

密码SPACE测试

SRV="httpbin.org"
URL="https://$SRV/basic-auth/username/pass%20word"
USERNAME=username
PASSWORD='pass word'
curl -v --netrc-file <(echo "machine $SRV login $USERNAME password $PASSWORD") "$URL"

结果:❌失败

WARNING: If your shell's echo command is not a builtin, the above curl invocation will leak $PASSWORD into the process table momentarily. In bash, whether echo is a builtin or not can be tested with type -t echo. WORKAROUND: Use cat and a here-string: Replace <(echo "string") with <(cat <<< "string"). This warning applies to all the examples in this answer.

密码中的TAB测试

SRV="httpbin.org"
URL="https://$SRV/basic-auth/username/pass%09word"
USERNAME=username
PASSWORD=$'pass\tword'
curl -v --netrc-file <(echo "machine $SRV login $USERNAME password $PASSWORD") "$URL"

结果:❌失败

密码中双引号"的测试

SRV="httpbin.org"
URL="https://$SRV/basic-auth/username/pass%22word"
USERNAME=username
PASSWORD='pass\"word'
curl -v --netrc-file <(echo "machine $SRV login $USERNAME password $PASSWORD") "$URL"

结果:❌失败

更稳健的方式

@sfgeorge 正确地指出 -K,  --config <file> 选项(将 <file> 设置为 -)可用于在 STDIN 上提供密码。但是,将 STDIN 用于此目的将排除将 STDIN 用于其他目的,例如使用 --data @-.

POST 数据

幸运的是,我们可以使用 process substitution 而不是 STDIN。进程替换扩展为文件名,可用于需要文件名的地方。

密码SPACE测试

USERNAME=username
PASSWORD='pass word'
curl -v \
  -K <(echo "user: \"$USERNAME:$PASSWORD\"") \
  "https://httpbin.org/basic-auth/username/pass%20word"

结果:✅ 成功

密码中的TAB测试

USERNAME=username
PASSWORD=$'pass\tword'
curl -v \
  -K <(echo "user: \"$USERNAME:$PASSWORD\"") \
  "https://httpbin.org/basic-auth/username/pass%09word"

结果:✅ 成功

而且,为了更加稳健,我们让它也处理密码中的双引号。

密码中双引号"的测试

USERNAME=username
PASSWORD=$'pa s\ts"wo$rd'

# Build 'user' option
USER_OPT="$USERNAME:$PASSWORD"
USER_OPT=${USER_OPT//\/\\} # Escape `\`
USER_OPT=${USER_OPT//\"/\\"} # Escape `"`
USER_OPT="user: \"${USER_OPT}\""

curl -v \
  -K <(echo "$USER_OPT") \
  "https://httpbin.org/basic-auth/username/pa%20s%09s%22wo%24rd"

结果:✅ 成功

我添加了一个表情符号以备不时之需。

我想指出的是,即使是 bash here-string 语法 (<<strace bash -c '/bin/cat <<<hello' 揭示了它是如何工作的:

19376 open("/tmp/sh-thd-1651575757", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600) = 3
19376 write(3, "hello", 5)              = 5
19376 write(3, "\n", 1)                 = 1
19376 open("/tmp/sh-thd-1651575757", O_RDONLY) = 4
19376 close(3)                          = 0
19376 unlink("/tmp/sh-thd-1651575757")  = 0
19376 dup2(4, 0)                        = 0
19376 close(4)                          = 0