RoR Encryption and Decryption for query string - ruby-on-rails

I've been searching for the past few days about encrypting some data to use on the URL.
I basically have 3 strings and i need to encrypt then on a unique token.
eg: code: '12345678' email: 'teste#encrypt.com' name: 'nameTest'
This will be join together as code%email%name and i need to encrypt them.
What's the best way to do it as i need to pass the encrypted string on the URL for the other server to decrypt? The algorithms i've used all put some form of '/\=' and i guess that probably may cause problems
Thanks for the help

In 2019, URI.encode is obsolete and should not be used
If you want safely put encrypted string into url without problems with special characters then you could use CGI.escapeor ERB::Util.url_encode for this purpose.
require 'erb'
ERB::Util.url_encode("Hello world")
#=> "Hello%20world"
Rails will decode automatically when receiving
If you want something simple that encodes and hide raw data which could be decoded somewhere later and verified then you could use MessageVerifier provided by active support:
#verifier = ActiveSupport::MessageVerifier.new('s3Krit')
cookies[:remember_me] = #verifier.generate([#user.id, 2.weeks.from_now])
id, time = #verifier.verify(cookies[:remember_me])
http://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html
http://ngauthier.com/2013/01/rails-unsubscribe-with-active-support-message-verifier.html
If you want true encryption then you could look into such project. It uses OpenSSL:
http://rocketjob.github.io/symmetric-encryption/

Related

Not sure how to generate an ECDSA signature, given a private key and a message

I'm following Apple's guide towards composing a CloudKit Web Services request. The bit I'm having trouble with is Step 2, under "Authenticate Web Service Requests":
Compute the ECDSA signature of this message with your private key.
Before getting to this point, I generated my certificate, a .pem file, which when opening it in a text editor shows me my private key, so I have that in string format too.
I've also followed the steps for generating what it refers to as a message, which I now have as a string.
So given that I have a private key, (or the .pem file if required), and a message as a string, it should in theory be fairly simple for me to get a ECDSA signature of the message, computed with my private key. But here's where I'm struggling. Libraries that I've found online seem to take a far more complicated approach, with different moving parts, no reference to a .pem file and talk of generating new public/private keys.
Any help with this step would be greatly appreciated.
It appears that the documentation and the actual API for Ruby’s OpenSSL EC support are both currently rather lacking. In particular, in Ruby <= 2.3.1 the OpenSSL::PKey::EC doesn’t follow the same API as RSA and DSA keys for signing and verifying. What you would want to do, but currently can’t with EC keys, is this (all the code here assumes you have called require 'openssl' somewhere):
# Get the key, here I'm reading the file
priv_key = OpenSSL::PKey.read(File.read('eckey.pem'))
# This should be the appropriately formatted string
data = "some data to sign"
# The hash algorithm, I assume SHA256 is being used
digest = OpenSSL::Digest::SHA256.new
# This doesn't work in 2.3.1, but does in 2.4.0-preview1
signature = priv_key.sign(digest, data)
As I note in the comments, this does work in Ruby 2.4.0-preview1, but that’s likely not much use to you.
To get it working with current Ruby, you need to do something like this:
# As before:
priv_key = OpenSSL::PKey.read(File.read('eckey.pem'))
data = "some data to sign"
signature = priv_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(data))
Both these techniques give you a binary string. I think you will need to base64 encode it before adding it as your request header.
To extract the public key to check the signature verifies is also a bit tricky (although you could just use the openssl command line and read in the file). The public_key methods returns an OpenSSL::PKey::EC::Point object rather than an actual key, so we need to recreate one from the private key. The verify method does work on Ruby 2.3.1:
pub = OpenSSL::PKey::EC.new(priv_key.group)
pub.public_key = priv_key.public_key
data = "some data to sign"
digest = OpenSSL::Digest::SHA256.new
puts pub.verify(digest, sig, data)
The Apple page doesn’t appear to specify the hash algorithm to use, but from what I’ve seen it looks like SHA-256 is right. (Also I could have got this completely wrong and Apple are using a completely different format. I’d be keen to know whether or not this code works you you).

Decrypt AES-256-CBC String (need IV, string/data format?)

