libcurl-security(3)

πŸ’… NAME πŸ’…

libcurl-security β€” security considerations when using libcurl. This is the libcurl-security(3) page from the Library Functions Manual, and bestie, it is lowkey essential reading. 🫢

πŸ›‘οΈ Security πŸ›‘οΈ

Okay so first thing: the libcurl project takes security very seriously. The library is written with caution, and precautions are taken to mitigate many kinds of risks you encounter while operating with potentially malicious servers on the Internet. It is a powerful library, though, which lets application writers make trade-offs between ease of writing and exposure to potentially risky operations. If used the right way, you can use libcurl to transfer data pretty safely. ✨

Many applications are used in closed networks where users and servers can (possibly) be trusted, but many others are used on arbitrary servers and are fed input from potentially untrusted users. What follows is a discussion about some risks in the ways applications commonly use libcurl and potential mitigations of those risks. It is not comprehensive, but it shows classes of attacks that robust applications should consider. The Common Weakness Enumeration project at https://cwe.mitre.org/ is a good reference for many of these and similar types of weaknesses that application writers should be aware of. πŸŽ€

πŸ–₯️ Command Lines πŸ–₯️

If you use a command line tool (such as curl) that uses libcurl, and you give options to the tool on the command line, those options can get read by other users of your system when they use ps or other tools to list currently running processes. 😭

To avoid these problems, never feed sensitive things to programs using command line options. Write them to a protected file and use the -K option to avoid this. πŸ’…

πŸͺ .netrc πŸͺ

.netrc is a pretty handy file/feature that allows you to login quickly and automatically to frequently visited sites. The file contains passwords in clear text and is a real security risk. In some cases, your .netrc is also stored in a home directory that is NFS mounted or used on another network based file system, so the clear text password flies through your network every time anyone reads that file. 🫠

For applications that enable .netrc use, a user who manages to set the right URL might then be possible to pass on passwords. πŸ™ˆ

To avoid these problems, do not use .netrc files and never store passwords in plain text anywhere. Periodt. 🚫

πŸ”“ Clear Text Passwords πŸ”“

Many of the protocols libcurl supports send name and password unencrypted as clear text (HTTP Basic authentication, FTP, TELNET etc). It is easy for anyone on your network or a network nearby yours to just fire up a network analyzer tool and eavesdrop on your passwords. Do not let the fact that HTTP Basic uses base64 encoded passwords fool you. They may not look readable at a first glance, but they are easily "deciphered" by anyone within seconds. πŸ’€

To avoid this problem, use an authentication mechanism or other protocol that does not let snoopers see your password: Digest, CRAM-MD5, Kerberos, SPNEGO or NTLM authentication. Or even better: use authenticated protocols that protect the entire connection and everything sent over it. πŸ”₯

πŸ•΅οΈ Unauthenticated Connections πŸ•΅οΈ

Protocols that do not have any form of cryptographic authentication cannot with any certainty know that they communicate with the right remote server. 😬

If your application is using a fixed scheme or fixed hostname, it is not safe as long as the connection is unauthenticated. There can be a man-in-the-middle or in fact the whole server might have been replaced by an evil actor. Unauthenticated protocols are unsafe. The data that comes back to curl may have been injected by an attacker. The data that curl sends might be modified before it reaches the intended server. If it even reaches the intended server at all. 😭

🌸 Remedies 🌸

  • Restrict operations to authenticated transfers β€” use authenticated protocols protected with HTTPS or SSH. πŸ”
  • Make sure the server's certificate etc is verified β€” never ever switch off certificate verification. πŸŽ€

πŸ“œ Certificates and Long-running Connections πŸ“œ

Certificate validation of encrypted connections is performed immediately after a connection is established. That connection could be used for many subsequent transfers, even if the certificate used for validation expires or is revoked, the local certificate bundle is changed in a way that would have caused that certificate to fail validation, the server changes its certificate to one that would have failed validation, or even if a completely different server is brought up under the same hostname. This could continue for many hours (or even years) after such a change occurs, which may not be desired behavior for some applications. πŸ˜΅β€πŸ’«

