Hi Rick, Do you have a utility to encrypt/decrypt query strings instead of passing Id's in the url? I was going to look into incorporating IDataProtector unless you already had a wrapper in your AspNetCore classes or something...
/adminbrowser/requestfordocumentation/944267123 to /adminbrowser/requestfordocumentation/VzFtYWV4dXN3czEyMw==
Thanks! Craig
Hi Craig, You can use a function like this:
LPARAMETERS lcUrl, lBase64
LOCAL lcKey
lcKey = "Mykeymykey"
loEnc = CREATEOBJECT ("wwEncryption")
RETURN m.loEnc.EncryptString (TRIM (m.lcUrl), m.lcKey, m.lBase64)
Thanks for the feedback Vincent, but I'm looking for a c# MVC .NET Core solution.
There are libraries available in .net core for doing this now using IDataProtector but whenever I am building these solutions out I like to check with Rick to see if he has a better solution or wrapper class first.
It looks like you have to register at startup.
services.AddDataProtection();
services.AddScoped<UrlEncryptor>();
and encrypt/decrypt in your controller, which Rick wrote...
string encryptedId = _urlEncryptor.Protect(subscriber.SubscriberId);
string url = Url.Action("RequestForDocumentation", new { id = encryptedId });
decrypt coming back...
public IActionResult RequestForDocumentation(string id)
{
string decryptedId;
try
{
decryptedId = _urlEncryptor.Unprotect(id);
}
catch
{
return BadRequest("Invalid or tampered ID.");
}
// Use decryptedId safely here
}
You have to be careful with the DataProtection APIs because they are machine specific. The keys are generated using a unique Machine key. So if you end up in scenarios where you need to encrypt a URL elsewhere or you're using load balanced servers (implicitly or explicitly) you can run into trouble with this.
If you need to make sure you can universally encrypt you should explicit encryption using an application key that's shared across all possible instances and applications that might have to do this.
As far as West Wind Tools goes there's an Encryption class with static methods that do two-way encryption.
The two-way encryption functionality is just a thin wrapper around AES encryption in Windows using the .NET libraries. The methods are static so you can use them from anywhere.
string encKey = "superseekrit1";
// one way you can set the key is globally at startup
// Encryption.EncryptionKey = encKey;
// Or you can pass it in in each request
var enc = Encryption.EncryptString(queryString, encKey, true); // bin hex is more compact
Console.WriteLine(enc);
var dec = Encryption.DecryptString(enc, encKey, true);
Console.WriteLine(enc);
Vincent had it sort of right 😄 - it's using the same code base that the FoxPro wrapper is using.
+++ Rick ---
Ah! Great points on machine specific/load balancing and so glad I asked first! Good news on the WW Encryptor as I am very familiar with it already... Assuming the encrypt algorithm produces keys that are URL friendly, I'm good to go! Thanks guys!
I've run into a couple blockers on this URL Encryption initiative...
- If you encrypt an INT id, it will throw an API error because it is expecting and INT. To resolve, the method must change to accept a STRING, then covert the string back to INT, or some other type of work around...
Some quick hack code...
[HttpGet]
[Route("/adminbrowser/subscriber")]
[Route("/adminbrowser/subscriber/{Id}")]
public IActionResult Subscriber([FromRoute] string Id)
{
int subscriberId = 0;
if (Id.Length < 9)
{
subscriberId = Convert.ToInt32(Id);
}
// Decrypt the Id if it is encrypted
if (Id != null && Id.Length > 9)
{
Id = Encryption.DecryptString(Id, "LGHIB", true);
}
- Bigger issue with no nice workaround other than maybe calling an API to encrypt the value first is when calling from Javascript. The c# encryption method is not available.
afterSelect: function (item) {
$inputu.data('typeahead').lastSelection = item;
if (item.id.length == 3)
window.location = "/adminbrowser/unit/" + encodeURIComponent(item.id);
else
window.location = "/adminbrowser/subscriber/" + encodeURIComponent(item.id);
}
Encryption always works on bytes or strings. An int has to be converted to string to make any sense in encryption.
As to client side encoding for keys - that's generally a bad idea. It's better to generate any thing that needs to be encrypted into the page from the server. If you really have to do it on the client, pass the key down from the server in encrypted form in the model so that you can use it without having to do anything on the client or else make an API call to get it. You can't pass the secret keys to the client since that would compromise the encryption.
There are ways to do this with public key encryption and certificates but that's a whole lot more complicated than using this simple two-way encryption scheme. Also hashing and data storage and retrieval are common (that's what the CC companies use).
+++ Rick ---