使用带有用户名和密码的cURL?


465

我想访问一个需要用户名/密码的URL。我想尝试使用curl访问它。现在我正在做类似的事情:

curl http://api.somesite.com/test/blah?something=123

我得到一个错误。我想我需要指定用户名和密码以及上述命令。

我怎样才能做到这一点?

Answers:


692

使用该-u标志包括用户名,然后curl将提示输入密码:

curl -u username http://example.com

您也可以在命令中包含密码,但是您的密码将在bash历史记录中显示:

curl -u username:password http://example.com

109
请注意,如果您从控制台执行此操作,密码将保留在历史记录中,这是错误的。您应该只指定-u用户,并且CURL将在无回显模式下询问您密码。
Cristian Vrabie

25
@CristianVrabie从技术上讲是正确的,但如果从不允许提示的自动化脚本中运行它,则不正确。对解决该问题会感到好奇。
Ligemer 2014年

26
@OmarOthman,如果您正在通过脚本运行curl,则凭据(显然)不会出现在您的历史记录中,但它们会在ps(1)中可见。解决:print -- '-u username:password' > somewhere && curl -K somewhere http://...
某人2014年

7
@Jay环境变量将在命令执行之前求值。密码在ps输出中仍然可见。
罗伯特·瓦赞(RobertVažan)

8
不要太在意这一点,但我相信我的答案(stackoverflow.com/a/27894407/758174即使用--netrc-file)更加安全。它将密码保留在历史记录,ps,您的脚本等之外。这是我在所有脚本中以及对的所有经过身份验证的用法中使用的唯一形式curl
皮埃尔·D

251

这样做更安全:

curl --netrc-file my-password-file http://example.com

...因为在命令行上传递普通用户名/密码字符串,是个坏主意。

密码文件的格式为(按照man curl):

machine <example.com> login <username> password <password>

注意:

  1. 机器名称不得包含https://或类似名称!只是主机名。
  2. 单词“ machine”,“ login”和“ password”只是关键字;实际信息是这些关键字之后的内容。

10
是的,这样可以将密码保留在进程列表和命令历史记录之外。更可取的方法是这样做,而且只需要多一点工作即可:)
AC Capehart

8
这绝对应该是公认的答案;命令行上的密码是一种可怕的做法。(这是众所周知的事实。)
ELLIOTTCABLE

6
您还可以使用标志-K <file>--config <file>通过文件或标准输入接收卷曲标志。(警告:请勿与-k或混淆--insecure!)
Rufflewind '16

2
这种curl方法使凭据不受历史记录和进程状态的影响,但将用户名和密码保留在my-password文件中的明文中,从而创建了另一个攻击媒介-比在历史记录文件中包含信息更糟:例如,bash自动限制权限历史记录文件。如果将环境变量与脚本一起使用来设置用户名/密码,则会出现类似的问题。如果脚本不安全,则凭据也不安全。
轻松逗乐的史蒂文(Steven the Emusly Amused)

5
@SteventheEasilyAmused我不同意,使用.netrc具有适当严格权限的明文文件要好得多,因此只有您的用户才能阅读它,而不是其他机制(例如,命令行args)让其他用户阅读信息。
肯·威廉姆斯

76

还是相同但语法不同

curl http://username:password@api.somesite.com/test/blah?something=123

1
我使用该语法,因为可以在更多情况下使用。就像从没有cURL和wGet的Windows cmd一样,使用start "" "http://username:password@api.somesite.com/test/blah?something=123"。它可以从任何地方启动。这也适用于ftp登录; D
m3nda 2014年

8
您需要对用户名和密码进行URL编码才能使用有趣的字符
diachedelic

2
我知道大多数人都知道像这样的示例在URL中不发送密码(甚至用户名),因为它很容易嗅到。照这样说; 我不建议您使用它,仅当您知道自己在做什么时才使用它。
LosManos

1
这是suuuuuper过时,应该不会被使用。这是从FTP时代开始的:o
Qix-MONICA被盗

2
不幸的是,这使密码在进程列表中可见。
Mark Ribau

63

您也可以通过以下方式发送用户名:

curl -u USERNAME http://server.example

然后,Curl将询问您密码,并且密码在屏幕上不可见(或者是否需要复制/粘贴命令)。


27

为了安全地在脚本中传递密码(例如,防止它与ps auxf或日志一起显示),您可以使用-K-标志(从stdin读取配置)和heredoc来实现:

curl --url url -K- <<< "--user user:password"

2
感谢您对--config选项(-K)的引用...可能更好的解决方案是将“ --user user:password”放入文件中,-K the file这样您就只拥有一个密码副本,而每个脚本中只有一个副本。保护单个文件容易得多。
克里斯·科格登

