Archive

Posts Tagged ‘cybersecurity’

The Static HttpClient That Wouldn’t Rotate: A Tale of Pooled Connections

The symptom

A production .NET service had been running fine for months. It made outbound HTTP calls through a rotating proxy provider — the kind that promises a new exit IP for each request. Then one day, requests started timing out. Not failing cleanly with a 4xx or 5xx. Timing out. Each one sat there for thirty seconds or more before giving up.

The fix that worked, every time, was restarting IIS.

That’s an interesting fix. It tells you the problem isn’t with the code’s logic — the code is the same after the restart as before. It tells you the problem is with state that the application is accumulating over its lifetime. And it tells you that whatever that state is, it lives somewhere inside the application’s process, not on disk or in a database.

The setup

Here’s the shape of the code:

csharp

private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler
{
Proxy = _rotatingProxy,
UseProxy = true
});

A static HttpClient. A rotating proxy. By every piece of Microsoft guidance written in the last decade, this is correct. “Don’t create a new HttpClient per request — it’ll exhaust your sockets.” So a static instance it is.

The intent: every outbound request goes through the proxy, the proxy assigns a different exit IP each time, and the upstream service sees a varied stream of source addresses rather than a single hammering client.

The reality, as we’ll see, was very different.

Anti-pattern #1: assuming HttpClient opens a new connection per request

This is the foundational misunderstanding. HttpClient does not open a new TCP connection for each request. It maintains a connection pool, keyed by destination host, and reuses pooled connections wherever possible. This is HTTP keep-alive doing its job — and it’s a good thing for the general case, because TCP and TLS handshakes are expensive.

But here’s the consequence for a rotating proxy: rotation happens per TCP connection, not per HTTP request. If your HttpClient opens one TCP connection on the first request and then reuses that connection for every subsequent request, you get one exit IP, forever. Your “rotating” proxy is effectively a sticky proxy, because nothing is ever telling it you want a new connection.

The static HttpClient makes this worse, because the connection pool lives for the lifetime of the process. There is no natural moment at which the pooled connection gets replaced. It just sits there, being reused, until something forces it to close.

Anti-pattern #2: no timeout configured

HttpClient.Timeout defaults to 100 seconds. Most developers never set it. For most APIs this is fine — if the call is going to succeed, it’ll succeed in well under 100 seconds.

But “100 seconds” becomes a serious problem when something on the network silently fails. A pooled TCP connection that’s been killed by an intermediate firewall, a load balancer that’s stopped routing your traffic, a remote server that’s decided to tarpit you — none of these produce a clean error. They produce a hang. And your code will sit in that hang for the full timeout duration.

The combination of “no timeout” + “pooled connection that’s gone bad” is the textbook recipe for the mysterious thirty-second-plus pause that I described at the top. The connection looks fine to your code. The pool hands it out. The request goes out. Nothing comes back. You wait.

Anti-pattern #3: silent bans look identical to network glitches

Modern anti-abuse systems rarely respond to suspected scraping or hammering with a clean 403 Forbidden. A 403 is a gift to the attacker — it tells them immediately that they need to rotate. Far more effective is to do nothing: accept the TCP connection, accept the request, and then never send a response. The attacker’s client hangs. Their thread is consumed. Their queue backs up. Their logs fill with timeouts that look indistinguishable from random network failures.

If you’re not expecting this behaviour, you’ll spend a lot of time chasing phantom network problems, blaming your cloud provider, blaming your proxy, blaming DNS. The truth — that you’ve been quietly added to a deny list — is invisible from the symptoms alone.

When you combine this with the pooled-connection problem, you get a particularly nasty failure mode: you get one exit IP from your proxy, you hammer the upstream API from that single IP, the upstream API decides you’re abusive and starts tarpitting that IP, and now every single request hangs because they’re all going through the same pooled connection to the same banned IP. The only way out is to break the connection — which an IIS restart conveniently does as a side effect.

What’s actually going on

Putting these three pieces together, the mechanism is:

  1. The application starts and opens its first outbound connection through the proxy. The proxy assigns exit IP X.
  2. .NET pools that connection and reuses it for every subsequent request.
  3. Every request to the upstream service comes from IP X.
  4. Upstream eventually notices the volume from IP X and starts silently dropping requests.
  5. Now every request hangs for the default 100-second timeout, or however long the underlying TCP layer takes to give up.
  6. Restarting the application destroys the connection pool, forces a new TCP connection on the next request, and the proxy assigns a new exit IP. Service is restored — until the cycle repeats.

The root cause is not the proxy. The proxy is doing what it was told. The root cause is that the application is never asking it for a new connection.

