Encrypt or hash a string to decimal numbers in Ruby - ruby

In one of our signup processes, in a Ruby on Rails app, I want to send someone an email with a 6-digit numerical code that they need to copy or type into a box on the page.
How I thought I might do it is to have a string on the page, in a hidden field, say, which, on generating the email, gets combined with some secret key from our codebase, then hashed or encrypted to a series of decimal numbers. I then take the last six of these and put them in the email.
When the form is submitted, with the original string, and the 6 digits the user has typed in, I can repeat the process on the string and test that it produces the same six digits as the user entered.
The question is: how do I hash/encrypt a string to get a series of decimal numbers?  The original string can be anything (including a different set of decimal numbers), it just needs to be something that is randomly generated really.

Note: Do not use this as a security measure. You've stated that you intend to use this only as a kind of diy captcha system, and this answer is provided in that context.
OpenSSL provides good hash functions. Once you have the hash in hex the to_i method takes a base argument, so converting to decimal is simple. Then back to a string because that makes it easier to get the last characters.
require 'openssl'
hash = OpenSSL::Digest::SHA256.hexdigest('user#example.com' + Random.new_seed.to_s)
message = hash.to_i(16).to_s.chars.last(6).join
You could then store any part of this along with an expiration time.

Related

Abbreviating a UUID

What would be a good way to abbreviate UUID for use in a button in a user interface when the id is all we know about the target?
GitHub seems to abbreviate commit ids by taking 7 characters from the beginning. For example b1310ce6bc3cc932ce5cdbe552712b5a3bdcb9e5 would appear in a button as b1310ce. While not perfect this shorter version is sufficient to look unique in the context where it is displayed. I'm looking for a similar solution that would work for UUIDs. I'm wondering is some part of the UUID is more random than another.
The most straight forward option would be splitting at dash and using the first part. The UUID 42e9992a-8324-471d-b7f3-109f6c7df99d would then be abbreviated as 42e9992a. All of the solutions I can come up with seem equally arbitrary. Perhaps there is some outside the box user interface design solution that I didn't think of.
Entropy of a UUID is highest in the first bits for UUID V1 and V2, and evenly distributed for V3, V4 and V5. So, the first N characters are no worse than any other N characters subset.
For N=8, i.e. the group before the first dash, the odds of there being a collision within a list you could reasonably display within a single GUI screen is vanishingly small.
The question is whether you want to show part of the UUID or only ensure that unique strings are presented as shorter unique strings. If you want to focus on the latter, which appears to be the goal you are suggesting in your opening paragraph:
(...) While not perfect this shorter version is sufficient to look unique in
the context where it is displayed. (...)
you can make use of hashing.
Hashing:
Hashing is the transformation of a string of characters into a usually
shorter fixed-length value or key that represents the original string.
Hashing is used to index and retrieve items in a database because it
is faster to find the item using the shorter hashed key than to find
it using the original value.
Hashing is very common and easy to use across many of popular languages; simple approach in Python:
import hashlib
import uuid
encoded_str = uuid.UUID('42e9992a-8324-471d-b7f3-109f6c7df99d').bytes
hash_uuid = hashlib.sha1(encoded_str).hexdigest()
hash_uuid[:10]
'b6e2a1c885'
Expectedly, a small change in string will result in a different string correctly showing uniqueness.
# Second digit is replaced with 3, rest of the string remains untouched
encoded_str_two = uuid.UUID('43e9992a-8324-471d-b7f3-109f6c7df99d').bytes
hash_uuid_two = hashlib.sha1(encoded_str_two).hexdigest()
hash_uuid_two[:10]
'406ec3f5ae'
After thinking about this for a while I realised that the short git commit hash is used as part of command line commands. Since this requirement does not exist for UUIDs and graphical user interfaces I simply decided to use ellipsis for the abbreviation. Like so 42e9992...

Encode array to fixed length string

I'm trying to implement a similar function to PCPartPicker's list permalink function.
https://au.pcpartpicker.com/list/
basically generate a permalink based on the items in the list. The key part is to generate a string which should be:
unique
persistent
fixed length
I'm thinking about encoding an array contains product id, but can't find the right way to implement it.
Base64 and the similar (like Hashids library) can ensure it's unique and persistent, but it ends up quite long when the array has many items.
Is there other way to encode the array or is there other direction I can implement this function?
Thank you in advance.
One can't generate unique fixed length string for arbitrary length list that will contain all info - there is always some length that can't fit.
Since your site has database, you can generate UUID and store list in DB along with UUID. To save space and efforts you can save it into DB only when user presses "get permalink" button or something like that.

How to avoid exception by mapping string to number in thymeleaf?