🌈 Remedies 🌈

  • Use the CURLOPT_MAXLIFETIME_CONN(3) option to limit the amount of time that connections are used after they have been successfully validated. Further transfers require a new connection with validation performed again. πŸ’…

πŸ” Redirects πŸ”

The CURLOPT_FOLLOWLOCATION(3) option automatically follows HTTP redirects sent by a remote server. These redirects can refer to any kind of URL, not just HTTP. libcurl restricts the protocols allowed to be used in redirects for security reasons: only HTTP, HTTPS, FTP and FTPS are enabled by default. Applications may opt to restrict that set further. πŸ‘‘

A redirect to a file: URL would cause libcurl to read (or write) arbitrary files from the local file system. If the application returns the data back to the user (as would happen in some kinds of CGI scripts), an attacker could leverage this to read otherwise forbidden data (e.g. file://localhost/etc/passwd). 😭

If authentication credentials are stored in the ~/.netrc file, or Kerberos is in use, any other URL type (not just file:) that requires authentication is also at risk. A redirect such as ftp://some-internal-server/private-file would then return data even when the server is password protected. 🫠

In the same way, if an unencrypted SSH private key has been configured for the user running the libcurl application, SCP: or SFTP: URLs could access password or private-key protected resources, e.g. sftp://user@some-internal-server/etc/passwd. πŸ’€

The CURLOPT_REDIR_PROTOCOLS_STR(3) and CURLOPT_NETRC(3) options can be used to mitigate against this kind of attack. πŸ›‘οΈ

A redirect can also specify a location available only on the machine running libcurl, including servers hidden behind a firewall from the attacker. E.g. https://127.0.0.1/ or https://intranet.example/delete-stuff.cgi?delete=all or tftp://bootp-server.example/pc-config-data. 😳

Applications can mitigate against this by disabling CURLOPT_FOLLOWLOCATION(3) and handling redirects itself, sanitizing URLs as necessary. Alternately, an app could leave CURLOPT_FOLLOWLOCATION(3) enabled but set CURLOPT_REDIR_PROTOCOLS_STR(3) and install a CURLOPT_OPENSOCKETFUNCTION(3) or CURLOPT_PREREQFUNCTION(3) callback function in which addresses are sanitized before use. πŸ’–

🧨 CRLF in Headers 🧨

For all options in libcurl which specify headers, including but not limited to CURLOPT_HTTPHEADER(3), CURLOPT_PROXYHEADER(3), CURLOPT_COOKIE(3), CURLOPT_USERAGENT(3), CURLOPT_REFERER(3) and CURLOPT_RANGE(3), libcurl sends the headers as-is and does not apply any special sanitation or normalization to them. 🚨

If you allow untrusted user input into these options without sanitizing CRLF sequences in them, someone malicious may be able to modify the request in a way you did not intend, such as injecting new headers. πŸ’€

🏠 Local Resources 🏠

A user who can control the DNS server of a domain being passed in within a URL can change the address of the host to a local, private address which a server-side libcurl-using application could then use. E.g. the innocuous URL https://fuzzybunnies.example.com/ could actually resolve to the IP address of a server behind a firewall, such as 127.0.0.1 or 10.1.2.3. Applications can mitigate against this by setting a CURLOPT_OPENSOCKETFUNCTION(3) or CURLOPT_PREREQFUNCTION(3) and checking the address before a connection. 🫢

All the malicious scenarios regarding redirected URLs apply just as well to non-redirected URLs, if the user is allowed to specify an arbitrary URL that could point to a private resource. For example, a web app providing a translation service might happily translate file://localhost/etc/passwd and display the result. Applications can mitigate against this with the CURLOPT_PROTOCOLS_STR(3) option as well as by similar mitigation techniques for redirections. 😭

A malicious FTP server could in response to the PASV command return an IP address and port number for a server local to the app running libcurl but behind a firewall. Applications can mitigate against this by using the CURLOPT_FTP_SKIP_PASV_IP(3) option or CURLOPT_FTPPORT(3). πŸ”₯

Local servers sometimes assume local access comes from friends and trusted users. An application that expects https://example.com/file_to_read and instead gets http://192.168.0.1/my_router_config might print a file that would otherwise be protected by the firewall. 😳

Allowing your application to connect to local hosts, be it the same machine that runs the application or a machine on the same local network, might be possible to exploit by an attacker who then perhaps can "port-scan" the particular hosts β€” depending on how the application and servers act. πŸ•΅οΈ

πŸ”’ IPv4 Addresses πŸ”’

Some users might be tempted to filter access to local resources or similar based on numerical IPv4 addresses used in URLs. This is a bad and error-prone idea because of the many different ways a numerical IPv4 address can be specified and libcurl accepts: one to four dot-separated fields using one of or a mix of decimal, octal or hexadecimal encoding. 🚩

🌐 IPv6 Addresses 🌐

libcurl handles IPv6 addresses transparently and just as easily as IPv4 addresses. That means that a sanitizing function that filters out addresses like 127.0.0.1 is not sufficient β€” the equivalent IPv6 addresses ::1, ::, 0:00::0:1, ::127.0.0.1 and ::ffff:7f00:1 supplied somehow by an attacker would all bypass a naive filter and could allow access to undesired local resources. IPv6 also has special address blocks like link-local and site-local that generally should not be accessed by a server-side libcurl-using application. A poorly configured firewall installed in a data center, organization or server may also be configured to limit IPv4 connections but leave IPv6 connections wide open. In some cases, setting CURLOPT_IPRESOLVE(3) to CURL_IPRESOLVE_V4 can be used to limit resolved addresses to IPv4 only and bypass these issues. πŸ’…

⬆️ Uploads ⬆️

When uploading, a redirect can cause a local (or remote) file to be overwritten. Applications must not allow any unsanitized URL to be passed in for uploads. Also, CURLOPT_FOLLOWLOCATION(3) should not be used on uploads. Instead, the applications should consider handling redirects itself, sanitizing each URL first. πŸŽ€

πŸ” Authentication

Okay bestie, listen β€” CURLOPT_UNRESTRICTED_AUTH(3) is giving "tell everyone my secrets" energy, because it could literally cause your auth info to get yeeted to some random unknown second server. πŸ’€ Applications can protect themselves by turning off CURLOPT_FOLLOWLOCATION(3) and handling redirects all by themselves, sanitizing stuff wherever necessary. Main character energy = doing your own redirects.

Using the CURLAUTH_ANY option with CURLOPT_HTTPAUTH(3)? That could lowkey send your username AND password in clear text to an HTTP server. Not the vibe. πŸ‘Ž Instead use CURLAUTH_ANYSAFE, which makes sure the password is encrypted over the network β€” or else it just fails the request. Failsafe slay. ✨

And if you're using CURLUSESSL_TRY with CURLOPT_USE_SSL(3), that could also result in username and password being sent in clear text to an FTP server. Nope. 🚫 Use CURLUSESSL_CONTROL instead, which ensures an encrypted connection is used or else fails the request. Protect yourself, queen. πŸ‘‘


πŸͺ Cookies

So here's the tea: if cookies are enabled and cached, a user could craft a URL that performs some malicious action against a site whose authentication is already stored in a cookie. Like, for real:

https://mail.example.com/delete-stuff.cgi?delete=all

Yikes. 😭 Applications can mitigate against this by disabling cookies or clearing them between requests. Clean slate, fresh start, no crumbs. 🧹


πŸ’€ Dangerous SCP URLs

SCP URLs can contain raw commands right inside the scp: URL β€” it's a side effect of how the SCP protocol is designed, bestie. Not our fault, just how it is. E.g.

scp://user:pass@host/a;date >/tmp/test;

Applications must not allow unsanitized scp: URLs to be passed in for downloads. Periodt. πŸ›‘


πŸ“ file://

By default curl and libcurl support file:// URLs. A URL like that is always an access β€” or an attempted access β€” to a local resource. So if your application wants to avoid that whole situation, keep control of what URLs get used and/or prevent curl/libcurl from using the protocol at all. πŸ‘€

By default, libcurl prohibits redirects to file:// URLs. Good. We love a safety rail. πŸ™Œ

⚠️ Warning: file:// on Windows

The Windows operating system tries automatically β€” and there's no way for applications to disable it β€” to establish a connection to another host over the network and access it (over SMB or other protocols), if only the correct file path is accessed. Like, hello?? 😳

When the curl team first realized this, they tried to filter out such attempts to protect applications from inadvertent probes of, for example, internal networks etc. That resulted in CVE-2019-15601 and the associated security fix.

However, we've since been made aware that the previous fix was far from adequate, because there are several other ways to accomplish more or less the same thing: accessing a remote host over the network instead of the local file system. Of course there are. 🫠

The conclusion we've come to is that this is a weakness or feature in the Windows operating system itself, that we as an application cannot safely protect users against. It would just be a whack-a-mole race we don't want to participate in. There are too many ways to do it and there's no knob we can use to turn off the practice. πŸŽͺ

If you use curl or libcurl on Windows (any version), disable the use of the FILE protocol in curl or be prepared that accesses to a range of "magic paths" potentially make your system access other hosts on your network. curl cannot protect you against this. πŸ’…

πŸ™‹ What if the user can set the URL

Applications may find it tempting to let users set the URL that it works on. That's probably fine, but it opens up for mischief and trickery that you, as an application author, may want to address or take precautions against. πŸ‘€

If your curl-using script allows a custom URL β€” do you also, perhaps unintentionally, allow the user to pass other options to the curl command line, if creative use of special characters are applied? Sneaky sneaky. 😈

If the user can set the URL, the user can also specify the scheme part to other protocols that you did not intend for users to use and perhaps did not consider. curl supports over 27 different URL schemes. https:// might be what you expect β€” ftp:// or imap:// might be what the user gives your application. Also, cross-protocol operations might be done by using a particular scheme in the URL but pointing to a server doing a different protocol on a non-standard port. Wild, right? 🀯

Remedies:

  • Use --proto β€” curl command lines can use --proto to limit what URL schemes it accepts. 🎯
  • Use CURLOPT_PROTOCOLS_STR β€” libcurl programs can use CURLOPT_PROTOCOLS_STR(3) to limit what URL schemes it accepts. 🎯
  • Consider not allowing the user to set the full URL β€” maybe just let the user provide data for parts of it? Or maybe filter input to only allow specific choices? Chef's kiss. πŸ‘©β€πŸ³

πŸ“œ RFC 3986 vs WHATWG URL

curl supports URLs mostly according to how they're defined in RFC 3986, and has done so since the beginning. Old school, but in a chic way. ✨

Web browsers mostly adhere to the WHATWG URL Specification.

This deviance makes some URLs copied between browsers (or returned over HTTP for redirection) and curl not work the same way. It can also cause problems if an application parses URLs differently from libcurl and makes different assumptions about a link. This can mislead users into getting the wrong thing, connecting to the wrong host, or otherwise not working identically. 😬

Within an application, this can be mitigated by always using the curl_url(3) API to parse URLs, ensuring they're parsed the same way as within libcurl itself. Consistency is the vibe. πŸŽ€


πŸ” FTP uses two connections

When performing an FTP transfer, two TCP connections are used: one for setting up the transfer and one for the actual data. Two besties, one mission. πŸ‘―

FTP is not only unauthenticated, but the setting up of the second transfer is also a weak spot. The second connection (the data one) is either set up with the PORT/EPRT command, which makes the server connect back to the client on the given IP+PORT β€” or with PASV/EPSV, which makes the server set up a port to listen to and tells the client to connect to a given IP+PORT.

Again: unauthenticated means the connection might be meddled with by a man-in-the-middle, or that there's a malicious server pretending to be the right one. πŸ™ˆ

A malicious FTP server can respond to PASV commands with the IP+PORT of a totally different machine. Perhaps even a third party host β€” and when there are many clients trying to connect to that third party, it could create a Distributed Denial-Of-Service attack out of it. Yikes on bikes. πŸš²πŸ’€ If the client makes an upload operation, it can make the client send the data to another site. And if the attacker can affect what data the client uploads, it can be made to work as an HTTP request β€” and then the client could be made to issue HTTP requests to third party hosts. That's a whole cascade of chaos. πŸ˜΅β€πŸ’«

An attacker that manages to control curl's command line options can tell curl to send an FTP PORT command to ask the server to connect to a third party host instead of back to curl. Rude. 😀

The fact that FTP uses two connections makes it vulnerable in a way that is hard to avoid. It's giving "structural flaw", ngl.

🧭 Active FTP passes on the local IP address

If you use curl/libcurl to do active FTP transfers, curl passes on the address of your local IP to the remote server β€” even when for example using a SOCKS or HTTP proxy in between curl and the target server. Yeah, it just does that. 🫑


πŸ’₯ Denial of Service

A malicious server could cause libcurl to effectively hang by sending data slowly, or even no data at all but just keeping the TCP connection open. This could effectively result in a denial-of-service attack. So rude. 😀 The CURLOPT_TIMEOUT(3) and/or CURLOPT_LOW_SPEED_LIMIT(3) options can be used to mitigate against this. Set those, bestie. ⏰

A malicious server could cause libcurl to download an infinite amount of data, potentially causing system resources to be exhausted, resulting in a system or application crash. πŸ’€ Setting the CURLOPT_MAXFILESIZE_LARGE(3) option is not sufficient to guard against this. Instead, applications should monitor the amount of data received within the write or progress callback and abort once the limit is reached. You gotta watch it yourself, no shortcuts. πŸ‘€

A malicious HTTP server could cause an infinite redirection loop, causing a denial-of-service. This can be mitigated by using the CURLOPT_MAXREDIRS(3) option. Cap it. 🧒


🏷️ Arbitrary Headers

User-supplied data must be sanitized when used in options like CURLOPT_USERAGENT(3), CURLOPT_HTTPHEADER(3), CURLOPT_POSTFIELDS(3) and others that are used to generate structured data. Characters like embedded carriage returns or ampersands could allow the user to create additional headers or fields that could cause malicious transactions. Sanitize your inputs, it's literally the basics. 🧼✨


πŸ“› Server-supplied Names

A server can supply data which the application may, in some cases, use as a filename. The curl command-line tool does this with --remote-header-name, using the Content-disposition: header to generate a filename. An application could also use CURLINFO_EFFECTIVE_URL(3) to generate a filename from a server-supplied redirect URL. Special care must be taken to sanitize such names to avoid the possibility of a malicious server supplying one like "/etc/passwd", "autoexec.bat", "prn:" or even ".bashrc". Absolutely not. 🚫😭


🎫 Server Certificates

A secure application should never use the CURLOPT_SSL_VERIFYPEER(3) option to disable certificate validation. There are numerous attacks that are enabled by applications that fail to properly validate server TLS/SSL certificates, thus enabling a malicious server to spoof a legitimate one. HTTPS without validated certificates is potentially as insecure as a plain HTTP connection. No cap. πŸ”’πŸ’―


πŸ—£οΈ Showing What You Do

Relatedly β€” be aware that in situations when you have problems with libcurl and ask someone for help, everything you reveal in order to get the best possible help might also impose certain security related risks. Hostnames, usernames, paths, operating system specifics, etc. (not to mention passwords of course) may in fact be used by intruders to gain additional information about a potential target. Oops. πŸ™Š

Be sure to limit access to application logs if they could hold private or security-related data. Besides the obvious candidates like usernames and passwords, things like URLs, cookies or even filenames could also hold sensitive data. Logs are little snitches, keep them locked up. πŸ”

To avoid this problem, you must of course use your common sense. Often, you can just edit out the sensitive data or just search/replace your true information with faked data. Fake it till you make it. πŸ’…βœ¨


πŸ‘‘ setuid applications using libcurl

libcurl-using applications that set the 'setuid' bit to run with elevated or modified rights also implicitly give that extra power to libcurl β€” and this should only be done after careful considerations. Like, careful careful. 🧐

Giving setuid powers to the application means that libcurl can save files using those new rights (if for example the SSLKEYLOGFILE environment variable is set). Also: if the application wants these powers to read or manage secrets that the user is otherwise not able to view (like credentials for a login etc), it should be noted that libcurl still might understand proxy environment variables that allow the user to redirect libcurl operations to use a proxy controlled by the user. Yeah. That's a thing. πŸ˜¬πŸ™ƒ

🍴 File descriptors, fork and NTLM

okay so this one's a whole thing. if an app uses libcurl and calls fork(), then literally every file descriptor gets duplicated into the child process β€” including all the ones libcurl made for you. that's just how fork() works, bestie, it does not care about your boundaries πŸ’€

and plot twist: libcurl itself uses fork() and execl() if you tell it to use the CURLAUTH_NTLM_WB authentication method. so it spins up that helper command in a child process, and yeah β€” file descriptors get duplicated along for the ride. so like, make sure the helper program being invoked is actually trusted and reliable, mkay? 🫢

πŸ‘‘ heads up: this feature was removed from curl in 8.8.0. so if you're on a modern curl, this whole saga is ancient history. slay.

🧠 Secrets in memory

when apps hand usernames, passwords, or any other sensitive data to libcurl for upcoming transfers, those secrets are just… kept around as-is in memory πŸ™ˆ no scrambling, no vaulting, just vibes and plaintext.

in a lot of cases they're stored in the heap for as long as the handle itself lives β€” like, the same handle those options were set on. so the secret hangs out for the whole relationship, basically πŸ’œ

so if an attacker can get at the heap β€” say by reading swap space, or through a core dump file β€” that data might be accessible. not great, not terrible, just the truth ✨

and it gets a little worse: when you eventually close a handle and those secrets aren't needed anymore, libcurl does not explicitly clear the memory before freeing it. so credentials can be left behind in freed data. ghosts of passwords past 😭


πŸ’Ύ Saving files

libcurl cannot protect you from attacks where an attacker already has write access to the same directory that libcurl is saving files into. like, if they can write there, they can do stuff there. that's on the filesystem permissions, not on libcurl. periodt.


πŸͺ Cookies

if libcurl is built with PSL (Public Suffix List) support, it detects and discards any cookies that are specified for those suffix domains that shouldn't be allowed to have cookies. good riddance bestie πŸ’…

but if libcurl is not built with PSL support? then it has zero ability to stop super cookies. none. zilch. so maybe check that build flag, iykyk πŸ‘€


🚨 Report Security Problems

if you detect β€” or even just suspect β€” a security problem in libcurl or curl, contact the project curl security team immediately. don't sit on it, don't post it in the group chat, just tell them 🫢

see https://curl.se/dev/secprocess.html for all the details on how it works.


🌈 SEE ALSO


and that's a wrap on the whole page, bestie!! πŸŽ€ this was libcurl, dated 2026-10-12, and the doc you just read is libcurl-security(3). go forth and be secure, no cap πŸ’–βœ¨πŸ‘‘

πŸ“¦ where this page comes from