Back to Blog
medium SEVERITY8 min read

HTTP Basic Auth Over Plain HTTP: How ESP32 Credentials Were Exposed on Your Wi-Fi

A medium-severity vulnerability in the ESP32-audioI2S library allowed audio streaming credentials to be transmitted via HTTP Basic Authentication over unencrypted HTTP connections, making them trivially recoverable by anyone on the same network. The fix gates the Authorization header behind an SSL/TLS check, ensuring credentials are only sent when the connection is encrypted. For embedded IoT devices where credentials are often hardcoded in firmware, this kind of passive interception risk is esp

O
By Orbis AppSec
Published May 28, 2026Reviewed June 3, 2026

Answer Summary

HTTP Basic Auth credential exposure (CWE-319) in ESP32-audioI2S allowed credentials to be transmitted in cleartext over unencrypted HTTP connections. In embedded Arduino/C++ IoT code, this is fixed by gating the Authorization header behind an SSL/TLS check—only sending credentials when `https://` is used—preventing passive Wi-Fi sniffing attacks that could recover hardcoded firmware credentials.

Vulnerability at a Glance

cweCWE-319
fixGate credential transmission behind SSL/TLS connection verification
riskNetwork attackers can passively intercept authentication credentials
languageC++/Arduino (ESP32)
root causeAuthorization header sent regardless of connection encryption status
vulnerabilityCleartext Transmission of Sensitive Information (HTTP Basic Auth over plain HTTP)

HTTP Basic Auth Over Plain HTTP: How ESP32 Credentials Were Exposed on Your Wi-Fi

Summary

A security vulnerability in the popular ESP32-audioI2S library (v3.0.6) allowed audio streaming server credentials to be silently leaked over the air. When connecting to a password-protected stream, the library would attach an Authorization: Basic header to the HTTP request — even when the connection was not encrypted. Any device passively sniffing traffic on the same Wi-Fi network could capture that header and decode the credentials in seconds. A one-line patch now ensures credentials are only sent over SSL/TLS-protected connections.


Introduction

If you've ever built an internet radio, a smart speaker, or a streaming audio project with an ESP32, there's a good chance you've used the ESP32-audioI2S library. It's a well-regarded, widely adopted library that handles the heavy lifting of connecting to audio streams, decoding formats, and pushing audio out over I2S to a DAC or amplifier.

Many audio streams — especially private or subscription-based ones — require authentication. The library supports this via HTTP Basic Authentication, a decades-old scheme where a username and password are Base64-encoded and attached to the HTTP request as a header.

Here's the problem: Base64 is not encryption. It's encoding. Anyone who captures the header can reverse it instantly. And if the HTTP connection itself isn't encrypted, that header travels across the network in plaintext, readable by any device within radio range.