The pattern: disable keep-alive when you actually want rotation

The fix is one line:

csharp

client.DefaultRequestHeaders.ConnectionClose = true;

This sets the Connection: close header on every outgoing request. It tells .NET’s connection pool not to reuse the underlying TCP socket after the response. The next request opens a fresh connection. The proxy assigns a fresh exit IP. Rotation now works the way you expected it to work all along.

The complete pattern looks like this:

csharp

private static readonly HttpClient _httpClient = BuildHttpClient();
private static HttpClient BuildHttpClient()
{
var handler = new HttpClientHandler
{
Proxy = _rotatingProxy,
UseProxy = true
};
var client = new HttpClient(handler)
{
Timeout = TimeSpan.FromSeconds(15)
};
client.DefaultRequestHeaders.ConnectionClose = true;
return client;
}

Three things to notice:

The HttpClient is still static. We haven’t abandoned the standard guidance. The object itself is long-lived; it’s just configured not to pool connections internally.

The timeout is now sane. Fifteen seconds is appropriate for most API calls. If something goes wrong — a tarpit, a network glitch, anything — we fail fast and try again, rather than blocking a thread for a minute and a half.

There’s no manual connection management. No tracking of connection age, no recycling logic, no locks around pool rebuilds. The framework handles it because we’ve told it to handle it the right way.

The cost, and when not to do this

Disabling keep-alive isn’t free. Every request now pays the full cost of a TCP handshake and a TLS negotiation. Through a proxy, that’s typically 400–800ms of overhead per request, on top of whatever the upstream service itself takes.

If you’re making thousands of requests per second to the same endpoint and you want a stable identity from the upstream service’s perspective, do not do this. Keep-alive exists for excellent reasons. The pattern in this post is specifically for the case where you have a rotating proxy and you actively want to defeat connection reuse so that rotation happens.

For high-volume scenarios where you want both rotation and performance, the right answer is usually session tokens — most rotating proxy providers let you encode a session identifier in the proxy username, and you can vary that token per request to force rotation without giving up keep-alive on individual sessions. That’s a more involved pattern and a topic for another post.

The broader lesson

The interesting thing about this bug is how each of the three anti-patterns is, in isolation, considered best practice or at least defensible. Static HttpClient is recommended. Default timeouts are what you get if you don’t think about them. And nobody designs their code around the possibility of being silently banned by an upstream service.

It’s the interaction between these three things, plus the presence of a rotating proxy, that produces the failure. And the failure mode — a hang, not an error — is the worst possible signal, because it tells you nothing useful and points you in completely the wrong direction.

The takeaway isn’t “always disable keep-alive” or “always set short timeouts.” The takeaway is that when your code interacts with infrastructure that has its own behaviours — proxies, load balancers, anti-abuse layers — the default settings of your HTTP client may not match the assumptions you’re making about how that infrastructure works. Take a moment to ask: does my code’s connection lifecycle match what I actually want to happen at the network level? If you have a rotating proxy and you’ve never thought about connection pooling, the answer is almost certainly no.

How to Set Up Your Own Custom Disposable Email Domain with Mailnesia

Disposable email addresses are incredibly useful for maintaining privacy online, avoiding spam, and testing applications. While services like Mailnesia offer free disposable emails, there’s an even more powerful approach: using your own custom domain with Mailnesia’s infrastructure.

Why Use Your Own Domain?

When you use a standard disposable email service, the domain (like @mailnesia.com) is publicly known. This means:

  • Websites can easily block known disposable email domains
  • There’s no real uniqueness to your addresses
  • You’re sharing the domain with potentially millions of other users

By pointing your own domain to Mailnesia, you get:

  • Higher anonymity – Your domain isn’t in any public disposable email database
  • Unlimited addresses – Create any email address on your domain instantly
  • Professional appearance – Use a legitimate-looking domain for sign-ups
  • Better deliverability – Less likely to be flagged as a disposable email

What You’ll Need

  • A domain name you own (can be purchased for as little as $10/year)
  • Access to your domain’s DNS settings
  • That’s it!

Step-by-Step Setup

1. Access Your DNS Settings

Log into your domain registrar or DNS provider (e.g., Cloudflare, Namecheap, GoDaddy) and navigate to the DNS management section for your domain.

2. Add the MX Record

Create a new MX (Mail Exchange) record with these values:

Type: MX
Name: @ (or leave blank for root domain)
Mail Server: mailnesia.com
Priority/Preference: 10
TTL: 3600 (or default)

Important: Make sure to include the trailing dot if your DNS provider requires it: mailnesia.com.

3. Wait for DNS Propagation

