Skip to content

Instantly share code, notes, and snippets.

@mclosson
Last active March 23, 2017 05:42
Show Gist options
  • Save mclosson/95ae211ff0212066c5ff458969273e6a to your computer and use it in GitHub Desktop.
Save mclosson/95ae211ff0212066c5ff458969273e6a to your computer and use it in GitHub Desktop.
// This started out as an exploration on decoding Rails session cookies in Rust.
// After reaching the "it works" stage I found:
//
// https://github.com/mikeycgto/message_verifier/ by @mikeycgto
// and you should really use that as its a much more elegant solution
// and also implements verifying the message as well as decrypting it
// which would be critical in actual use.
//
// https://gist.github.com/profh/e36e5dd0bec124fef04c
// https://gist.github.com/JoshCheek/7b1c1eb231dfa83098be
// https://matt.aimonetti.net/posts/2013/11/30/sharing-rails-sessions-with-non-ruby-apps/
// http://big-elephants.com/2014-01/handling-rails-4-sessions-with-go/
// https://techbrahmana.blogspot.com/2012/03/rails-cookie-handling-serialization-and.html
extern crate crypto;
extern crate data_encoding;
extern crate rand;
use crypto::{buffer, aes, blockmodes};
use crypto::buffer::{ReadBuffer, WriteBuffer};
use crypto::pbkdf2::pbkdf2;
use crypto::hmac::Hmac;
use crypto::sha1::Sha1;
use data_encoding::base64;
use std::str;
fn decrypt(session_cookie: &str, secret_key_base: &str) -> String {
let salt = "encrypted cookie"; // String literal used in Rails 4.x, 5.x as the salt
let cookie = base64::decode(session_cookie.split("--").nth(0).unwrap().as_bytes()).unwrap();
let cookie = String::from_utf8(cookie).unwrap();
let v: Vec<_> = cookie.split("--").map(|s| s.as_bytes()).collect();
let encrypted_data = base64::decode(v[0]).unwrap();
let iv = base64::decode(v[1]).unwrap();
let mut mac = Hmac::new(Sha1::new(), secret_key_base.as_bytes());
let mut key: Vec<u8> = std::iter::repeat(0).take(64).collect();
pbkdf2(&mut mac, salt.as_bytes(), 1000, &mut key);
let mut decryptor = aes::cbc_decryptor(
aes::KeySize::KeySize256,
&key[..],
&iv[..],
blockmodes::PkcsPadding);
let mut final_result = Vec::<u8>::new();
let mut read_buffer = buffer::RefReadBuffer::new(&encrypted_data[..]);
let mut buffer = [0; 4096];
{
let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
decryptor.decrypt(&mut read_buffer, &mut write_buffer, true).unwrap();
final_result.extend(write_buffer.take_read_buffer().take_remaining());
}
let mut plain_text = buffer.to_vec();
let terminator = plain_text.iter().position(|&n| n == 0).unwrap();
plain_text.truncate(terminator);
String::from_utf8(plain_text).unwrap()
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let session_cookie = "KzlOUi9lK01vL0Z1Sk1vUUw2QksxM0JpT2xTdm1xMkxoaExMWFZUQWJBUUl0UEgvN1NYUGNtdVVadGZ1REM3YkNCQW56WDhEUDN5WG1KWjdRSjE4d3hoTEJvcHlLSzgwQTdWT1lFMmF3WkpvY0RJV1kveHF1eURSTlgrUkE3N3Q4S2JlY1gwcXdycTArdno3empleS93bC9XODRWMUJCZm1MOUh5dlZVMjJNSjZKc3cwNGFLaXRsVHlCaUpwUW4rN3pZYWlVQ2l3UmVqR2g4aVZLcFl6NUczMkluV0t1cE9CODJKL2RSYVhOZnJsZGhOanEyQ1JGVjNPVlkvQW5MQ3BKTjFjbGNHLzdoaDdPaFRySnFEa1E9PS0teVlEL0lZQ3Y1a1dnZnBnREd1S1lrdz09--ef5736349befb8afcab96954144e9188521326f1";
let secret_key_base = "9b068b0ffb899625a4e11fd75180907439961e3cba7da709d060826de1ab6ae674dde93d2cf62106e5d22a667f9173ace8331f22b1539503ef8d79fc1fe3c5ef";
let expected = "{\"session_id\":\"278407bb306a0f61b7490afae89da92f\",\"_csrf_token\":\"XantipYbR5itu1Gw5mLvo/Q2QVL3pXlLBbI6vnI33I0=\",\"user_id\":3,\"flash\":{\"discard\":[\"notice\"],\"flashes\":{\"notice\":\"Sign in successful\"}}}";
let plain_text = ::decrypt(&session_cookie, &secret_key_base);
assert_eq!(expected, plain_text);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment