DEV Community

LoseNine
LoseNine

Posted on

Firefox Fingerprint Browser Customization: Supporting Password-Protected HTTP Proxies

Customizing Firefox to Read HTTP Proxy Credentials from an fpfile

Join my Discord community to learn, share, and discuss together: https://discord.gg/rX2vkNNW

This article explains a small Firefox source-code customization for HTTP proxy
authentication.

The goal is simple: when Firefox receives an HTTP authentication challenge from
a password-protected proxy, it first tries to read the username and password from
a local fpfile passed through the --fpfile command-line argument. If the file
is missing, unreadable, or incomplete, Firefox falls back to its original
username/password prompt.

This is useful when running automated or customized Firefox builds where proxy
credentials should be provided by local runtime configuration instead of an
interactive dialog.

Modified File

The core change is in:

C:\firefox\firefox\netwerk\protocol\http\nsHttpChannelAuthProvider.cpp
Enter fullscreen mode Exit fullscreen mode

Firefox enters nsHttpChannelAuthProvider::GetCredentialsForChallenge() when it
needs credentials for an HTTP authentication challenge. A proxy challenge, such
as 407 Proxy Authentication Required, also goes through this path.

Originally, Firefox called PromptForIdentity() directly when credentials were
required. The customized version first attempts to load credentials from the file
specified by --fpfile. Only if that fails does it call the original prompt.

New Includes

The file adds a few includes near the top:

#include <fstream>
#include <string>

#include "base/command_line.h"
#include "nsStringStream.h"
#include "mozilla/Base64.h"
Enter fullscreen mode Exit fullscreen mode

For this HTTP proxy authentication change, the important ones are:

#include <fstream>
#include <string>
#include "base/command_line.h"
Enter fullscreen mode Exit fullscreen mode

They are used to read the local fpfile, parse text, and access the current
process command-line arguments.

Where the Logic Lives

The new logic is placed inside GetCredentialsForChallenge(), around the area
where Firefox previously prompted for credentials.

The original flow was:

rv = PromptForIdentity(level, proxyAuth, realm, aAuthType, authFlags, *ident);
if (NS_FAILED(rv)) return rv;
identFromURI = false;
Enter fullscreen mode Exit fullscreen mode

The customized flow is:

  1. Check whether the Firefox process was started with --fpfile.
  2. If it was, open that file.
  3. Read httpauth.username and httpauth.password.
  4. If both values exist, create an nsHttpAuthIdentity.
  5. If anything fails, continue with Firefox's original prompt behavior.

Reading HTTP Auth Credentials from fpfile

The code first checks whether the current Firefox process has the fpfile
switch:

const char kRuyiFileSwitch[] = "fpfile";
if (CommandLine::ForCurrentProcess()->HasSwitch(UTF8ToWide(kRuyiFileSwitch))) {
  std::wstring fpValue =
      CommandLine::ForCurrentProcess()->GetSwitchValue(
          UTF8ToWide(kRuyiFileSwitch));
Enter fullscreen mode Exit fullscreen mode

If the switch has a value, the path is converted to UTF-8 and opened:

if (!fpValue.empty()) {
  std::string fileName = WideToUTF8(fpValue);
  std::fstream fs(fileName, std::ios::in);
  if (fs.is_open()) {
Enter fullscreen mode Exit fullscreen mode

Then the file is read line by line. Both : and = are accepted as separators:

std::string line;
std::string username;
std::string password;

while (std::getline(fs, line)) {
  if (line.empty()) {
    continue;
  }

  size_t delimPos = line.find_first_of(":=");
  if (delimPos == std::string::npos ||
      delimPos + 1 >= line.length()) {
    continue;
  }

  std::string key = line.substr(0, delimPos);
  std::string value = line.substr(delimPos + 1);
Enter fullscreen mode Exit fullscreen mode

The key and value are trimmed, so spaces or tabs around them are allowed:

size_t keyStart = key.find_first_not_of(" \t");
if (keyStart == std::string::npos) {
  continue;
}
size_t keyEnd = key.find_last_not_of(" \t");
key = key.substr(keyStart, keyEnd - keyStart + 1);

size_t valueStart = value.find_first_not_of(" \t");
if (valueStart == std::string::npos) {
  value.clear();
} else {
  size_t valueEnd = value.find_last_not_of(" \t");
  value = value.substr(valueStart, valueEnd - valueStart + 1);
}
Enter fullscreen mode Exit fullscreen mode

Only two keys are recognized:

if (key == "httpauth.username") {
  username = value;
} else if (key == "httpauth.password") {
  password = value;
}
Enter fullscreen mode Exit fullscreen mode

When both values are present, Firefox creates an HTTP authentication identity:

if (!username.empty() && !password.empty()) {
  *ident = nsHttpAuthIdentity(
      EmptyString(), NS_ConvertUTF8toUTF16(username),
      NS_ConvertUTF8toUTF16(password));
}
Enter fullscreen mode Exit fullscreen mode

The first field, EmptyString(), is the domain. The next two fields are the
username and password.

Falling Back to Firefox's Original Prompt

If --fpfile is not provided, the file cannot be opened, the fields are missing,
or either credential is empty, ident remains empty. In that case, Firefox keeps
its original behavior and prompts the user:

if (ident->IsEmpty()) {
  rv = PromptForIdentity(level, proxyAuth, realm, aAuthType, authFlags,
                         *ident);
  if (NS_FAILED(rv)) return rv;
}
identFromURI = false;
Enter fullscreen mode Exit fullscreen mode

In practice:

  • If the fpfile contains both httpauth.username and httpauth.password, Firefox authenticates automatically without showing the prompt.
  • If the fpfile is missing, unreadable, or incomplete, Firefox behaves exactly as before and shows the username/password dialog.

fpfile Format

Start Firefox with:

--fpfile=C:\firefox\fp.txt
Enter fullscreen mode Exit fullscreen mode

The fpfile can use =:

httpauth.username=your_user
httpauth.password=your_password
Enter fullscreen mode Exit fullscreen mode

It can also use ::

httpauth.username: your_user
httpauth.password: your_password
Enter fullscreen mode Exit fullscreen mode

Use real credentials only in your local fpfile. Do not put production proxy
passwords into documentation, code comments, commits, screenshots, or blog posts.

Important Behavior and Limitations

This implementation uses one global HTTP authentication username/password pair.
It does not currently match credentials by proxy host or port.

It also does not restrict the injected credentials to proxyAuth == true.
That means a normal website HTTP Basic or Digest challenge that reaches the same
authentication path may also receive this httpauth.* identity.

For a tighter production design, I would add at least two extra guards:

  • Apply the fpfile credentials only when proxyAuth == true.
  • Scope credentials by proxy host and port instead of using a single global pair.

Why This Approach

The main benefit is that the change is narrow and preserves Firefox's default
behavior as the fallback path.

The custom build can run without an interactive proxy credential prompt when
valid fpfile credentials are available. At the same time, normal Firefox behavior
still works when the file is absent or invalid.

This keeps the runtime configuration outside the profile and avoids saving proxy
credentials into Firefox preferences.

Summary

The customization changes Firefox's HTTP authentication flow so that proxy
credentials can be supplied through a local --fpfile configuration file.

The important pieces are:

  • The change lives in nsHttpChannelAuthProvider.cpp.
  • The code reads httpauth.username and httpauth.password from --fpfile.
  • It creates an nsHttpAuthIdentity only when both values are present.
  • If reading fails, Firefox falls back to PromptForIdentity().
  • Real passwords should stay in the local fpfile, not in public documentation.

Top comments (0)