DNS changes can take anywhere from a few minutes to 48 hours to fully propagate, though it’s usually quick (under an hour). You can check if your MX record is live using a DNS lookup tool.

4. Start Using Your Custom Disposable Emails

Once the DNS has propagated, any email sent to any address at your domain will be received by Mailnesia. Access your emails by going to:

https://mailnesia.com/mailbox/USERNAME

Where USERNAME is the part before the @ in your email address.

For example:

  • Email sent to: testing123@yourdomain.com
  • Access inbox at: https://mailnesia.com/mailbox/testing123

Use Cases

This setup is perfect for:

  • Service sign-ups – Use a unique email for each service (e.g., netflix@yourdomain.com, github@yourdomain.com)
  • Testing – Developers can test email functionality without setting up mail servers
  • Privacy protection – Keep your real email address private
  • Spam prevention – If an address gets compromised, simply stop using it
  • Tracking – See which services sell or leak your email by using unique addresses per service

Important Considerations

Security and Privacy

  • No authentication required – Anyone who guesses or knows your username can access that mailbox. Don’t use this for sensitive communications.
  • Temporary storage – Mailnesia emails are not stored permanently. They’re meant to be disposable.
  • No sending capability – This setup only receives emails; you cannot send from these addresses through Mailnesia.

Best Practices

  1. Use random usernames – Instead of newsletter@yourdomain.com, use something like j8dk3h@yourdomain.com for better privacy
  2. Subdomain option – Consider using a subdomain like disposable.yourdomain.com to keep it separate from your main domain
  3. Don’t use for important accounts – Reserve this for non-critical services only
  4. Monitor your usage – Keep track of which addresses you’ve used where

Technical Notes

  • You can still use your domain for regular email by setting up additional MX records with different priorities
  • Some providers may allow you to set up email forwarding in addition to this setup
  • Check Mailnesia’s terms of service for any usage restrictions

Verifying Your Setup

To test if everything is working:

  1. Send a test email to a random address at your domain (e.g., test12345@yourdomain.com)
  2. Visit https://mailnesia.com/mailbox/test12345
  3. Your email should appear within a few seconds

Troubleshooting

Emails not appearing?

  • Verify your MX record is correctly set up using an MX lookup tool
  • Ensure DNS has fully propagated (can take up to 48 hours)
  • Check that you’re using the correct mailbox URL format

Getting bounced emails?

  • Make sure the priority is set to 10 or lower
  • Verify there are no conflicting MX records

Conclusion

Setting up your own custom disposable email domain with Mailnesia is surprisingly simple and provides a powerful privacy tool. With just a single DNS record change, you gain access to unlimited disposable email addresses on your own domain, giving you greater control over your online privacy and reducing spam in your primary inbox.

The enhanced anonymity of using your own domain, combined with the zero-configuration convenience of Mailnesia’s infrastructure, makes this an ideal solution for anyone who values their privacy online.


Remember: This setup is for non-sensitive communications only. For important accounts, always use a proper email service with security features like two-factor authentication.

How to Extract #EXIF Data from an Image in .NET 8 with #MetadataExtractor

GIT REPO : https://github.com/infiniteloopltd/ExifResearch

When working with images, EXIF (Exchangeable Image File Format) data can provide valuable information such as the camera model, date and time of capture, GPS coordinates, and much more. Whether you’re building an image processing application or simply want to extract metadata for analysis, knowing how to retrieve EXIF data in a .NET environment is essential.

In this post, we’ll walk through how to extract EXIF data from an image in .NET 8 using the cross-platform MetadataExtractor library.

Why Use MetadataExtractor?

.NET’s traditional System.Drawing.Common library has limitations when it comes to cross-platform compatibility, particularly for non-Windows environments. The MetadataExtractor library, however, is a powerful and platform-independent solution for extracting metadata from various image formats, including EXIF data.

With MetadataExtractor, you can read EXIF metadata from images in a clean, efficient way, making it an ideal choice for .NET Core and .NET 8 developers working on cross-platform applications.

Step 1: Install MetadataExtractor

To begin, you need to add the MetadataExtractor NuGet package to your project. You can install it using the following command:

bashCopy codedotnet add package MetadataExtractor

This package supports EXIF, IPTC, XMP, and many other metadata formats from various image file types.

Step 2: Writing the Code to Extract EXIF Data

Now that the package is installed, let’s write some code to extract EXIF data from an image stored as a byte array.

Here is the complete function:

using System;
using System.Collections.Generic;
using System.IO;
using MetadataExtractor;
using MetadataExtractor.Formats.Exif;