For developers building hobby projects, this might sound academic. But in practice, IoT credentials are frequently:
- Hardcoded in firmware and difficult to rotate
- Reused across services (the same password used for the stream and for the user's email)
- Transmitted repeatedly on every connection attempt

This makes the exposure window wide and the consequences potentially serious.


The Vulnerability Explained

What Is HTTP Basic Authentication?

HTTP Basic Auth is defined in RFC 7617. When a client wants to authenticate, it sends a header like this:

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

That long string is just username:password encoded in Base64. To decode it:

echo "dXNlcm5hbWU6cGFzc3dvcmQ=" | base64 --decode
# Output: username:password

That's it. No hashing. No encryption. No secret key. Anyone who sees the header has the credentials.

RFC 7617 itself explicitly states:

"This scheme is not considered to be a secure method of user authentication unless used in conjunction with some external secure system such as TLS."

What Was the Vulnerable Code Doing?

In Audio.cpp, the connecttohost() function builds an HTTP request and conditionally appends an Authorization header:

// BEFORE (vulnerable)
if (auth > 0) {
    strcat(rqh, "Authorization: Basic ");
    strcat(rqh, authorization);
    strcat(rqh, "\r\n");
}

The condition here only checks whether credentials were provided (auth > 0). It does not check whether the connection is using SSL/TLS. This means:

  • Connect to http://stream.example.com with credentials? → Header is sent in plaintext. ❌
  • Connect to https://stream.example.com with credentials? → Header is sent over TLS. ✅

Both cases triggered the same code path.

Real-World Attack Scenario

Imagine the following scenario:

  1. A developer builds a home audio player using an ESP32 and the audioI2S library.
  2. They subscribe to a private internet radio service that requires a username and password.
  3. They hardcode those credentials in their firmware and flash the device.
  4. The device sits on their home Wi-Fi network, connecting to http://radio.example.com every time it boots.

Now imagine a neighbor, a guest, or a compromised device on the same network runs a simple packet capture:

sudo tcpdump -i wlan0 -A port 80 | grep -i authorization

Within seconds of the ESP32 booting, they see:

Authorization: Basic dXNlcjpteXNlY3JldHBhc3N3b3Jk

Decoded: user:mysecretpassword

The attack requires no active intrusion, no brute forcing, no special tools beyond a laptop with Wi-Fi in monitor mode. It's entirely passive.

Why Is This Worse on Embedded Devices?

On a web application, if credentials are compromised, you can force a password reset. On an ESP32:

  • The credentials are likely compiled into the firmware binary
  • Updating firmware requires physical access or a working OTA pipeline
  • Users often don't know a compromise has occurred
  • The same credentials may protect other services

The blast radius of a single captured packet can be significant.


The Fix

The fix is elegant in its simplicity — a single additional condition:

// AFTER (fixed)
if (auth > 0 && m_f_ssl) {
    strcat(rqh, "Authorization: Basic ");
    strcat(rqh, authorization);
    strcat(rqh, "\r\n");
}

By adding && m_f_ssl, the Authorization header is now only appended when the connection is SSL/TLS-encrypted. If a caller tries to authenticate over a plain HTTP connection, the credentials are silently withheld rather than transmitted in the clear.

What Does m_f_ssl Represent?

In the ESP32-audioI2S library, m_f_ssl is a boolean member flag that is set to true when the library establishes a secure (HTTPS/SSL) connection rather than a plain HTTP one. It's already tracked internally by the library for other purposes — the fix simply reuses this existing signal to gate credential transmission.

The Full Diff

- if (auth > 0) {
+ if (auth > 0 && m_f_ssl) {
      strcat(rqh, "Authorization: Basic ");
      strcat(rqh, authorization);
      strcat(rqh, "\r\n");
  }

One line changed. One condition added. Credentials no longer travel over unencrypted connections.

Limitations to Be Aware Of

This fix prevents the leak but doesn't change the underlying authentication scheme. HTTP Basic Auth, even over TLS, still has weaknesses:

  • Credentials are sent on every request (no session tokens)
  • No protection against server-side compromise
  • No mutual authentication

For highest security, consider digest authentication or token-based schemes if your streaming server supports them. But for the vast majority of ESP32 audio projects, "only send credentials over TLS" is a practical and sufficient improvement.


Prevention & Best Practices

1. Always Tie Authentication to Transport Security

Never send credentials — regardless of encoding scheme — over unencrypted channels. This applies to:

  • HTTP Basic Auth
  • API keys in headers
  • Session tokens
  • OAuth bearer tokens

If the transport isn't encrypted, the credential isn't protected.

2. Use HTTPS Endpoints Where Possible

When connecting to audio streams or any authenticated service from an ESP32, prefer https:// URLs. The ESP32's WiFiClientSecure class supports TLS, and the audioI2S library already handles HTTPS connections — use them.

// Prefer this:
audio.connecttohost("https://stream.example.com/radio", "user", "pass");

// Not this:
audio.connecttohost("http://stream.example.com/radio", "user", "pass");

3. Avoid Hardcoding Credentials in Firmware

Store credentials in:
- NVS (Non-Volatile Storage) on the ESP32, loaded at runtime
- A configuration file on SPIFFS/LittleFS, excluded from version control
- A provisioning flow that sets credentials after deployment

This way, if firmware is extracted or shared, credentials aren't embedded in the binary.

4. Validate Connection Security Before Sending Sensitive Data

This is the pattern the fix implements — a general principle worth applying everywhere:

// Pattern: gate sensitive operations on security preconditions
if (hasCredentials && isConnectionSecure) {
    sendCredentials();
}

5. Code Review for Authentication Logic

When reviewing authentication code, always ask:
- Is the transport encrypted? (TLS/SSL check)
- Are credentials validated server-side? (not just client-side)
- What happens if the security check fails? (fail closed, not open)

Relevant Standards and References

Standard Relevance
OWASP Top 10 - A02: Cryptographic Failures Transmitting credentials without encryption
CWE-319: Cleartext Transmission of Sensitive Information Exact category for this vulnerability
CWE-522: Insufficiently Protected Credentials Credential storage and transmission weaknesses
RFC 7617 - HTTP Basic Auth Specification explicitly requiring TLS

Tools to Detect This Issue

  • Static analysis: Tools like Semgrep can be configured with rules that flag Basic Auth usage without TLS checks
  • Network analysis: Wireshark or tcpdump to verify no credentials appear in plaintext traffic
  • Firmware analysis: Binwalk to check if credentials are embedded in compiled firmware
  • Automated security scanning: AI-assisted scanners (like the one that caught this issue) can identify authentication logic that lacks transport security checks

Conclusion

This vulnerability is a textbook example of how a small oversight in authentication logic can have outsized consequences on embedded systems. The ESP32-audioI2S library correctly implemented the mechanics of HTTP Basic Auth — encoding credentials, attaching the header — but missed the critical precondition: Basic Auth is only safe when the transport is encrypted.

The fix is a single line, but the lesson is broader:

Authentication and encryption are not the same thing. You need both.

For embedded IoT developers, the stakes are higher than on traditional platforms. Credentials are hard to rotate, devices are often unmonitored, and network environments (shared Wi-Fi, guest networks, coffee shops) are frequently untrusted. Building in transport security checks from the start — and having automated tools that catch regressions — is not optional. It's essential.

If you're using the ESP32-audioI2S library in a project that authenticates against a streaming server, update to the patched version and ensure your stream URLs use https://.


This vulnerability was identified and fixed by OrbisAI Security as part of an automated security scanning pipeline. Automated security tooling caught what human code review missed — a reminder that layered security practices, including static analysis and AI-assisted scanning, are worth investing in.

Frequently Asked Questions

What is HTTP Basic Auth credential exposure?

HTTP Basic Authentication encodes credentials in Base64 (not encryption) and transmits them in the Authorization header. When sent over plain HTTP, anyone on the network can decode and read them.

How do you prevent HTTP Basic Auth credential exposure in ESP32/Arduino?

Check that the connection uses HTTPS/SSL before adding the Authorization header. Only transmit credentials over encrypted TLS connections.

What CWE is HTTP Basic Auth credential exposure?

CWE-319: Cleartext Transmission of Sensitive Information. This covers any scenario where sensitive data is sent over an unencrypted channel.

Is Base64 encoding enough to protect HTTP Basic Auth credentials?

No. Base64 is encoding, not encryption. Any attacker who captures the traffic can instantly decode Base64 credentials. TLS encryption is required for protection.

Can static analysis detect HTTP Basic Auth credential exposure?

Yes. Static analysis tools can identify when Authorization headers are constructed without verifying the connection uses HTTPS, flagging potential cleartext credential transmission.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #4

Related Articles

critical

How plaintext credential storage happens in C embedded firmware (wifi.cpp) and how to fix it

A migration function in `main/wifi.cpp` was designed to move legacy WiFi credentials from plaintext SD card files into encrypted NVS storage, but a logic flaw meant that if the NVS write failed, the plaintext files were never deleted. This left SSID and password data recoverable from the SD card filesystem — even after "deletion" on FAT — by anyone with physical access to the device. The fix restructures the deletion logic so plaintext files are always wiped, regardless of whether the NVS migrat

critical

How heap buffer overflow happens in C WiFi frame capture and how to fix it

A critical buffer overflow vulnerability in the ESP32 WiFi frame capture feature (feat_capture_hs.c) allowed attackers within WiFi range to craft oversized 802.11 frames that would overflow heap buffers and achieve remote code execution. The fix adds explicit length validation before memcpy operations and rejects oversized frames rather than silently truncating them.

critical

How buffer overflow in memcpy() happens in C/C++ embedded firmware and how to fix it

A critical buffer overflow vulnerability was discovered in the ESP32-based micro-journal firmware where `memcpy()` calls used `strlen()` without bounds checking, allowing oversized USB descriptor strings to corrupt adjacent memory. The fix replaces unbounded `strlen()` with `strnlen()` calls that enforce the destination buffer sizes (8, 16, and 4 bytes respectively), preventing heap/stack corruption from malicious USB devices.

critical

Stack Buffer Overflow in FTM File Parser: How strcpy() Almost Enabled Arbitrary Code Execution on ESP32

A critical stack buffer overflow vulnerability was discovered in `ftm_file.cpp`, where unchecked `strcpy()` calls allowed attacker-controlled filenames from crafted FTM files to overwrite stack memory, including the saved return address, enabling arbitrary code execution on ESP32 devices. The fix replaces both dangerous `strcpy()` calls with bounds-checked `strncpy()` plus explicit null-termination, eliminating the overflow vector entirely. This is a textbook reminder that unsafe C string functi

high

Stack Corruption on ESP32: When memcpy Reads Beyond UART Buffer Bounds

A high-severity vulnerability in ESP32 firmware allowed attackers to trigger stack and heap corruption by sending malformed UART frames shorter than expected to an mmWave sensor driver. Multiple `memcpy` operations copied data into fixed-size local variables without first verifying the source buffer was large enough, opening the door to arbitrary code execution. The fix replaces magic-number length guards with `sizeof()`-based checks that are portable, self-documenting, and provably correct.

medium

How insecure update manifest parsing happens in C++ UpdateHelper.cpp and how to fix it

TrafficMonitor's software update mechanism in `UpdateHelper.cpp` fetched and parsed update manifests from remote servers without validating the version string or enforcing trusted download URLs, leaving users exposed to man-in-the-middle (MITM) attacks. An attacker on the same network could intercept the update channel and inject a malicious binary under a crafted version string or an HTTP download link pointing to attacker-controlled infrastructure. The fix adds strict version-string sanitizati