Encoding two values in a short URL token - c#

I am working on a short URL app, where the token must identify 2 values: the link ID and the user ID. Ideally this token should be short.
For example, considering the URL http://sho.rt/15qq6, the token "15qq6" must identify the link and user ID.
I guess one option is to insert both values in a table and use the auto-generated ID as a token, but I would rather not. I would prefer a solution involving encryption.
How could I use the .NET encryption classes for such purpose, if possible? Many thanks for your help.

I'm not clear on how short you want your code. I posted some code online to encrypt any number of query arguments.
The result could be shortened by base64-encoding the result. That might still not be short enough for you though. (Note that I didn't base64-encode it because I had some concerns about base64 encoding is case-sensitive.)
Another approach would be to come up with a code that consists of an ID into your database and some sort of checksum. If the user tries modifying the ID, you could detect this. However, this approach may not be that secure since it might not be that hard to figure out how to create your own checksums.

Short answer is "You can't", at least, not easily.
Encryption typically doesn't change the length of the data being encrypted. So if you take the URL and UserId that you want to encode and encrypt them you'll end up with a token that's the same total length.
You could try compressing the data before encryption, but there's not a lot of redundancy in a single URL, and this won't buy you much.
You culd hash the data to give you a shorter result, but there's no way to reverse this process to get your URL and userId back.
If it's a short token you need then the only real option I can think of is a lookup table on the server, using the token as the key.

I don't think you understand exactly how Encryption works.
Encryption is just a technique for making it difficult to decode the response, without knowing the original encryption key.
The encrypted data is at least as long as the original data, if not larger.
There is no viable way of encoding a URL into a smaller amount of data, that's still valid in a URL.
Use a database for this, that's what they're for.
Edit: D'oh, Andrew beat me to it with a better response after editing.

You could use something like the RNGCryptoServiceProvider to generate a unique set of characters. Use a few constants strings holding a range of characters like "a" to "z", "A" to "Z", and "1" to "9". Save the randomly mixed case alphanumeric string with the original URL and UserID.

Generate random token and save link and user id in db for this token. It is security enough.

If you don't need encryption, than simple combination of Convert.ToBase64String and BitConverter.GetBytes will give you reasonable string. Note that Base64 uses some non-url cahnracters, so consider replacing them in result WikiPedia Base64, or using Base32 encoding.
int first =1234;
int second =789;
var encoded = Convert.ToBase64String(
BitConverter.GetBytes (((ulong)first<<32)+(ulong)second));

Related

Generate another Guid using one Guid and vice versa

Is there a way/algorithm/method to generate a new Guid (x) using our old GUid (y) and then get y back whenever we want from x?
Something similar to below answer but it shows a way to old Guid(I can consider it as a string) to convert to Guid but not a way back.
https://stackoverflow.com/a/9386095/5887074
How can I generate a GUID for a string?
P.S.: This is not related to anything security. The two Guids will just be used to find records from the table. We can convert Guid to string in this conversion if required.
There are thousands of ways: a guid is 128 bits, so you could flip one bit which would make it simple to translate back and forth. Or you could do modulo 42 and make it look as if you made something unpredictable. Or you could reverse the order of the bits, do a NOT operation on all of them or rearrange the bits by some predefined pattern.
But I suspect that you have a use case which you do not define. Please tell a bit more about the problem you want to solve. Your request sounds a little bit dangerous as it sounds as if you want to enable some kind of tracking between seemingly unrelated entities. If there is some security issues involved you are very likely to get it wrong if both cleartext (guid pre translation) and cipher (guid after translation) are public. Perhaps simple AES encryption would suffice as a translation function, but I think you need to specify you problems in much more details to get a useful answer.

strings from DynamoDB that were originally byte arrays have funky values

Now I'm not sure if this is something I'm doing wrong, or something thats happening in DynamoDB..
Basically, Im building a simple registration/login system for my project, saving the userdata/password in a DynamoDB instance with the password hashed using RIPEMD160, and salted as well using C#'s RNGCryptoServiceProvider().
Registration seems to work perfectly fine. the issue is upon login, no matter what, the passwords dont match up, and I think its because I'm getting some funky characters back when pulling the hash/salt back from DynamoDB. First off, both the hash and the salt are byte arrays of length 20, and converted to strings before saved in the database.
These examples are copy/pasted from the dynamo web interface
Example Hash: ">�Bb.ŧ�E���d��Ʀ"
Example Salt: "`���!�!�Hb�m�}e�"
When they're coming back and I debug into the function that pulls back the data from dynamo, both strings have different characters (VS2010 Debugger):
Returned Hash: "u001B>�Bb.ŧ�E��u0003�d�u001C�Ʀ"
Returned Salt: "`���!u000B�!�Hb�u001Dmu0012�u0001}e�"
Seems these u001B, u000B, u001D, u0012, u0003, u001C, and u0001 are sneaking into the returned data, and I'm not entirely sure whats going on?
You shouldn't be trying to convert opaque binary data into a string in this way in the first place. They're not text so don't treat them that way. You're just begging to lose information that way.
Use Convert.ToBase64String(data) instead of Encoding.GetString before putting the data into the database. When you get it out again, use Convert.FromBase64String to retrieve the original binary data.
Alternatively, don't store the data in a text field to start with - use a database field type which is meant to store binary data...

Migrate C# Hash Code to PHP

I know there are similar questions already on SO but none of them seem to address this problem. I have inherited the following c# code that has been used to create password hashes in a legacy .net app, for various reasons the C# implementation is now being migrated to php:
string input = "fred";
SHA256CryptoServiceProvider provider = new SHA256CryptoServiceProvider();
byte[] hashedValue = provider.ComputeHash(Encoding.ASCII.GetBytes(input));
string output = "";
string asciiString = ASCIIEncoding.ASCII.GetString(hashedValue);
foreach ( char c in asciiString ) {
int tmp = c;
output += String.Format("{0:x2}",
(uint)System.Convert.ToUInt32(tmp.ToString()));
}
return output;
My php code is very simple but for the same input "fred" doesn't produce the same result:
$output = hash('sha256', "fred");
I've traced the problem down to an encoding issue - if I change this line in the C# code:
string asciiString = ASCIIEncoding.ASCII.GetString(hashedValue);
to
string asciiString = ASCIIEncoding.UTF7.GetString(hashedValue);
Then the php and C# output match (it yields d0cfc2e5319b82cdc71a33873e826c93d7ee11363f8ac91c4fa3a2cfcd2286e5).
Since I'm not able to change the .net code I need to work out how to replicate the results in php.
Thanks in advance for any help,
I don’t know PHP well enough to answer your question; however, I must point out that your C# code is broken. Try generating the hash of these two inputs: "âèí" and "çñÿ". You will find that their hash collides:
3f3b221c6c6e3f71223f51695d456d52223f243f3f363949443f3f763b483615
The first bug lies in this operation:
Encoding.ASCII.GetBytes(input)
This assumes that all characters within your input are US-ASCII. Any non-ASCII characters would cause the encoder to fall back to the byte value for the ? character, thereby giving (unwanted) hash collisions, as demonstrated above. Notwithstanding, this will not be an issue if your input is constrained to only allow US-ASCII characters.
The other (more severe) bug lies in the following operation:
ASCIIEncoding.ASCII.GetString(hashedValue)
ASCII only defines mappings for values 0–127. Since the elements of your hashedValue byte array may contain any byte value (0–255), encoding them as ASCII would cause data to be lost whenever a value greater than 127 is encountered. This may lead to further “unwanted” (read: potentially maliciously generated) hash collisions, even when your original input was US-ASCII.
Given that, statistically, half of the bytes constituting your hashes would be greater than 127, then you are losing at least half the strength of your hash algorithm. If a hacker gains access to your stored hashes, it is quite likely that they will manage to devise an attack to generate hash collisions by exploiting this cryptographic weakness.
Edit: Notwithstanding the considerations mentioned in my posts and Jon’s, here is the PHP code that succumbs to the same weakness – so to speak – as your C# code, and thereby gives the same hash:
$output = hash('sha256', $input, true);
for ($i = 0; $i < strlen($output); $i++)
if ($output[$i] > chr(127))
$output[$i] = '?';
$output = bin2hex($output);
Could you use mb_convert_encoding (see http://php.net/manual/en/function.mb-convert-encoding.php - the page also has a link to a list of supported encodings) to convert the PHP string to ASCII from UTF7?
I've traced the problem down to an encoding issue
Yes. You're trying to treat arbitrary binary data as if it's valid text-encoded data. It's not. You should not be using any Encoding here.
If you want the results in hex, the simplest approach is to use BitConverter.ToString
string text = BitConverter.ToString(hashedValue).Replace("-", "").ToLower();
And yes, as pointed out elsewhere, you probably shouldn't be using ASCII to convert the text to binary at the start of the hashing process. I'd probably use UTF-8.
It's really important that you understand the problem here though, as otherwise you'll run into it in other places too. You should only use encodings such as ASCII, UTF-8 etc (on any platform) when you've genuinely got encoded text data. You shouldn't use them for images, the results of cryptography, the results of hashing, etc.
EDIT: Okay, you say you can't change the C# code... it's not clear whether that just means you've got legacy data, or whether you need to keep using the C# code regardless. You should absolutey not run this code for a second longer than you have to.
But in PHP, you may find you can get away with just replacing every byte with a value >= 0x80 in the hash with 0x3F, which is the ASCII for "question mark". If you look through your data you'll probably find there are a lot of 3F bytes in there.
If you can get this to work, I would strongly suggest that you migrate over to the true MD5 hash without losing information like this. Wherever you're storing the hashes, store two: the legacy one (which is all you have now) and the rehashed one. Whenever you're asked to validate that a password is correct, you should:
Check whether you have a "new" one; if so, only use that - ignore the legacy one.
If you only have a legacy one:
Hash the password in the broken way to check whether it's correct
If it is, hash it again properly and store the results in the "new" place.
Then when everyone's logged in correctly once, you'll be able to wipe out the legacy hashes.

Asp.net C# Encryption/Decryption on Client and server Side

Sir,
I have the jquery solution to encryption on the client side but it create "MD5" only.
I want Salted Md5 Encryption on the Clientside
and Decrypt it at the Server Side in Asp.net 4.0 and C#
My Code for encryption are as follows:
<script type="text/javascript">
function chn() {
var a = document.getElementById('txt1');
var b = document.getElementById('txt2');
var c = a.value; var d = $.md5(c);
b.value = (d);
}
</script>
I want that encryption must be change on every attempt..
Example : first time encryption of abc is xyz
and again if I will try with that name "Abc" then it should create another Encryption
and check on server Side.
Please Help me out
MD5 is a hash, not an encryption mechanism. Hashes are by their very nature lossy, and multiple inputs can (and by virtue of the pigeonhole principle absolutely will) produce the same outputs.
Running MD5 works like counting the number of vowels in a word. If I tell you that a word has 4 vowels in it, and ask you to tell me what the original word was, you simply don't have enough information to give me the the correct answer. You may be able to find some word that has 4 vowels in it, but you won't know whether the word you found was my word. Maybe it is, maybe it isn't. It's mathematically impossible for you to tell.
MD5 works the same way. You're throwing away tons of information, possible gigabytes or terabytes of information, and producing instead a single 16-byte summary.
It is, by intention, an inherently one-way process.
MD5 cant be decrypted. It is a one way hash. Beside I find that anything that could be decrypted on the other end is insecure, in the case it is intercepted. Always design and code to ensure that you can validate a salt and not decrypt it :)

Compress Guids by hashing in small data sets

I'm working on a mobile app and i want to optimise the data that it's receiving from the server (as JSON).
There are 3 lists returned (each containing its own class of objects, the approximate list sizes are 50, 100 and 170). Each object has a Guid id and there is some relation data for each object. E.g.:
o = { Id = "8f088552-5b24-4ba4-a6e5-8958c4353581",
RelatedIds = ["19d2e562-0874-473f-8e05-7052e8defd9a", "615b4c47-199a-4f7d-8268-08ed43d9c891", ... ] }
Is there a way to compress these Guids to something sorter without storing an identity map? Perhaps using a hash function?
You can convert the 16-byte representation of a GUID into a Base 64 string. However you didn't mention a programming language so we can't help further.
A hash function is not recommended here because hash functions are generally lossy.
No. One of the attributes of (non-cryptographic) hashes is that they collide: hash(a) == hash(b) but a != b. They are a performance optimization in the case where you are doing a lot of equality checks and you expect many false results (because if hash(a) != hash(b) then a != b). A GUID->counter map is probably the best way to get smaller ids here.
You can convert hex (base16) to base64, and remove all the punctuation. You should save 25% for using base64, and another 4 bytes for punctuation.
Thinking about it some more i've realized that HTTP compression (if enabled) is probably going to compress that data well enough anyway, so it's not really worth the effort to compress data manually.

Categories

Resources