
Product
Rust Support Now in Beta
Socket's Rust support is moving to Beta: all users can scan Cargo projects and generate SBOMs, including Cargo.toml-only crates, with Rust-aware supply chain checks.
ASP.NET Core integration for CloakId - includes model binding, OpenAPI/Swagger support, and metrics for obfuscated IDs.
A .NET library that provides automatic encoding/decoding of numeric properties to obfuscated strings during JSON serialization using attributes. This helps prevent exposing internal numeric IDs in APIs while maintaining clean, readable code.
[Cloak]
to enable encodingWhile CloakId is not a substitute for proper security measures like authentication, authorization, and access control, it provides valuable protection against information disclosure and business intelligence gathering.
The German Tank Problem: During WWII, Allied forces estimated German tank production by analyzing captured tank serial numbers. By observing the highest serial number and using statistical analysis, they could accurately determine total production numbers and manufacturing rates. This same principle applies to modern web applications.
Competitor Analysis:
12345
15678
A6das1
and xnF9Hu
- no business intelligence can be extractedResource Enumeration:
/api/users/1
, /api/users/2
, etc.Remember: CloakId adds a defense-in-depth layer to make unauthorized reconnaissance significantly more difficult - it does not replace fundamental security practices.
CloakId provides a completely unintrusive solution by working transparently at the serialization boundary:
The Magic Happens at the Boundary:
/users/A6das1
→ id: 12345
)This boundary-based approach provides clean separation between your client and server code:
On the Server Side:
int
, long
, etc.)On the Client Side:
"A6das1"
, "xnF9Hu"
)Install the packages using Package Manager Console:
Install-Package CloakId
Install-Package CloakId.Sqids
Alternatively, using the .NET CLI:
dotnet add package CloakId
dotnet add package CloakId.Sqids
using CloakId;
using CloakId.Sqids;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddCloakId().WithSqids(options =>
{
options.MinLength = 6; // Configure minimum length
// options.Alphabet = "custom123456789abcdefghijk"; // Custom alphabet (optional)
});
var serviceProvider = services.BuildServiceProvider();
public class UserDto
{
[Cloak]
public int UserId { get; set; }
[Cloak]
public long AccountId { get; set; } // Regular properties without the attribute remain unchanged
public int RegularId { get; set; }
public string Name { get; set; }
[Cloak]
public int? OptionalId { get; set; }
}
var typeInfoResolver = serviceProvider.GetRequiredService<CloakIdTypeInfoResolver>();
var jsonOptions = new JsonSerializerOptions
{
TypeInfoResolver = typeInfoResolver
};
var user = new UserDto
{
UserId = 12345,
AccountId = 98765432109876,
RegularId = 999, // This remains as a number
Name = "John Doe",
OptionalId = 42
};
// Serialize - only [Cloak] properties become encoded strings
var json = JsonSerializer.Serialize(user, jsonOptions);
// Result: {"UserId":"A6das1","AccountId":"xnF9HulfM","RegularId":999,"Name":"John Doe","OptionalId":"JgaEBg"}
// Deserialize - strings decode back to original values
var deserializedUser = JsonSerializer.Deserialize<UserDto>(json, jsonOptions);
// deserializedUser.UserId == 12345
// deserializedUser.AccountId == 98765432109876
// deserializedUser.RegularId == 999 (unchanged)
The [Cloak]
attribute can be applied to the following numeric property types:
int
and int?
uint
and uint?
long
and long?
ulong
and ulong?
short
and short?
ushort
and ushort?
You can also use the codec directly for manual encoding/decoding:
var codec = serviceProvider.GetRequiredService<ICloakIdCodec>();
var originalValue = 12345;
var encoded = codec.Encode(originalValue, typeof(int)); // "A6das1"
var decoded = (int)codec.Decode(encoded, typeof(int)); // 12345
CloakId includes built-in support for ASP.NET Core model binding, allowing automatic conversion of encoded route parameters:
// Enable model binding in Program.cs
builder.Services.AddCloakId().WithSqids();
builder.Services.AddControllers().AddCloakIdModelBinding();
// Use in controllers
[HttpGet("{id}")]
public IActionResult GetUser([Cloak] int id) // Automatically converts "A6das1" → 12345
{
return Ok(new { UserId = id });
}
Routes like GET /api/users/A6das1
will automatically convert the encoded string to the numeric ID before reaching your controller method. See Model Binding Documentation for complete details.
CloakId provides configurable security options for model binding:
// Configure fallback behavior for enhanced security
builder.Services.AddControllers().AddCloakIdModelBinding(options =>
{
// Disable numeric fallback for better security (default: false)
// When false: only accepts encoded strings, rejects numeric IDs
// When true: accepts both encoded strings and numeric IDs (backwards compatibility)
options.AllowNumericFallback = false;
});
Security Note: Setting AllowNumericFallback = false
provides better security by rejecting any non-encoded values, but may break existing clients that send numeric IDs. The fallback behavior could potentially expose alphabet patterns through systematic testing.
CloakId includes built-in metrics using System.Diagnostics.Metrics
for monitoring security-related behavior:
cloakid_model_binding_decoding_success_total
- Successful decodingscloakid_model_binding_decoding_failure_total
- Failed decodingscloakid_model_binding_numeric_fallback_total
- Security-relevant: Fallback usagecloakid_model_binding_fallback_rejection_total
- Rejected requests when fallback disabledcloakid_model_binding_decoding_duration_ms
- Decoding performanceThe numeric fallback metric is particularly important for security monitoring as it can indicate potential attempts to probe the encoding alphabet through systematic testing.
// With Sqids
services.AddCloakId().WithSqids(options =>
{
options.Alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; // Custom alphabet
options.MinLength = 8; // Minimum length of encoded strings
});
// With custom codec
services.AddCloakId().WithCustomCodec<MyCustomCodec>();
// With pre-registered Sqids
services.AddCloakId().WithRegisteredSqids();
If you already have Sqids encoders registered in your DI container:
// First register your Sqids encoders
services.AddSingleton(new SqidsEncoder<int>(new SqidsOptions { /* your config */ }));
// ... register other encoders
// Then use the registered encoders
services.AddCloakId().WithRegisteredSqids();
Important: You cannot call both WithSqids()
and WithRegisteredSqids()
on the same builder. The second call will throw an InvalidOperationException
to prevent conflicting codec registrations.
You can implement your own encoding strategy by implementing ICloakIdCodec
:
public class MyCustomCodec : ICloakIdCodec
{
public string Encode(object value, Type valueType) { /* ... */ }
public object Decode(string encodedValue, Type targetType) { /* ... */ }
}
// Register custom codec by type
services.AddCloakId().WithCustomCodec<MyCustomCodec>();
// Register custom codec by instance
var myCodec = new MyCustomCodec();
services.AddCloakId().WithCustomCodec(myCodec);
// Register custom codec with factory
services.AddCloakId().WithCustomCodec(provider =>
{
var someService = provider.GetRequiredService<ISomeService>();
return new MyCustomCodec(someService);
});
When serialized to JSON, your attributed properties will look like this:
{
"UserId": "A6das1",
"AccountId": "xnF9HulfM",
"RegularId": 999,
"Name": "John Doe",
"OptionalId": "JgaEBg"
}
Instead of exposing the raw numeric values:
{
"UserId": 12345,
"AccountId": 98765432109876,
"RegularId": 999,
"Name": "John Doe",
"OptionalId": 42
}
Notice how only the properties marked with [Cloak]
are encoded, while RegularId
remains as a number.
CloakId is designed for performance with minimal overhead. You can run comprehensive benchmarks to see the performance characteristics:
# Run all benchmarks
./run-benchmarks.ps1
# Run only encoding/decoding benchmarks
./run-benchmarks.ps1 "*Encode*"
# Run only JSON serialization benchmarks
./run-benchmarks.ps1 "*Json*"
# Run only happy path tests
./run-benchmarks.ps1 "*HappyPath*"
# Run only error handling tests
./run-benchmarks.ps1 "*SadPath*"
# Quick validation run
./run-benchmarks.ps1 "*" --dry
Based on benchmarks, typical performance characteristics:
See /benchmarks/README.md
for detailed benchmark information.
This project is licensed under the MIT License - see the LICENSE file for details.
FAQs
ASP.NET Core integration for CloakId - includes model binding, OpenAPI/Swagger support, and metrics for obfuscated IDs.
We found that cloakid.aspnetcore demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket's Rust support is moving to Beta: all users can scan Cargo projects and generate SBOMs, including Cargo.toml-only crates, with Rust-aware supply chain checks.
Product
Socket Fix 2.0 brings targeted CVE remediation, smarter upgrade planning, and broader ecosystem support to help developers get to zero alerts.
Security News
Socket CEO Feross Aboukhadijeh joins Risky Business Weekly to unpack recent npm phishing attacks, their limited impact, and the risks if attackers get smarter.