I have some HTML page and on this page I will provide the possibility for free text.
For example, it is possible to write in textbox either: 10 or 10 apples.
In a case of writing 10 apples I got NumberFormatException which is correct, but for me will be good to extract only number automatically without javascript writing.
Is it possible to map string from HTML page to the number in my java entity? May be with some annotation or somehow else?
Try:
final String stripped = textbox.getText().replaceAll("[^0-9]", "");
This takes the contents of the text field and strips out any characters that aren't digits. If you need to deal with floating point or negative numbers, it can be done, but becomes more complicated.

CI encrypt and decrypt

I get that the encryption class produces different output each time the same word/string is encrypted, for example, $this->encrypt->encode("word") ran five times would produce five different encrypted strings.
How can I reference the encrypted string in a DB query if each time I call $this->encrypt->encode("word") gives me something different?
Asked in a different way, is there something I can encrypt with that doesn't have a random value so that each time I encrypt I get the same output for the same input?
Base64 encoding is not encryption (referring to your own answer). I have not used codeigniter, but I notice on its doc pages that it allows:
$this->encrypt->set_mode();
You could encrypt with ECB mode (MCRYPT_MODE_ECB) for deterministic encryption where the same data always encrypts to the same ciphertext. This way, you encrypt your search string, and it will match the encrypted data in the database.
This is considered a weakness of ECB mode, but in this case, the deterministic behavior may be what you want.
I think base64_encode($str) is what I'm looking for.
this code works only on php 5.5 or higher
echo password_hash(variable, PASSWORD_DEFAULT);
The first parameter is the password string that needs to be hashed and the second parameter specifies the algorithm that should be used for generating the hash.
The default algorithm is currently bcrypt, but a stronger algorithm may be added as the default later at some point in the future and may generate a larger string. If you are using PASSWORD_DEFAULT in your projects, be sure to store the hash in a column that’s capacity is beyond 60 characters. Setting the column size to 255 might be a good choice. You could also use PASSWORD_BCRYPT as the second parameter. In this case the result will always be 60 characters long.
and to check the password hash here is the syntax
<?php
if (password_verify($oldpassword, $hash)) {
// Success!
// the first parameter is your password that's not yet encrypted, the secode is your password encrypted
}
else {
// Invalid password
}

Local Currency String conversion

I am maintaining an app for a client that is used in two locations. One in England and one in Poland.
The database is stored in England and uses the format £1000.00 for currency, but the information is being gathered locally in Poland where 1000,00 is the format.
My question is, in VB6 is there a function that takes a currency string in a local format and converts to another, or will I just have to parse the string and replace , or . ?
BTW I have looked at CCur, but not sure if that will do what I want.
The data is not actually stored as the string "£1000.00"; it's stored in some numeric format.
Sidebar: Usually databases are set up to store money amounts using either the decimal data type (also called money in some DBs), or as a floating point number (also called double).
The difference is that when it's stored as decimal certain numbers like 0.01 are represented exactly whereas in double those numbers can only be stored approximately, causing rounding errors.
The database appears to be storing the number as "£1000.00" because something is formatting it for display. In VB6, there's a function FormatCurrency which would take a number like 1000 and return a string like "£1000.00".
You'll notice that the FormatCurrency function does not take an argument specifying what type of currency to use. That's because it, along with all the other locale-specific functions in VB, figures out the currency from the current locale of the system (from the Windows Control Panel).
That means that on my system,
Debug.Print FormatCurrency(1000)
will print $1,000.00, but if I run that same program on a Windows computer set to the UK locale, it will probably print £1,000.00, which, of course, is something completely different.
Similarly, you've got some code, somewhere, I can't tell where, in Poland, it seems, that is responsible for parsing the user's string and converting it to a number. And if that code is in Visual Basic, again, it's relying on the control panel to decide whether "." or "," is the thousands separator and whether "," or "." is the decimal point.
The function CDbl converts its argument to a number. So for example on my system in the US
Debug.Print CDbl("1.200")
produces the number one point two, on a system with the Control Panel set to European formatting, it would produce the number one thousand, two hundred.
It's possible that the problem is that you have someone sitting a computer with the regional control panel set to use "." as the decimal separator, but they're typing "," as the decimal separator.
What database are you using? And what data type is the amount stored in?
As long as you are always converting from one format to another, you do not need to do any parsing, just replace "." with "," or the other way around. You may need to remove the "£"-sign as well if that is stored in your string.
There's probably a correct answer dealing with culture objects and such, but the easiest way would be to taken the input from the polish input, and replace the , with a ., and then store it in your database as type "money" or "decimal". If you know they (possibly configurable per user) are always entering numbers in either Polish or English, you could have a function that you run all the input numbers through to convert the string to a proper "decimal" typed variable. Also, for display purposes you could run it through another similar function to ensure that the user always sees the number format they are comfortable with. The key here is to switch it to a decimal as soon as you get it from the user, and only switch it back to a string at the last step before sending it out to the user.
#KiwiBastard yes i would think so. Are you storing your amount in an "(n)varchar" field or are you using a currency/decimal type field? If the latter is the case, the currency-symbols and separators are added by your client, and there would be no need to replace anything in the database.

Resources