I've been going around in circles from Apple's CCCrypto docs, frameworks and other SO answers and am not making any headway.
I think I need to figure out how to get a IV from an encrypted string that I receive.
I receive a JSON payload which contains a String. That string is encrypted in AES-256-CBC. (From a Laravel PHP instance that I think uses OpenSSL). The string itself, decrypted, is another JSON object.
I have a pre-defined key.
The string I receive looks something like:
eJahdkawWKajashwlkwAkajsne8ehAhdhsiwkdkdhwNIEhHEheLlwhwlLLLLhshnNWhwhabwiIWHWHwh=
(but is a lot longer).
I'm trying to use this answer here: Issue using CCCrypt (CommonCrypt) in Swift
But am a) unsure if I'm properly converting the string to data and b) how to get the IV (initialization vector) from the string I receive.
Using that answer I do get "success" however when I try to pass it to the NSJSONSerailizer I never got a good result (it always fails) but I do get data out - I think it's garbage.
Edit:
I really mis-understood my original problem - I was receiving a base64 encoded string that I needed to decode into JSON (which went fine). Then using the linked answer and importing CommonCrypto I thought I'd be able to get usable data but I am not. #Rob Napier 's answer is extremely helpful. I think my problem is that the instance of laravel in question is using OpenSSL.
There is no really commonly used standard format for AES encrypted data (there are several "standard formats" but they're not commonly used....) The only way to know how the data you have is encrypted is to look at the documentation for the data format, or failing that, the encrypting code itself.
In good encryption formats, the IV is sent along with the data. But in many common (insecure) formats, there is a hard-coded IV (sometimes 16 bytes of 0x00). If there's a password, you also need to find out how they've converted the password to a key (there are several ways to do this, some good, some horrible). In a good format, the key derivation may include some random "salt" that you need to extract from the data. You'll also need to know if there is an HMAC or similar authentication (which might be stored at the beginning or the end of the data, and may include its own salt).
There just isn't any good way to know without documentation from the sender. Any decently encrypted format is going to look like random noise, so figuring it out just by looking at the final message is pretty hard.
If this comes out of Laravel's encrypt function, then that seems to be ultimately this code:
public function encrypt($value)
{
$iv = mcrypt_create_iv($this->getIvSize(), $this->getRandomizer());
$value = base64_encode($this->padAndMcrypt($value, $iv));
// Once we have the encrypted value we will go ahead base64_encode the input
// vector and create the MAC for the encrypted value so we can verify its
// authenticity. Then, we'll JSON encode the data in a "payload" array.
$mac = $this->hash($iv = base64_encode($iv), $value);
return base64_encode(json_encode(compact('iv', 'value', 'mac')));
}
If this is correct, then you should have been passed base64-encoded JSON with three fields: the IV (iv), the ciphertext (value), and what looks like an HMAC encrypted using the same key as the plaintext (mac). The data you've given above doesn't look like JSON at all (even after base-64 decoding).
This assumes that the caller used this encrypt function, though. There are many, many ways to encrypt, though, so you need to know how the actual server you're talking to did it.

Is it possible to run a string injection attack on a redis query?

I'm building a little token-based authentication library for my (rails based) api server which uses redis to store generated auth tokens. The line I'm worried about is: user_id = $redis.get("auth:#{token}"), where token is what's passed in to authenticate_or_request_with_http_token.
If this were SQL, that'd be a huge red flag - string interpolated SQL queries are pretty insecure. As far as I can tell, however, doing string interpolation on a redis key query isn't insecure.
My source for the above claim is the redis documentation here: http://redis.io/topics/security (under the string escaping and nosql injection header), but I wanted to make sure that this is the case before I get a Bobby Tables attack.
The documentation you are pointing to is quite explicit:
The Redis protocol has no concept of string escaping, so injection is impossible under normal circumstances using a normal client library. The protocol uses prefixed-length strings and is completely binary safe.
There is a small attack vector for these kinds of string injections. While the redis documentation is clear about the difficulty of executing multiple commands on the database, it does not mention that the key separator (':' in your example) usually needs to be escaped when used as the part of a key.
I have seen a redis database using these keys:
oauth_token:123456 (which contained a hash of OAuth token parameters) and
oauth_token:123456:is_temp (which contained a boolean property to indicate whether the OAuth token is a temporary token)
Trusting the user input without escaping might result in GET oauth_token:#{token} accidentally ending up as GET oauth_token:123456:is_temp (when token has been set to 123456:is_temp by the user).
So I highly recommend to properly escape colons from potential user input to make sure your key paths cannot be tricked like this.
NOTE: Someone recommended to fix the example above by using oauth_token:123456 and oauth_token:is_temp:123456, but that is flawed (for the user-provided token is_temp:123456). The correct solution to that problem (without escaping) would be to use keys oauth_token:info:123456 and oauth_token:is_temp:123456 to make sure these keys cannot overlap whatever the user-provided input was (or simply escape colons).
Basically Redis is immune from escaping issues when the input string is used verbatim. For example:
SET mykey <some-attacker-chosen-data>
However Redis is not immune from issues arising by using non validate input in the context of string interpolation, as showed by Sven Herzberg. In order to turn the Sven example into a safe one, it is possible to just use an Hash, and avoid reverting to interpolation. Otherwise either use not common prefixes to use in conjunction with keys interpolation, or use some basic form of sanity check on the input, which is, filtering away the separator used, or better, validate that the input is actually a number (in the specific example).
So while Redis does not suffer from the typical injection attacks of SQL, when used untrusted input in the context of a string interpolation used to create key names, or even worse, Lua scripts, some care should be taken.

how to store hashed passwords generated from erlang

I used crypto:sha/1 to hash passwords in my erlang application.
Should I store the binary obtained directly in mnesia or should i convert them to hex string before?
Using crypto:sha/1 for hashing passwords is dangerous. At least have a salt, but preferably, use say scrypt, bcrypt or pbkdf2 for storing passwords like this. They are resilient to a number of attacks. Unfortunately, I know of no Erlang-support for those :/
Use https://github.com/smarkets/erlang-bcrypt to do the hashing rather than SHA1 or MD5.
One could get an Hmac SHA256 hex Digest or MD5 Digest of a password from a front-end application, create a hash using the erlang method and then store this hash. For example, if i had a web application, i ask for password from users, right at account creation or at login, i use javaScript to create an MD5 Digest of this password and send that along the wire (HTTPS) instead of the actual password. On reaching Erlang, i create a hash of this MD5 Digest from JavaScript and store that as the users password. So each time the user attempts to login on my page, i would do the similar process and then compare the hash output of his entry with the one that was stored. Read more on SHA256 HMac Digest by looking at the solutions to this question: HMAC SHA256 hex digest of a string in Erlang, how? and this one: Erlang and JavaScript MD5 Digest match
Actually you store tuples (or records, which is the same) in mnesia, but in the fields of that records you can store any term (including binaries). It's not neccessary to convert them to strings.

SHA256 implementation using Base64 for input and output

I've been asked to develop the company's backoffice for the iPad and, while developing the login screen, I've ran into an issue with the authentication process.
The passwords are concatenated with a salt, hashed using SHA-256 and stored in the database.
The backoffice is Flash-based and uses the as3crypto library to hash then password+salt and my problem is that the current implementation uses Base64 for both input and output.
This site demonstrates how this can be done: just select Hash and select Base64 for both input and output format and fire away. So far, all my attempts have yielded different results from the ones this site (and the backoffice code) give me.
While I think that in theory it should be relatively simply:
Base64 encode the pass+salt
Hash it using SHA-256
Base64 encode the result again
so far I haven't been able to do this and I'm getting quite the headache to be honest.
My code is becoming a living maze, i'll have to redo-it tomorrow I reckon.
Any ideas?
Cheers and thanks in advance
PS: Here's the Backoffice's Flash code for generating hashed passwords by the way:
var currentResult:ByteArray;
var hash:IHash = Crypto.getHash('sha256');
var data:ByteArray = Base64.decodeToByteArray(str + vatel);
currentResult = hash.hash(data);
return Base64.encodeByteArray(currentResult).toString();
The backoffice code does not do
Base64 encode the pass+salt
Hash it using SHA-256
Base64 encode the result again
(as you wrote above)
Instead, what it does is
Base64 decode the pass+salt string into a byte array
Hash the byte array using SHA-256
Base64 encode the byte array, returning a string
As per step 1 above, it's a unclear what kind of character encoding the input strings uses. You need to make sure that both systems use the same encoding for the input strings! UTF8, UTF16-LE or UTF16-BE makes a world of a difference in this case!
Start by finding out the correct character encoding to use on the iOS side.
Oh, and Matt Gallagher has written an easy to use wrapper class for hashes to use on iOS, HashValue.m, I've used it with good results.

Resources