From 7c7bd7272ccf3514a0b2b9626d8e3cddb5246902 Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Thu, 5 Nov 2015 10:33:34 -0500 Subject: [PATCH 1/8] update no TLDR docs to include bundler stuff --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8fa842a..45859b9 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ $ jekyll -v Then, start the Jekyll Server. I always like to give the `--watch` option so it updates the generated HTML when I make changes. ``` +$ gem install bundler +$ bundle $ bundle exec jekyll serve --watch --drafts ``` From 5d1927e9e091a7947a0d36b11171e4da85b235b8 Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Thu, 5 Nov 2015 11:00:22 -0500 Subject: [PATCH 2/8] rm draft that everyone hated :-( --- ...ys-to-create-bulletproof-software.markdown | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 _drafts/5-ways-to-create-bulletproof-software.markdown diff --git a/_drafts/5-ways-to-create-bulletproof-software.markdown b/_drafts/5-ways-to-create-bulletproof-software.markdown deleted file mode 100644 index 11ffcbc..0000000 --- a/_drafts/5-ways-to-create-bulletproof-software.markdown +++ /dev/null @@ -1,25 +0,0 @@ ---- -layout: post -title: 5 Ways to Create Bulletproof Software -author: Paul Franzen -summary: -image: http://res.cloudinary.com/wework/image/upload/s--Al86WVSb--/c_fill,fl_progressive,g_face:center,h_1000,q_jpegmini:1,w_1600/v1427921778/engineering/5-ways-to-create-bulletproof-software-bear.jpg -categories: process ---- - -When beginning a new software development endeavor, there is no shortage of information available on processes and procedures to ensure success. I can tell you right here and now in front of God and Tom Cruise that most of these will lead to failure, hardship and eventually suicide. If you’re looking for the capital T truth in software engineering processes follow these 5 easy rules and you’ll be well on your way to creating the next “uber-for-prostitutes”. - -**One.** _You don’t need an in-house engineering team._
-Whiny. Overpaid. Insubordinate and churlish. These three words describe your typical, good-for-nothing software engineer. If you don’t want to spend all your time arguing over things like “quality user experience” and “maintainability” go ahead and save yourself a huge headache and find an overseas development firm, you’ll always pay less and you’ll end up with exactly the product you ask for. Even better, consider using unpaid interns and students from technical programs. It’s a fact, all programming knowledge is gained from googling things then copying and pasting. Remember, if your developer has spent more than 6 weeks honing their craft, they obviously don’t know how to prioritize. One geek is just as good as another. - -**Two.** _Start with a well-defined, long term plan_
-Ensuring success is all about taking your time. Really think through your product. If you don’t exactly know you’re going to process credit cards from Australia or whether or not you’re going to offer coupon codes, you better figure it out, buddy! I can’t tell you how many projects I’ve worked on where everything was going along perfectly for the first 60 days, great user feedback, amazing profitability only to be DERAILED COMPLETELY by the fact that we did not account for something as simple as daylight saving times in reservations in Israel. Boy was my face red and the investors pulled the plug! Don’t end up like this. Write a plan. Include detailed specs. The more the better. Remember it's your vision. - -**Three.** _Design. Brand. Enough Said._
-Why do you think Steve Jobs was so successful? He cared more about design and branding than anything else, that's why. That's the only thing separating the good products from the bad ones. Just like with a long-term plan. You really want to think every piece of design through. Make beautiful comps. Decide on animations early and often. Remember your users really only care about one thing: how your product feels. Breaking conventions is a great way to make your product feel unique. If you don’t think the logo should go in the top left and take you to the homepage, don’t put it there. The more you think about a design, the more beautiful and simple it will be. - -**Four.** _The Logo_
-Everyone knows, great ideas and products have great logos. This is really a no-brainer. - -**Five.** _Be persistent._
-If an idea is not working. That’s OK, it just needs more time. Consider redesigning the experience. You would be amazed what a simple change in button color, or typeface can accomplish. From 8e7ff10052a9456699a1d6ce9989d2ce2ea9b6cd Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Thu, 5 Nov 2015 20:55:33 -0500 Subject: [PATCH 3/8] first half of post --- ...015-11-06-simply-secure-rails-api.markdown | 378 ++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 _posts/2015-11-06-simply-secure-rails-api.markdown diff --git a/_posts/2015-11-06-simply-secure-rails-api.markdown b/_posts/2015-11-06-simply-secure-rails-api.markdown new file mode 100644 index 0000000..1c05693 --- /dev/null +++ b/_posts/2015-11-06-simply-secure-rails-api.markdown @@ -0,0 +1,378 @@ +--- +layout: post +title: Simply Secure Rails API +author: Paul Franzen +summary: +image: http://res.cloudinary.com/wework/image/upload/s--Al86WVSb--/c_fill,fl_progressive,g_face:center,h_1000,q_jpegmini:1,w_1600/v1427921778/engineering/5-ways-to-create-bulletproof-software-bear.jpg +categories: rails, api, security +--- + +If you're attempting to implement a service orientated architecture +one of the most daunting tasks can be deciding on a method for +securing API endpoints. + +A simple (and obvious) solution is to make every request using an +API key or something like [Basic HTTP Authentication headers](https://en.wikipedia.org/wiki/Basic_access_authentication). +Something like: + +Requesting APP (using the wonderful Faraday): + +```ruby + Faraday.new(:url => "https://www.api.io/api/v1/posts") do |faraday| + faraday.adapter Faraday.default_adapter + faraday.use Faraday::Request::TokenAuthentication, + faraday.headers['Content-Type'] = 'application/json' + faraday.token_auth("1234567cy") + end +``` + +Receiving APP: + +```ruby + module Api + module V1 + class PostsController < ApplicationController + before_filter :restrict_access + + def index + @posts = Post.page(1) + respond_with @posts + end + + private + + def restrict_access + authenticate_or_request_with_http_token do |token, options| + ApiKey.exists?(access_token: token) + end + end + + end + end + end +``` + +This is a fine system, pleasingly simple and straight-forward. +However, it misses one basic fact about these requests: Most of them +are made on behalf of the current user and not the requesting +application. Enter OAuth. + +OAuth especially via [DoorKeeper](https://github.com/doorkeeper-gem/doorkeeper) +and [Omniauth](https://github.com/intridea/omniauth), is great, +but opening new windows, redirect URIs, tokens, and the funky +user experience that often accompany them can be a bit too much +ceremony, particularly if you're connecting two trusted applications, +often times sharing the same base domain. + +#### Tokens to the rescue! + +Encrypting strings that can be later decrypted (usually obfuscated to a word: Token) +is a concept oft hidden Wizard-Of-Oz style curtains within frameworks like Rails. + +*Initialization Vectors*, *Ciphers*, fancy words and bad hacker nicknames, inspiring both dread and awe in the uninitiated. +But just like any good magic trick deciphering the slight of hand is both empowering and inspiring. + +#### Scenario + +##### Application 1: +Our Authentication service (https://id.wework.io) +In a more traditional architecture, this would be the OAuth provider +This service has a login API. It uses [Devise](https://github.com/plataformatec/devise) +to manage authentication. This application also utilizes CORS via [Rack CORs](https://github.com/cyu/rack-cors). +Allowing the appropriate cross-origin access. + +##### It has a user model + +```ruby +class User < ActiveRecord::Base + # Schema + # Name: string + # UUID: UUID (primary key) + # Email + # Devise password stuff + devise :database_authenticatable +end +``` + +##### It has a base API controller + +```ruby +module Api + class ApiController < ApplicationController + skip_before_action :verify_authenticity_token + end +end +``` + +##### It has a login controller endpoint: + +```ruby +module Api + module V1 + class LoginController < ApiController + + def create # POST: /api/v1/login + @user = User.find_by(email: params[:email]) + + if @user.present? && @user.valid_password?(params[:password]) + respond_with: @user.as_json() + else + head 404 + end + end + + end + end +end +``` + +##### It has a users controller + +```ruby +module Api + module V1 + class UsersController < ApiController + + def show # GET: /api/v1/users/[UUID] + @user = User.find_by(uuid: params[:id]) + + if @user.present? + respond_with: @user.as_json() + else + head 404 + end + end + + end + end +end +``` + +--- + +##### Application 2: +Social Network (https://network.wework.io) +A rails web app which uses the authentication service to log a user in and seed user data. +It stores: users (like most OAuth apps), and posts. + +##### Login Screen + +```html + + + Network + + + +
+ + + +
+ + +``` + +So, how do we get from these simple endpoints and actions, to a functioning ecosystem? +I thought you'd never ask! + +First you want to add a method to the authentication service's User Model. This method encrypts a +user's uuid or any column that's unique (or a combination of columns for extra security). + +Using ruby's OpenSSL library + +```ruby +class User < ActiveRecord::Base + # Schema + # Name: string + # UUID: UUID (primary key) + # Email + # Devise password stuff + devise :database_authenticatable + + def encrypted + cipher = OpenSSL::Cipher.new('aes-256-cbc') + cipher.encrypt + cipher.key = ENV['SOME_SECRET_KEY'] + cipher.iv = ENV['SOME_SECRET_IV'] + cipher.padding = 1 + encrypted = cipher.update(self.uuid) + cipher.final # use only this user's UUID for encryption + Base64.urlsafe_encode64(encrypted).encode('utf-8') + end +end +``` + +Anyone know what this method does? Anyone? Anyone? + +That's OK!! + +It's pretty much way outside the norm of "ordinary" MVC web development. So lets go through it line by line. + + +#### Line 1 +```ruby +cipher = OpenSSL::Cipher.new('aes-256-cbc') +``` + +The argument 'aes-256-cbc' is a hyphenated description of components of the cipher. + +1. "aes" is the name of the encryption algorithm. In this case, its an acronym for [Advanced Encryption Standard](http://aesencryption.net/). + +2. 256 is the number of bits of the key used in the algorithm. You'll usually see 128 or 256. Practically, this just means that the key is "large" enough that it cannot be easily broken via a brute force attack. + +3. cbc stands for "cipher block chaining" and is the mode by which the aes algorithm encrypts the data. Its most commonly compared the ecb (electronic code book)[http://searchsecurity.techtarget.com/definition/Electronic-Code-Book]. +Its generally preferred to use cbc for encoding smaller things, because ebc may expose pieces of the real string in the encrypted string. + +Full documentation is available [here](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html). + +--- + +#### Line 2 +```ruby +cipher.encrypt +``` + +This lets the cipher instance we defined before know that we intend to use it for encryption rather than decryption. + +--- + +#### Line 3 +```ruby +cipher.key = ENV['SOME_SECRET_KEY'] +``` + +OK, here's where it starts to get interesting. +This is basically the first part of the password that will shared between applications. +Its important to create a truly random (or as close to that as possible) key, so consult the [documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#class-OpenSSL::Cipher-label-Choosing+a+key) + +--- + +#### Line 4 +```ruby +cipher.iv = ENV['SOME_SECRET_IV'] +``` + +The iv here, well this stands for initialization vector. An initialization vector creates an extra layer of security around a key, and is generally used only once per instance of a cipher. +We're breaking the rules a little here, because our IV will be used only to prevent the accidental exposure of the key in the encrypted payload. +Consult the [documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#class-OpenSSL::Cipher-label-Choosing+an+IV) to choose your IV. + +--- + +For both of these, as implied by the code, you'll want to store them outside of version control and in some sort of easily changeable way (such as an environment variable) in case the are ever compromised. + +You'll also want to use different values for each environment, i.e. development, staging, production. + +--- + +#### Line 5 +```ruby +cipher.padding = 1 +``` + +This sets the cipher padding to 1. Just kidding, that would make too much sense! This ENABLES padding in the cipher. The short story is that the aes +block cipher algorithm mentioned above requires its input to be and EXACT multiple of the block size. Read the full [documentation](http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-padding-3D) + +--- + +#### Line 6 +```ruby +encrypted = cipher.update(self.uuid) + cipher.final +``` +For practical purposes, this is a procedural event in the cipher's life-cycle. It returns the, you guessed it, final remaining data in the cipher object. +Or in more sane terms, something that's ready to be deciphered. [Documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#method-i-final) + +--- + +#### Line 7 +```ruby +Base64.urlsafe_encode64(encrypted).encode('utf-8') +``` +This is the actual return we want to transform the finalized cipher into so that it can be passed safely via a query string or form data. Only over SSL, of course :-) + +--- + +### OK, lets put this to use. + +Now that our user model has an encrypted method we can modify the authentication app's + +##### Users controller + +```ruby +module Api + module V1 + class LoginController < ApiController + + def create # POST: /api/v1/login + @user = User.find_by(email: params[:email]) + + if @user.present? && @user.valid_password?(params[:password]) + respond_with: @user.as_json(methods: [:encrypted]) + else + head 404 + end + end + + end + end +end +``` + +Now that our authentication application is prepped to return encrypted uuids when provided a valid email and password we can alter our + +##### Client application login screen + +```html + + + Network + + + +
+ + + +
+ + + +``` + +Using *GASP* jQuery we are able to post to our authentication app (with properly configured CORS) and +get a response that looks something like this: + +```json +{ + user:{ + encrypted: SOME_IMPOSSIBLE_TO_DECIPHER_STRING, + name: "Jane Doe", + email: "jane@doe.com" + } +} +``` + +We then store the encrypted key to local storage, where we'll use it for subsequent calls to the servers from the client. + + + + + + + From 2b71474fafe24c62aa74cac4fdf959f1f48c1888 Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Mon, 14 Dec 2015 16:04:07 -0500 Subject: [PATCH 4/8] write article --- ...015-11-06-simply-secure-rails-api.markdown | 307 ++++++++++++------ 1 file changed, 214 insertions(+), 93 deletions(-) diff --git a/_posts/2015-11-06-simply-secure-rails-api.markdown b/_posts/2015-11-06-simply-secure-rails-api.markdown index 1c05693..b217eac 100644 --- a/_posts/2015-11-06-simply-secure-rails-api.markdown +++ b/_posts/2015-11-06-simply-secure-rails-api.markdown @@ -13,6 +13,7 @@ securing API endpoints. A simple (and obvious) solution is to make every request using an API key or something like [Basic HTTP Authentication headers](https://en.wikipedia.org/wiki/Basic_access_authentication). + Something like: Requesting APP (using the wonderful Faraday): @@ -55,31 +56,67 @@ Receiving APP: This is a fine system, pleasingly simple and straight-forward. However, it misses one basic fact about these requests: Most of them are made on behalf of the current user and not the requesting -application. Enter OAuth. +application. + +So a posts controller like this: + +```ruby + module Api + module V1 + class PostsController < ApplicationController + before_filter :restrict_access + + def index + @posts = current_user.posts.page(1) + respond_with @posts + end + + private + + def restrict_access + authenticate_or_request_with_http_token do |token, options| + ApiKey.exists?(access_token: token) + end + end + + end + end + end +``` + +Is, well, a little awkward. Because there's really no way to +know who the "current_user" is without passing in some extra variable. + + +#### Enter OAuth. OAuth especially via [DoorKeeper](https://github.com/doorkeeper-gem/doorkeeper) -and [Omniauth](https://github.com/intridea/omniauth), is great, -but opening new windows, redirect URIs, tokens, and the funky +and [Omniauth](https://github.com/intridea/omniauth), is amazing, +but popping opening new windows (or mobile apps), redirect URIs, tokens, secrets, and the funky user experience that often accompany them can be a bit too much -ceremony, particularly if you're connecting two trusted applications, -often times sharing the same base domain. +ceremony especially if you're connecting two trusted applications. #### Tokens to the rescue! Encrypting strings that can be later decrypted (usually obfuscated to a word: Token) -is a concept oft hidden Wizard-Of-Oz style curtains within frameworks like Rails. +is a concept oft hidden by Wizard-Of-Oz style curtains within frameworks like Rails. -*Initialization Vectors*, *Ciphers*, fancy words and bad hacker nicknames, inspiring both dread and awe in the uninitiated. -But just like any good magic trick deciphering the slight of hand is both empowering and inspiring. +Just Set some random ```secret_base_key``` and you're good to go! -#### Scenario +When you dig in, oh boy, *Initialization Vectors*, *Ciphers*, wacky words / bad hacker nicknames, inspiring both dread and awe in the uninitiated. +But just like any good magic trick deciphering the slight of hand is both empowering and inspiring. + +So let's take a peek behind the curtain. + +#### Imagine a scenario ##### Application 1: Our Authentication service (https://id.wework.io) -In a more traditional architecture, this would be the OAuth provider -This service has a login API. It uses [Devise](https://github.com/plataformatec/devise) -to manage authentication. This application also utilizes CORS via [Rack CORs](https://github.com/cyu/rack-cors). -Allowing the appropriate cross-origin access. +In a more traditional architecture, this would be the OAuth provider. + +This service has a login API. For the sake of brevity, it uses [Devise](https://github.com/plataformatec/devise) +to manage users and password. This application also utilizes CORS via [Rack CORs](https://github.com/cyu/rack-cors). +Allowing appropriately configured cross-origin access. ##### It has a user model @@ -152,32 +189,14 @@ end ##### Application 2: Social Network (https://network.wework.io) -A rails web app which uses the authentication service to log a user in and seed user data. -It stores: users (like most OAuth apps), and posts. +A rails web application which uses the aforementioned authentication service to log a user in and seed user data. -##### Login Screen +We want this application to log a user in, persist a "copy" of this user (like any standard OAuth app) and allow that user to create posts. -```html - - - Network - - - -
- - - -
- - -``` - -So, how do we get from these simple endpoints and actions, to a functioning ecosystem? -I thought you'd never ask! +#### Let's get started First you want to add a method to the authentication service's User Model. This method encrypts a -user's uuid or any column that's unique (or a combination of columns for extra security). +user's uuid. Using ruby's OpenSSL library @@ -202,11 +221,8 @@ class User < ActiveRecord::Base end ``` -Anyone know what this method does? Anyone? Anyone? - -That's OK!! - -It's pretty much way outside the norm of "ordinary" MVC web development. So lets go through it line by line. +Look at this bonkers method! It's pretty hard to figure out what's going on here. And to be fair it's outside +the norm of ordinary, MVC style web development. So lets go through it line by line. #### Line 1 @@ -218,21 +234,22 @@ The argument 'aes-256-cbc' is a hyphenated description of components of the ciph 1. "aes" is the name of the encryption algorithm. In this case, its an acronym for [Advanced Encryption Standard](http://aesencryption.net/). -2. 256 is the number of bits of the key used in the algorithm. You'll usually see 128 or 256. Practically, this just means that the key is "large" enough that it cannot be easily broken via a brute force attack. +2. 256 is the number of bits of the key used in the algorithm. You'll usually see 128 or 256. Practically, this means that the key is "large" enough that it cannot be feasibly broken via a brute force attack (it would take foooooorrreeeevvvveeeerrr) . -3. cbc stands for "cipher block chaining" and is the mode by which the aes algorithm encrypts the data. Its most commonly compared the ecb (electronic code book)[http://searchsecurity.techtarget.com/definition/Electronic-Code-Book]. -Its generally preferred to use cbc for encoding smaller things, because ebc may expose pieces of the real string in the encrypted string. +3. cbc stands for "cipher block chaining" and is the mode by which the aes algorithm encrypts the data. Its most commonly compared to the ecb [electronic code book](http://searchsecurity.techtarget.com/definition/Electronic-Code-Book). +Its generally preferred to use cbc because ebc may expose pieces of the actual key in the encrypted string when encrypting smaller pieces of data. -Full documentation is available [here](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html). +Anyway, like electricity, you needn't full understand these modes to use them. +But full documentation is always [available](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html). --- #### Line 2 ```ruby -cipher.encrypt +cipher.encrypt # cipher.decrypt ``` -This lets the cipher instance we defined before know that we intend to use it for encryption rather than decryption. +Since you instantiate a "decipher" the same as a cipher, this line lets the instance know that we intend to use it for encryption rather than decryption. --- @@ -242,8 +259,9 @@ cipher.key = ENV['SOME_SECRET_KEY'] ``` OK, here's where it starts to get interesting. -This is basically the first part of the password that will shared between applications. +This is the first part of the password that we will shared between applications. Its important to create a truly random (or as close to that as possible) key, so consult the [documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#class-OpenSSL::Cipher-label-Choosing+a+key) +to get something as secure as possible. --- @@ -252,15 +270,15 @@ Its important to create a truly random (or as close to that as possible) key, so cipher.iv = ENV['SOME_SECRET_IV'] ``` -The iv here, well this stands for initialization vector. An initialization vector creates an extra layer of security around a key, and is generally used only once per instance of a cipher. +IV, well this stands for initialization vector. An initialization vector creates an extra layer of security around a key, and is generally used only once per instance of a cipher. We're breaking the rules a little here, because our IV will be used only to prevent the accidental exposure of the key in the encrypted payload. Consult the [documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#class-OpenSSL::Cipher-label-Choosing+an+IV) to choose your IV. --- -For both of these, as implied by the code, you'll want to store them outside of version control and in some sort of easily changeable way (such as an environment variable) in case the are ever compromised. +For both of these, as implied by the code, you'll want to store them outside of version control and in some sort of easily changeable way in case they are ever compromised. -You'll also want to use different values for each environment, i.e. development, staging, production. +You'll also want to use different values for each environment, i.e. development, test, staging, production. --- @@ -269,8 +287,9 @@ You'll also want to use different values for each environment, i.e. development, cipher.padding = 1 ``` -This sets the cipher padding to 1. Just kidding, that would make too much sense! This ENABLES padding in the cipher. The short story is that the aes -block cipher algorithm mentioned above requires its input to be and EXACT multiple of the block size. Read the full [documentation](http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-padding-3D) +This sets the cipher padding to 1 character. Just kidding! This is a boolean, which ENABLES padding in the cipher. The short story is that the aes +block cipher algorithm mentioned above requires its' input to be an EXACT multiple of the block size. Setting this to 1 allows for variance in the size of the data being encrypted. +If you're very interested: read the full [documentation](http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-padding-3D) --- @@ -278,8 +297,9 @@ block cipher algorithm mentioned above requires its input to be and EXACT multip ```ruby encrypted = cipher.update(self.uuid) + cipher.final ``` -For practical purposes, this is a procedural event in the cipher's life-cycle. It returns the, you guessed it, final remaining data in the cipher object. -Or in more sane terms, something that's ready to be deciphered. [Documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#method-i-final) +This takes the actual data you want to encrypt, in this case the user's +UUID, and turns is into something that's actually ready to be deciphered. For practical purposes, calling .final a procedural event in the cipher's life-cycle. +[Documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#method-i-final) --- @@ -287,15 +307,13 @@ Or in more sane terms, something that's ready to be deciphered. [Documentation]( ```ruby Base64.urlsafe_encode64(encrypted).encode('utf-8') ``` -This is the actual return we want to transform the finalized cipher into so that it can be passed safely via a query string or form data. Only over SSL, of course :-) +This is the actual data we want to transform the finalized cipher into so that it can be passed safely via a query string or form data. Only over SSL, of course :) --- -### OK, lets put this to use. - -Now that our user model has an encrypted method we can modify the authentication app's +After our authentication application's user model has an encrypted method we can modify the: -##### Users controller +##### Authentication Application's User's controller ```ruby module Api @@ -317,58 +335,161 @@ module Api end ``` -Now that our authentication application is prepped to return encrypted uuids when provided a valid email and password we can alter our +Now that our authentication application is prepped to return encrypted UUIDs (when provided a valid email and password), we +can create our Social Networking application here's some boilerplate: -##### Client application login screen +##### Our Social Network's user class + +```ruby +class User < ActiveRecord::Base + # Schema + # Name: string + # UUID: UUID (primary key) + # Email + + def self.create_from_authentication_service(email, password) + begin + connection = Faraday.new(url: "https://id.wework.io") do |faraday| + faraday.adapter Faraday.default_adapter + faraday.headers['Content-Type'] = 'application/json' + end + + response = connection.post("/api/v1/login/", { + email: email, + password: password + }.to_json) + + parsed_body = JSON.parse(response.body) + + User.create({ + uuid: parsed_body["user"]["uuid"], + email: parsed_body["user"]["email"], + name: parsed_body["user"]["name"], + }) + rescue + false + end + end +end +``` +##### Our Social Network login controller + +```ruby +class LoginController < ApplicationController + + def new # Get /login + end + + def create # Post/login + + user = User.create_from_authentication_service(params[:email], params[:password]) + + if user + cookies.signed[:uuid] = user.uuid + redirect_to "some home", notice: "Great Success!" + else + redirect new_login_path, error: "Something went wrong!" + end + + end + +end + +``` + +##### Our Social Network application controller + +```ruby +class ApplicationController < ActionController::Base + + def current_user + @current_user ||= User.find_by(uuid: cookies.signed[:uuid]) + end + helper_method :current_user + +end + +``` + +##### Our new Social Network login view ```html Network - -
+
- ``` +--- + +Here's the important part: + +##### Our new Social Network's Application Controller -Using *GASP* jQuery we are able to post to our authentication app (with properly configured CORS) and -get a response that looks something like this: - -```json -{ - user:{ - encrypted: SOME_IMPOSSIBLE_TO_DECIPHER_STRING, - name: "Jane Doe", - email: "jane@doe.com" - } -} +```ruby +class ApplicationController < ActionController::Base + + protected + + def decrypt(encrypted_uuid) + begin + decipher = OpenSSL::Cipher.new 'aes-256-cbc' + decipher.decrypt + decipher.padding = 1 + decipher.key = ENV['SOME_SECRET_KEY'] + decipher.iv = ENV['SOME_SECRET_IV'] + decrypted = cipher.update(Base64.urlsafe_decode64(encrypted_uuid)) + decipher.final + decrypted + rescue + head 403 and return + end + end + + def current_user + if params[:encrypted_user_uuid] + @current_user ||= User.find_by_uuid( decrypt_uuid(params[:encrypted_user_uuid]) ) + end + end + helper_method :current_user + + def require_user + if !current_user.present? + head 403 + end + end + +end ``` -We then store the encrypted key to local storage, where we'll use it for subsequent calls to the servers from the client. +You'll notice that the ``` decrypted_uuid ``` follows the same pattern as the encryption method. +As you can probably guess, this runs through the previously documented code, essentially in reverse, and it outputs a recognize-able +uuid that you can use for lookup. + +### Great Success! + +--- + +##### Cautions and Caveats: + +As with any security implementation, there are ways to make this more robust and (probably) more than a few different through it. +You should always take your time and implement the system that is best for your needs and take into account what you're securing, +highly classified documents vs payment information vs access to profile information and make your decision appropriately. + +I hope you found this enlightening and happy lock downs! + +--- + + + + + From 0541cb3c10bd16620f33afce56b68a749b6551f9 Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Mon, 14 Dec 2015 16:18:08 -0500 Subject: [PATCH 5/8] post about simple, secure rails API --- _data/authors.yml | 6 ++++++ ...rkdown => 2015-12-14-simply-secure-rails-api.markdown} | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) rename _posts/{2015-11-06-simply-secure-rails-api.markdown => 2015-12-14-simply-secure-rails-api.markdown} (98%) diff --git a/_data/authors.yml b/_data/authors.yml index eba8719..e03f038 100644 --- a/_data/authors.yml +++ b/_data/authors.yml @@ -4,3 +4,9 @@ ramin_bozorgzadeh: github: https://github.com/i8ramin twitter: https://twitter.com/i8ramin summary: Engineering Director at WeWork and loving all things performance, front-end and design related. +paul_franzen: + name: Paul Franzen + author_image: http://www.gravatar.com/avatar/b5f95618b8dd37ee3de0b1f29be08466.jpg + github: https://github.com/paulfranzen + twitter: https://twitter.com/pfranzen + summary: "Principal Engineer at WeWork and raving, loud-mouthed malcontent" \ No newline at end of file diff --git a/_posts/2015-11-06-simply-secure-rails-api.markdown b/_posts/2015-12-14-simply-secure-rails-api.markdown similarity index 98% rename from _posts/2015-11-06-simply-secure-rails-api.markdown rename to _posts/2015-12-14-simply-secure-rails-api.markdown index b217eac..b372a12 100644 --- a/_posts/2015-11-06-simply-secure-rails-api.markdown +++ b/_posts/2015-12-14-simply-secure-rails-api.markdown @@ -1,10 +1,10 @@ --- layout: post title: Simply Secure Rails API -author: Paul Franzen -summary: +author: paul_franzen +summary: Ever needed a simple way to lock down an API? image: http://res.cloudinary.com/wework/image/upload/s--Al86WVSb--/c_fill,fl_progressive,g_face:center,h_1000,q_jpegmini:1,w_1600/v1427921778/engineering/5-ways-to-create-bulletproof-software-bear.jpg -categories: rails, api, security +categories: rails, api, security, engineering --- If you're attempting to implement a service orientated architecture @@ -482,7 +482,7 @@ As with any security implementation, there are ways to make this more robust and You should always take your time and implement the system that is best for your needs and take into account what you're securing, highly classified documents vs payment information vs access to profile information and make your decision appropriately. -I hope you found this enlightening and happy lock downs! +I hope you found this enlightening and many happy lock downs. --- From 2bc798c4ee6bb7c720ee31aea99df2977c126ccd Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Tue, 15 Dec 2015 12:41:14 -0500 Subject: [PATCH 6/8] lots of changes --- ...015-12-14-simply-secure-rails-api.markdown | 306 +++++++++++++++--- 1 file changed, 265 insertions(+), 41 deletions(-) diff --git a/_posts/2015-12-14-simply-secure-rails-api.markdown b/_posts/2015-12-14-simply-secure-rails-api.markdown index b372a12..8a4465a 100644 --- a/_posts/2015-12-14-simply-secure-rails-api.markdown +++ b/_posts/2015-12-14-simply-secure-rails-api.markdown @@ -16,7 +16,7 @@ API key or something like [Basic HTTP Authentication headers](https://en.wikiped Something like: -Requesting APP (using the wonderful Faraday): +Requesting APP (using the wonderful [Faraday](https://github.com/lostisland/faraday)): ```ruby Faraday.new(:url => "https://www.api.io/api/v1/posts") do |faraday| @@ -101,7 +101,7 @@ ceremony especially if you're connecting two trusted applications. Encrypting strings that can be later decrypted (usually obfuscated to a word: Token) is a concept oft hidden by Wizard-Of-Oz style curtains within frameworks like Rails. -Just Set some random ```secret_base_key``` and you're good to go! +Just set some random ```secret_base_key``` and you're good to go! When you dig in, oh boy, *Initialization Vectors*, *Ciphers*, wacky words / bad hacker nicknames, inspiring both dread and awe in the uninitiated. But just like any good magic trick deciphering the slight of hand is both empowering and inspiring. @@ -111,12 +111,10 @@ So let's take a peek behind the curtain. #### Imagine a scenario ##### Application 1: -Our Authentication service (https://id.wework.io) -In a more traditional architecture, this would be the OAuth provider. +In a more traditional architecture, our Authentication service (id.wework.io for example) would be the OAuth provider. -This service has a login API. For the sake of brevity, it uses [Devise](https://github.com/plataformatec/devise) -to manage users and password. This application also utilizes CORS via [Rack CORs](https://github.com/cyu/rack-cors). -Allowing appropriately configured cross-origin access. +This service has a login API. For the sake of brevity, it uses [Devise](https://github.com/plataformatec/devise). +This application also utilizes CORS via [Rack CORs](https://github.com/cyu/rack-cors) to allow appropriately configured cross-origin access. ##### It has a user model @@ -151,7 +149,7 @@ module Api def create # POST: /api/v1/login @user = User.find_by(email: params[:email]) - if @user.present? && @user.valid_password?(params[:password]) + if if User.authenticate(params[:email], params[:password]) respond_with: @user.as_json() else head 404 @@ -203,13 +201,14 @@ Using ruby's OpenSSL library ```ruby class User < ActiveRecord::Base # Schema - # Name: string - # UUID: UUID (primary key) - # Email + # name: string + # uuid: UUID (primary key) + # email: string # Devise password stuff devise :database_authenticatable def encrypted + srting_to_encrypt = "#{self.uuid}" cipher = OpenSSL::Cipher.new('aes-256-cbc') cipher.encrypt cipher.key = ENV['SOME_SECRET_KEY'] @@ -221,10 +220,9 @@ class User < ActiveRecord::Base end ``` -Look at this bonkers method! It's pretty hard to figure out what's going on here. And to be fair it's outside +Look at this bonkers class! It's pretty hard to figure out what's going on here. And to be fair it's outside the norm of ordinary, MVC style web development. So lets go through it line by line. - #### Line 1 ```ruby cipher = OpenSSL::Cipher.new('aes-256-cbc') @@ -239,8 +237,7 @@ The argument 'aes-256-cbc' is a hyphenated description of components of the ciph 3. cbc stands for "cipher block chaining" and is the mode by which the aes algorithm encrypts the data. Its most commonly compared to the ecb [electronic code book](http://searchsecurity.techtarget.com/definition/Electronic-Code-Book). Its generally preferred to use cbc because ebc may expose pieces of the actual key in the encrypted string when encrypting smaller pieces of data. -Anyway, like electricity, you needn't full understand these modes to use them. -But full documentation is always [available](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html). +More in-depth documentation is [available here](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html). --- @@ -288,7 +285,7 @@ cipher.padding = 1 ``` This sets the cipher padding to 1 character. Just kidding! This is a boolean, which ENABLES padding in the cipher. The short story is that the aes -block cipher algorithm mentioned above requires its' input to be an EXACT multiple of the block size. Setting this to 1 allows for variance in the size of the data being encrypted. +block cipher algorithm mentioned above requires its input to be an EXACT multiple of the block size. Setting this to 1 allows for variance in the size of the data being encrypted. If you're very interested: read the full [documentation](http://ruby-doc.org/stdlib-1.9.3/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-padding-3D) --- @@ -298,7 +295,7 @@ If you're very interested: read the full [documentation](http://ruby-doc.org/std encrypted = cipher.update(self.uuid) + cipher.final ``` This takes the actual data you want to encrypt, in this case the user's -UUID, and turns is into something that's actually ready to be deciphered. For practical purposes, calling .final a procedural event in the cipher's life-cycle. +UUID, and turns is into something that's actually ready to be deciphered. [Documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.html#method-i-final) --- @@ -323,7 +320,7 @@ module Api def create # POST: /api/v1/login @user = User.find_by(email: params[:email]) - if @user.present? && @user.valid_password?(params[:password]) + if User.authenticate(params[:email], params[:password]) respond_with: @user.as_json(methods: [:encrypted]) else head 404 @@ -343,9 +340,9 @@ can create our Social Networking application here's some boilerplate: ```ruby class User < ActiveRecord::Base # Schema - # Name: string - # UUID: UUID (primary key) - # Email + # name: string + # uuid: uuid (primary key) + # email: string def self.create_from_authentication_service(email, password) begin @@ -364,8 +361,10 @@ class User < ActiveRecord::Base User.create({ uuid: parsed_body["user"]["uuid"], email: parsed_body["user"]["email"], - name: parsed_body["user"]["name"], + name: parsed_body["user"]["name"] }) + + parsed_body rescue false end @@ -377,7 +376,7 @@ end ```ruby class LoginController < ApplicationController - + def new # Get /login end @@ -386,8 +385,7 @@ class LoginController < ApplicationController user = User.create_from_authentication_service(params[:email], params[:password]) if user - cookies.signed[:uuid] = user.uuid - redirect_to "some home", notice: "Great Success!" + render json: user else redirect new_login_path, error: "Something went wrong!" end @@ -398,20 +396,6 @@ end ``` -##### Our Social Network application controller - -```ruby -class ApplicationController < ActionController::Base - - def current_user - @current_user ||= User.find_by(uuid: cookies.signed[:uuid]) - end - helper_method :current_user - -end - -``` - ##### Our new Social Network login view ```html @@ -419,11 +403,23 @@ end Network -
+
+ ``` @@ -472,11 +468,239 @@ You'll notice that the ``` decrypted_uuid ``` follows the same pattern as the en As you can probably guess, this runs through the previously documented code, essentially in reverse, and it outputs a recognize-able uuid that you can use for lookup. +Now our social network can implement a controller like: + +```ruby +class PostsController < ApplicationController + before_filter :require_user + + def index + render json: current_user.posts + end + +end +``` + +Which can serve to either a client web app or a mobile app as long as you pass ```encrypted_user_uuid``` as a parameter in each request. + + ### Great Success! --- -##### Cautions and Caveats: +#### Lets Go The Extra Mile + +This implementation is pretty solid, and satisfies the basic needs for locking requests, but it leaves much room for improvement. + +Let's look at some of the problems, and try to fix them w/o making everything really complicated. + +#### Problem 1: Can't revoke these tokens individually + +By using ENV variables for the IV and Key we can revoke permissions, but only for "all users" not individual users. + + +Lets fix this! + +##### Our modified Social Network's user class + +```ruby +class User < ActiveRecord::Base + # Schema + # name: string + # uuid: uuid (primary key) + # email: string + # token: string + def before_validation :create_token + + def create_token + self.token ||= SecureRandom.urlsafe_base64(nil, false) + end + + def self.create_from_authentication_service(email, password) + begin + connection = Faraday.new(url: "https://id.wework.io") do |faraday| + faraday.adapter Faraday.default_adapter + faraday.headers['Content-Type'] = 'application/json' + end + + response = connection.post("/api/v1/login/", { + email: email, + password: password + }.to_json) + + parsed_body = JSON.parse(response.body) + + user = User.create({ + uuid: parsed_body["user"]["uuid"], + email: parsed_body["user"]["email"], + name: parsed_body["user"]["name"] + }) + + {user: user, payload: parsed_body} + rescue + false + end + end +end +``` + +Here we're creating a random token, per user, which we can alter / delete if the top level ever becomes compromised. + + +##### Our modified Social Network login view +```html + + + Network + + +
+ + + +
+ + + +``` + +##### Our new Social Network's Application Controller + +```ruby +class ApplicationController < ActionController::Base + + protected + + def decrypt(encrypted_uuid) + begin + decipher = OpenSSL::Cipher.new 'aes-256-cbc' + decipher.decrypt + decipher.padding = 1 + decipher.key = ENV['SOME_SECRET_KEY'] + decipher.iv = ENV['SOME_SECRET_IV'] + decrypted = cipher.update(Base64.urlsafe_decode64(encrypted_uuid)) + decipher.final + decrypted + rescue + head 403 and return + end + end + + def current_user + if params[:encrypted_user_uuid] && params[:token] + @current_user ||= User.find_by(uuid: decrypt_uuid(params[:encrypted_user_uuid]), token: params[:token]) + end + end + helper_method :current_user + + def require_user + if !current_user.present? + head 403 + end + end + +end +``` + +Now we are accessing this user based on an easily revoke-able token in the client app. + +--- + +##### Problem 2: Decoding + +Since we are encoding a uuid and the encrypted uuid is stored in the client its *technically possible*, the best kind of possible, for a nefarious person to gather up +enough uuids and their encrypted counterparts to break our encryption. + +So lets add some additional values to our encryption for parsing. + +Our Authentication service's modified User class + +```ruby +class User < ActiveRecord::Base + # Schema + # name: string + # uuid: UUID (primary key) + # email: string + # extra_lock: string + # Devise password stuff + devise :database_authenticatable + before_validation: ensure_extra_lock + + def ensure_extra_lock + self.extra_lock ||= ["Key 1", "Key 2", "Key 3", "Key 4"].sample # These probably shouldn't be real words + end + + def encrypted + srting_to_encrypt = "#{self.uuid}|||#{self.extra_lock}" + cipher = OpenSSL::Cipher.new('aes-256-cbc') + cipher.encrypt + cipher.key = ENV['SOME_SECRET_KEY'] + cipher.iv = ENV['SOME_SECRET_IV'] + cipher.padding = 1 + encrypted = cipher.update(srting_to_encrypt) + cipher.final + Base64.urlsafe_encode64(encrypted).encode('utf-8') + end +end +``` + +As you can see we've added a little extra data to the string that we are encrypting. + +Now on the client application: + +```ruby +class ApplicationController < ActionController::Base + + protected + + def decrypt(encrypted_uuid) + begin + decipher = OpenSSL::Cipher.new 'aes-256-cbc' + decipher.decrypt + decipher.padding = 1 + decipher.key = ENV['SOME_SECRET_KEY'] + decipher.iv = ENV['SOME_SECRET_IV'] + decrypted = cipher.update(Base64.urlsafe_decode64(encrypted_uuid)) + decipher.final + decrypted_split = decrypted.split("|||") + unless ["Key 1", "Key 2", "Key 3", "Key 4"].include?(decrypted_split[1]) + raise + end + decrypted_split[0] + rescue + head 403 and return + end + end + + def current_user + if params[:encrypted_user_uuid] && params[:token] + @current_user ||= User.find_by(uuid: decrypt_uuid(params[:encrypted_user_uuid]), token: params[:token]) + end + end + helper_method :current_user + + def require_user + if !current_user.present? + head 403 + end + end + +end +``` + +Since our apps know what keys are valid and know precisely how they are encrypted. Its fairly simple to add this extra little layer which would make brute forcing via many uuids *practically* impossible. + + +## Cautions and Caveats: As with any security implementation, there are ways to make this more robust and (probably) more than a few different through it. You should always take your time and implement the system that is best for your needs and take into account what you're securing, From 6cd8ec630f767acb587c51f0b7a80e5f859a2b38 Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Wed, 16 Dec 2015 10:57:19 -0500 Subject: [PATCH 7/8] add foreward and refine some more --- ...015-12-14-simply-secure-rails-api.markdown | 66 +++++++++++-------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/_posts/2015-12-14-simply-secure-rails-api.markdown b/_posts/2015-12-14-simply-secure-rails-api.markdown index 8a4465a..87af38f 100644 --- a/_posts/2015-12-14-simply-secure-rails-api.markdown +++ b/_posts/2015-12-14-simply-secure-rails-api.markdown @@ -2,11 +2,35 @@ layout: post title: Simply Secure Rails API author: paul_franzen -summary: Ever needed a simple way to lock down an API? +summary: If you're attempting to implement a service orientated architecture one of the most daunting tasks can be deciding on a method for securing API endpoints. image: http://res.cloudinary.com/wework/image/upload/s--Al86WVSb--/c_fill,fl_progressive,g_face:center,h_1000,q_jpegmini:1,w_1600/v1427921778/engineering/5-ways-to-create-bulletproof-software-bear.jpg categories: rails, api, security, engineering --- +**Quick Note:** + +> Hi, you probably have not seen a lot of articles/posts about how organizations secure their content on the Interwebs. I'm guessing this is a symptom of: +> +> 1. Fear of exposing specific practices that could put their data / applications at risk. +> +> 2. Fear of backlash among the development community where misunderstandings and semantic arguments reign supreme. And can label an entire company, or an individual (myself) as incompetent. +> +> I'd by lying if I said that these fears are not in my mind when authoring this post, but by relegating these topics to back-room-locked-down-hundred-page-decks from security consultants we deny ourselves public and accessible conversations around one of the most important issues in our industry. +> +> Working at a company like WeWork, I (We) believe that individual success is exponentially more achievable when you're part of a greater community. +> +> This belief can sometimes mean uncomfortable travels in which trust outweighs mistrust and you are willing to take on risk in the interest of greater success for both yourself and your community. +> +> Ok. Now that's out of the way. What follows is a system/explanation, undoubtedly flawed, but one which We hope will help some folks to understand some security concepts a little better and maybe help them in there travels. +> +> Thank you, and as always, comments and feedback are not only welcome, but necessary. + + + +--- + + + If you're attempting to implement a service orientated architecture one of the most daunting tasks can be deciding on a method for securing API endpoints. @@ -54,11 +78,11 @@ Receiving APP: ``` This is a fine system, pleasingly simple and straight-forward. -However, it misses one basic fact about these requests: Most of them -are made on behalf of the current user and not the requesting -application. +However, it misses one basic fact about these requests: Most +are made on behalf of the current **user and not the requesting +application.** -So a posts controller like this: +So a posts controller: ```ruby module Api @@ -92,9 +116,9 @@ know who the "current_user" is without passing in some extra variable. OAuth especially via [DoorKeeper](https://github.com/doorkeeper-gem/doorkeeper) and [Omniauth](https://github.com/intridea/omniauth), is amazing, -but popping opening new windows (or mobile apps), redirect URIs, tokens, secrets, and the funky +but popping-open new windows (or mobile apps), redirect URIs, tokens, secrets, and the funky user experience that often accompany them can be a bit too much -ceremony especially if you're connecting two trusted applications. +ceremony especially if you're connecting two *trusted* applications. #### Tokens to the rescue! @@ -149,7 +173,7 @@ module Api def create # POST: /api/v1/login @user = User.find_by(email: params[:email]) - if if User.authenticate(params[:email], params[:password]) + if User.authenticate(params[:email], params[:password]) respond_with: @user.as_json() else head 404 @@ -220,7 +244,7 @@ class User < ActiveRecord::Base end ``` -Look at this bonkers class! It's pretty hard to figure out what's going on here. And to be fair it's outside +Look at this bonkers encrypted method! It's pretty hard to figure out what's going on here. And to be fair, it's way outside the norm of ordinary, MVC style web development. So lets go through it line by line. #### Line 1 @@ -273,7 +297,7 @@ Consult the [documentation](http://docs.ruby-lang.org/en/trunk/OpenSSL/Cipher.ht --- -For both of these, as implied by the code, you'll want to store them outside of version control and in some sort of easily changeable way in case they are ever compromised. +For both of these (as implied by the code), you'll want to store them outside of version control and in some sort of easily changeable way in case they are ever compromised. You'll also want to use different values for each environment, i.e. development, test, staging, production. @@ -619,7 +643,7 @@ Now we are accessing this user based on an easily revoke-able token in the clien ##### Problem 2: Decoding -Since we are encoding a uuid and the encrypted uuid is stored in the client its *technically possible*, the best kind of possible, for a nefarious person to gather up +Since we are encoding a uuid and the encrypted uuid is stored in the client its *technically possible* ([the best kind of possible](https://www.youtube.com/watch?v=hou0lU8WMgo)) for a nefarious person to gather up enough uuids and their encrypted counterparts to break our encryption. So lets add some additional values to our encryption for parsing. @@ -697,27 +721,13 @@ class ApplicationController < ActionController::Base end ``` -Since our apps know what keys are valid and know precisely how they are encrypted. Its fairly simple to add this extra little layer which would make brute forcing via many uuids *practically* impossible. - +Since our apps know what keys are valid and know precisely how they are encrypted. +Its fairly simple to add this extra little layer which would make brute forcing via many uuids *practically* impossible. ## Cautions and Caveats: As with any security implementation, there are ways to make this more robust and (probably) more than a few different through it. You should always take your time and implement the system that is best for your needs and take into account what you're securing, -highly classified documents vs payment information vs access to profile information and make your decision appropriately. - -I hope you found this enlightening and many happy lock downs. +highly classified documents vs payment information vs access to profile information and make your decision/implementation appropriately. --- - - - - - - - - - - - - From 7c4cba7fbbbb22771c2ecb63fe038f7c64035997 Mon Sep 17 00:00:00 2001 From: Paul Franzen Date: Wed, 16 Dec 2015 11:39:32 -0500 Subject: [PATCH 8/8] describe encrypted on client, and fix their --- _posts/2015-12-14-simply-secure-rails-api.markdown | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/_posts/2015-12-14-simply-secure-rails-api.markdown b/_posts/2015-12-14-simply-secure-rails-api.markdown index 87af38f..a07ba09 100644 --- a/_posts/2015-12-14-simply-secure-rails-api.markdown +++ b/_posts/2015-12-14-simply-secure-rails-api.markdown @@ -21,7 +21,7 @@ categories: rails, api, security, engineering > > This belief can sometimes mean uncomfortable travels in which trust outweighs mistrust and you are willing to take on risk in the interest of greater success for both yourself and your community. > -> Ok. Now that's out of the way. What follows is a system/explanation, undoubtedly flawed, but one which We hope will help some folks to understand some security concepts a little better and maybe help them in there travels. +> Ok. Now that's out of the way. What follows is a system/explanation, undoubtedly flawed, but one which We hope will help some folks to understand some security concepts a little better and maybe help them in their journey. > > Thank you, and as always, comments and feedback are not only welcome, but necessary. @@ -449,7 +449,10 @@ end ``` --- -Here's the important part: +As you can see, this lets you store an encrypted string on the *client* side. And unlike the Master Access Token Approach described at the beginning of this article, +this token is only valid for this particular user, so the client can be a mobile app or a web front-end so if this token is compromised, its only for this single user. + +Here's the reaaaalllyy important part: ##### Our new Social Network's Application Controller