1
类似的选项,其中只有密码在文件中:cat "${password_filepath}" | sed -e "s/^/-u ${username}:/" | curl --url "${url}" -K-。我必须将-K-旧的macOS bash YMMV的网址放在前面。
Mark Ribau

12

通常CURL命令称为

curl https://example.com\?param\=ParamValue -u USERNAME:PASSWORD

如果您没有任何密码,或者想跳过命令提示符以要求输入简单密码,请将密码部分留空。

curl https://example.com\?param\=ParamValue -u USERNAME:


1
注意:密码将在外壳历史记录和进程列表中可见。
Mark Ribau

10
curl -X GET -u username:password  {{ http://www.example.com/filename.txt }} -O

1
注意:密码将在外壳历史记录和进程列表中可见。
Mark Ribau

8

要使密码至少不会在您的帐户中弹出.bash_history

curl -u user:$(cat .password-file) http://example-domain.tld

6
在这种情况下,密码仍将保留在进程列表中,例如ps auxw |grep curl,在正确的时间对某人可见。同样,如果通过sudo
Adam Katz

1
然后,使用此方法,密码将出现在文件(.password-file)中,该文件可能比.bash历史记录更不安全。这样做的好处是,它只是密码-URL和用户名不会在.password文件中泄漏。
轻松逗乐的史蒂文(Steven the Emusly Amused)


6

其他答案也建议netrc根据我阅读的内容指定用户名和密码。以下是一些语法详细信息:

https://ec.haxx.se/zh-CN/curl-netrc.html

像其他答案一样,我想强调必须注意此问题的安全性。

尽管我不是专家,但我发现这些链接很有见地:

https://ec.haxx.se/cmdline-passwords.html

总结一下:

使用协议加密版本(HTTPS与HTTP)(FTPS与FTP)可以帮助避免网络泄漏。

使用netrc可以帮助避免命令行泄漏。

更进一步,看来您也可以使用gpg加密netrc文件

https://brandur.org/fragments/gpg-curl

这样,您的凭据就不会作为纯文本“处于静止状态”(存储)。


5

简而言之,最安全的方法是使用环境变量来存储/检索凭据。因此,curl命令如下:

curl -Lk -XGET -u "${API_USER}:${API_HASH}" -b cookies.txt -c cookies.txt -- "http://api.somesite.com/test/blah?something=123"

然后就打电话给你宁静的API,并通过HTTP WWW_Authentication标头中的Base64编码的值API_USERAPI_HASH。在-Lk刚刚告诉curl遵循HTTP重定向30倍和使用不安全的TLS处理(即忽略SSL错误)。虽然double --只是bash语法糖,但可以停止处理命令行标志。此外,-b cookies.txt-c cookies.txt标志会通过-b发送Cookie和-c在本地存储Cookie来处理Cookie 。

该手册包含更多身份验证方法示例


1
请记住,使用“ -Lk”可以使您容易受到中间人(MITM)攻击,因此请谨慎使用该选项。
GuyPaddock

4
这不起作用...因为bash为您扩展了这些变量,所以扩展将显示在进程列表中。
克里斯·科格登


4

提示将凭证传递给curl的最安全方法是提示插入凭证。如先前建议(-u USERNAME)传递用户名时,会发生这种情况。

但是,如果您不能以这种方式传递用户名怎么办? 例如,用户名可能需要是url的一部分,而只有密码是json负载的一部分。

tl; dr: 在这种情况下,这是安全使用curl的方法:

read -p "Username: " U; read -sp "Password: " P; curl --request POST -d "{\"password\":\"${P}\"}" https://example.com/login/${U}; unset P U

read 将从命令行提示输入用户名和密码,并将提交的值存储在两个变量中,这些变量可以在后续命令中引用,最后未设置。

我将详细说明为什么其他解决方案不理想。

为什么环境变量不安全

  1. 由于该环境对于进程是隐式可用的,因此无法跟踪环境变量内容的访问和公开模式(ps -eww)
  2. 应用程序通常会捕获整个环境并记录下来以进行调试或监视(有时在磁盘上以纯文本格式记录日志文件,尤其是在应用程序崩溃后)
  3. 环境变量被传递给子进程(因此违反了最小特权原则)
  4. 维护它们是一个问题:新工程师不知道他们在那里,也不了解周围的需求-例如,不要将其传递给子流程-因为它们没有得到执行或记录。

为什么直接在命令行上将其键入命令中是不安全的, 因为您的机密最终会被任何其他正在运行的用户看到,ps -aux因为它列出了为每个当前正在运行的进程提交的命令。同样是因为您的密钥随后出现在bash历史记录中(一旦shell终止)。

为什么将其包含在本地文件中是不安全的,对此文件进行 严格的POSIX访问限制可以减轻这种情况下的风险。但是,它仍然是文件系统上的文件,静态时未加密。


2
看来此方法仍会在进程列表中显示密码?
Mark Ribau

4

我对bash(Ubuntu 16.04 LTS)有相同的需求,答案中提供的命令在我的情况下无法正常工作。我不得不使用:

curl -X POST -F 'username="$USER"' -F 'password="$PASS"' "http://api.somesite.com/test/blah?something=123"

-F仅当您使用变量时,才需要在参数中使用双引号,因此从命令行即可... -F 'username=myuser' ...

相关安全声明:正如Mark Ribau先生在评论中指出的那样,此命令在进程列表中显示密码($ PASS变量,已展开)!


1
看起来这仍然在进程列表中显示$ PASS的值吗?
Mark Ribau

是的,不幸的是。
马可(Marco)

1

如果您使用的是具有Gnome密钥环应用程序的系统,那么避免直接暴露密码的解决方案是使用gkeyring.py从密钥中提取密码:

server=server.example.com
file=path/to/my/file
user=my_user_name
pass=$(gkeyring.py -k login -tnetwork -p user=$user,server=$server -1)

curl -u $user:$pass ftps://$server/$file -O

1
注意:密码将在进程列表中可见。(还有历史记录,如果这不是脚本的一部分。)
Mark Ribau

1

这比OP要求的要多得多,但是由于这是安全地将密码传递给的最佳结果curl,因此我在这里添加了这些解决方案,供其他到达此处进行搜索的人使用。


注意:-s用于read命令的arg 不是POSIX,因此并非在所有地方都可用,因此下面将不再使用。我们将使用stty -echostty echo代替。

注意:如果在函数中,则下面的所有bash变量都可以声明为locals,而不是取消设置。

注:perl很一般可在我试过,因为它有很多事情依赖的所有系统,而rubypython都没有,所以用perl在这里。如果可以保证ruby/ 可以python在其中执行此操作,则可以将perl命令替换为其等效命令。

注意:bash在macOS 10.14.4上的3.2.57中测试。其他外壳程序/安装可能需要一些小的翻译。


安全地提示用户输入(可重复使用的)密码以进行卷曲。如果需要多次调用curl,则特别有用。

对于现代的shell,echo内置的是哪里(通过来检查which echo):

url='https://example.com'
printf "Username: "
read username
printf "Password: "
stty -echo  # disables echoing user input, POSIX equivalent for 'read -s'
read pass
printf "\n" # we need to move the line ahead
stty echo   # re-enable echoing user input
echo ${pass} | sed -e "s/^/-u ${username}:/" | curl --url "${url}" -K-
unset username
unset pass

对于较旧的Shell,echo类似于/bin/echo(在进程列表中可以看到其回声的地方)的地方:
此版本无法重用该密码,请改低调。

url='https://example.com'
printf "Username: "
read username
printf "Password: "
stty -echo  # disables echoing user input, POSIX equivalent for 'read -s'
perl -e '
    my $val=<STDIN>;
    chomp $val;
    print STDERR "\n";  # we need to move the line ahead, but not send a newline down the pipe
    print $val;
' | sed -e "s/^/-u ${username}:/" | curl --url "${url}" -K-
stty echo   # re-enable echoing user input
unset username



如果您碰巧需要将密码临时存储到文件中,请在清除密码之前将其重新用于多个命令(例如,因为您正在使用函数来重复使用代码,并且不想重复代码并且不能通过echo传递值)。(是的,在这种形式下它们不是在不同的库中具有功能,因此有些许作弊;我试图将它们减少到显示它所需的最少代码。)

当echo是内置的时(这是特别设计的,因为echo是内置的,但出于完整性考虑而提供):

url='https://example.com'
filepath="$(mktemp)"  # random path, only readable by current user
printf "Username: "
read username
printf "Password: "
stty -echo  # disables echoing user input, POSIX equivalent for 'read -s'
read pass
echo "${pass}" > "${filepath}"
unset pass
printf "\n" # we need to move the line ahead
stty echo   # re-enable echoing user input

cat "${filepath}" | sed -e "s/^/-u ${username}:/" | curl --url "${url}" -K-

rm "${filepath}"  # don't forget to delete the file when done!!
unset username

当回声是这样的/bin/echo

url='https://example.com'
filepath="$(mktemp)"  # random path, only readable by current user
printf "Username: "
read username
printf "Password: "
stty -echo  # disables echoing user input, POSIX equivalent for 'read -s'
$(perl -e '
    my $val=<STDIN>;
    chomp $val;
    open(my $fh, ">", $ARGV[0]) or die "Could not open file \"$ARGV[0]\" $\!";
    print $fh $val;
    close $fh;
' "$filepath")
printf "\n" # we need to move the line ahead
stty echo   # re-enable echoing user input

cat "${filepath}" | sed -e "s/^/-u ${username}:/" | curl --url "${url}" -K-

rm "${filepath}"  # don't forget to delete the file when done!!
unset username
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.