public class ExifReader
{
public static Dictionary<string, string> GetExifData(byte[] imageBytes)
{
var exifData = new Dictionary<string, string>();

try
{
using var ms = new MemoryStream(imageBytes);
var directories = ImageMetadataReader.ReadMetadata(ms);

foreach (var directory in directories)
{
foreach (var tag in directory.Tags)
{
// Add tag name and description to the dictionary
exifData[tag.Name] = tag.Description;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error reading EXIF data: {ex.Message}");
}

return exifData;
}
}

How It Works:

  1. Reading Image Metadata: The function uses ImageMetadataReader.ReadMetadata to read all the metadata from the byte array containing the image.
  2. Iterating Through Directories and Tags: EXIF data is organized in directories (for example, the main EXIF data, GPS, and thumbnail directories). We iterate through these directories and their associated tags.
  3. Handling Errors: We wrap the logic in a try-catch block to ensure any potential errors (e.g., unsupported formats) are handled gracefully.

Step 3: Usage Example

To use this function, you can pass an image byte array to it. Here’s an example:

using System;
using System.IO;

class Program
{
static void Main()
{
// Replace with your byte array containing an image
byte[] imageBytes = File.ReadAllBytes("example.jpg");

var exifData = ExifReader.GetExifData(imageBytes);

foreach (var kvp in exifData)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
}
}

This code reads an image from the file system as a byte array and then uses the ExifReader.GetExifData method to extract the EXIF data. Finally, it prints out the EXIF tags and their descriptions.

Example Output:

If the image contains EXIF metadata, the output might look something like this:

  "Compression Type": "Baseline",
"Data Precision": "8 bits",
"Image Height": "384 pixels",
"Image Width": "512 pixels",
"Number of Components": "3",
"Component 1": "Y component: Quantization table 0, Sampling factors 2 horiz/2 vert",
"Component 2": "Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert",
"Component 3": "Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert",
"Make": "samsung",
"Model": "SM-G998B",
"Orientation": "Right side, top (Rotate 90 CW)",
"X Resolution": "72 dots per inch",
"Y Resolution": "72 dots per inch",
"Resolution Unit": "Inch",
"Software": "G998BXXU7EWCH",
"Date/Time": "2023:05:02 12:33:47",
"YCbCr Positioning": "Center of pixel array",
"Exposure Time": "1/33 sec",
"F-Number": "f/2.2",
"Exposure Program": "Program normal",
"ISO Speed Ratings": "640",
"Exif Version": "2.20",
"Date/Time Original": "2023:05:02 12:33:47",
"Date/Time Digitized": "2023:05:02 12:33:47",
"Time Zone": "+09:00",
"Time Zone Original": "+09:00",
"Shutter Speed Value": "1 sec",
"Aperture Value": "f/2.2",
"Exposure Bias Value": "0 EV",
"Max Aperture Value": "f/2.2",
"Metering Mode": "Center weighted average",
"Flash": "Flash did not fire",
"Focal Length": "2.2 mm",
"Sub-Sec Time": "404",
"Sub-Sec Time Original": "404",
"Sub-Sec Time Digitized": "404",
"Color Space": "sRGB",
"Exif Image Width": "4000 pixels",
"Exif Image Height": "3000 pixels",
"Exposure Mode": "Auto exposure",
"White Balance Mode": "Auto white balance",
"Digital Zoom Ratio": "1",
"Focal Length 35": "13 mm",
"Scene Capture Type": "Standard",
"Unique Image ID": "F12XSNF00NM",
"Compression": "JPEG (old-style)",
"Thumbnail Offset": "824 bytes",
"Thumbnail Length": "49594 bytes",
"Number of Tables": "4 Huffman tables",
"Detected File Type Name": "JPEG",
"Detected File Type Long Name": "Joint Photographic Experts Group",
"Detected MIME Type": "image/jpeg",
"Expected File Name Extension": "jpg"

This is just a small sample of the information EXIF can store. Depending on the camera and settings, you may find data on GPS location, white balance, focal length, and more.

Why Use EXIF Data?

EXIF data can be valuable in various scenarios:

  • Image processing: Automatically adjust images based on camera settings (e.g., ISO or exposure time).
  • Data analysis: Track when and where photos were taken, especially when handling large datasets of images.
  • Digital forensics: Verify image authenticity by analyzing EXIF metadata for manipulation or alterations.

Conclusion

With the MetadataExtractor library, extracting EXIF data from an image is straightforward and cross-platform compatible. Whether you’re building a photo management app, an image processing tool, or just need to analyze metadata, this approach is an efficient solution for working with EXIF data in .NET 8.

By using this solution, you can extract a wide range of metadata from images, making your applications smarter and more capable. Give it a try and unlock the hidden data in your images!