强制 cURL 从环境中获取密码
Forcing cURL to get a password from the environment
这个 question about using cURL with a username and password 对我来说不是最佳答案:
curl -u "user:pw" https://example.com
将 pw 放入进程列表
curl "https://user:pw@example.com"
将 pw 放入进程列表
curl -u "user:$(cat ~/.passwd)" https://example.com
将 pw 放入进程列表
curl -u user https://example.com
提示输入密码
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):
- 创建一个文件(使用您自己喜欢的编辑器)
vi ~YOUR_USER_NAME/.netrc
- 添加以下内容
machine example.com
login YOUR_USER_NAME
password THE_ACTUAL_PASSWORD
- 运行
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
这个 question about using cURL with a username and password 对我来说不是最佳答案:
curl -u "user:pw" https://example.com
将 pw 放入进程列表curl "https://user:pw@example.com"
将 pw 放入进程列表curl -u "user:$(cat ~/.passwd)" https://example.com
将 pw 放入进程列表curl -u user https://example.com
提示输入密码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
用户 "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):
- 创建一个文件(使用您自己喜欢的编辑器)
vi ~YOUR_USER_NAME/.netrc
- 添加以下内容
machine example.com
login YOUR_USER_NAME
password THE_ACTUAL_PASSWORD
- 运行
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. Inbash
, whetherecho
is a builtin or not can be tested withtype -t echo
. WORKAROUND: Usecat
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 @-
.
幸运的是,我们可以使用 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 语法 (<<
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