The information system I sometimes use has 2 access codes that are password-masked.
Is this just a security through obscurity measure (being able to punch in username and password in front of audience) or does it have any other advantage over the conventional user/pass or token/secred?
I'm considering this when building my own IS for my business partners and myself. Is this any good or just annoying and useless unmemorable stuff for the user?
If it is a good idea, how to implement that with user.authenticate()?
I would not implement such a system, because..
The username/ID (first "access code") need not be a secret; while it should not expose confidential information (defined by policy), the purpose of this key is not to "add security" and making it hard to remember would annoy people - at least, it would annoy me.
If a user has to write down a "secret" because it is too hard to remember .. then anyone with access to the recording (e.g. text file, Post-It note) has access to the might-not-be-a-secret secret.
The way to increase security with passwords (second "access code") is to encourage passphrases, which are can be easier than "P#ssw0rds!" to remember (and are much easier than random passwords to remember!), but much harder to brute-force. It is the password/pass-phrase which is the secret token.
Assuming the use of proper connection encryption and using sound bcrypt/scrypt password hashing (and not suffering from an attack vector such as Heartbleed or a local keysniffer), then the next consideration is to mitigate brute-force attacks.
I would focus on using a solid (exiting and proven) authentication implementation, and secure server management and key policy.
That being said, here are additional thoughts ..
It might be useful/relevant to make the username/ID (first "access code") field masked, like a password field. This can prevent cases where the password/pass-phrase is accidentally exposed when entered into a username/ID field, as when such authentication is done in front of a live audience. (I've seen this mistake done several times.)
However the goal is not to add security, excepting that it can mitigate accidents, as the username/ID is not a password: it is not "encrypted", hashed, or otherwise considered a secret.
Using an additional credential provider (e.g. RSA fob, or smart card/fingerprint/pub-private keys) can be used in such cases where increased security is required. Appropriate use of such is much more secure than "two passwords".
In terms of security, access codes are probably slightly more secure than user & pass, considering they're encrypted correctly. This is my opinion
For Rails, you will have to remember 3 important factors:
Who's using your system
How will they engage with the authentication area
Are you using with any other system (such as Devise)?
--
Public Keys
If you're looking to create a preview mode of sorts, I would create a series of API keys, which you'll be able to use to gain limited functionality to the app.
We do that like this:
#users table
id | public_key | other | information | created_at | updated_at
#app/models/concerns/token.rb
module Token
extend ActiveSupport::Concern
included do
before_create :generate_token
end
protected
def generate_token
self.public_key = loop do
random_token = SecureRandom.urlsafe_base64(10, false)
break random_token unless self.class.exists?(public_key: random_token)
end
end
end
#app/models/user.rb
include Token
I found this code somewhere (I forgot where unfortunately), but it basically uses the before_create callback to populate the public_key attribute of your User model.
The public key is created using the SecureRandom method
Related
Basically as the question asks.
The AuthLogic documentation for verify_password_method states:
The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
I'd like to support this, because it allows me to maintain our system's current masquerading behaviour without storing plain-text passwords like we have been in the past. However, it seems to me like allowing a simple string comparison to the crypted_password in the database is just as bad as storing the regular plain-text passwords.
Am I missing something here, or is there some other way to accept a crypted password in AuthLogic?
For reference, this is how I was going to write the valid_password? method:
def valid_password?(password, check_from_database = nil)
if password == self.crypted_password
true
else
super(password, check_from_database)
end
end
Ok, turns out that there's a much easier way to do this (although it seems horribly undocumented, and didn't turn up with a Google search of how to achieve this).
Authlogic::Session::Base.new(#user, true)
That line allows session creation without checking credentials. Obviously you should be careful with this since it assumes that the user has already identified themselves correctly - for my usage, since there is a check to ensure the current user is the admin user, it's safe.
I've been developing my rails apps whilst keeping them as modular as possible. I'm trying to implement different parts underneath as services.
Say an example of Facebook:
a) A MainApp that allows the user to have a wall, posts, etc.
b) A PhotoApp that stores photos, allows the user to see his photos, etc. This is a standalone app that will have a REST API that can be used by MainApp as well.
I was thinking of using OAuth as a Single Sign On solution (as in this tutorial http://blog.joshsoftware.com/2010/12/16/multiple-applications-with-devise-omniauth-and-single-sign-on/) where each app will be authorized via OAuth and will get access to the current user session based on the cookie.
First question: Is this a viable solution?
Second question: I want to be able to call the PhotoApp API from the MainApp server (not from the user's browser). How would authentication work in this situation?
Third question: How would this work if say I had a service that used node.js?
Yes, SSO using OAuth is a viable solution, but it's not the simplest one. When building anything new, OAuth 2.0 is the way to go. The OAuth standards cover a lot of ground.
The primary advantage of OAuth is that it allows users to give 3rd party apps access to their account without disclosing their password to the 3rd party. If you are not seriously providing such interoperability, then OAuth is probably overkill.
Given the complexity, I offer a different pair of solutions:
For Single Sign On
The trick is to share the session ID cookie between hosts within your domain & to use a shared session store (like ActiveRecordStore or a cache-based store.)
Every Rails app has a "secret" that is used to sign cookies. In newer Rails apps this is located in /config/initializers/secret_token.rb. Set the same secret token in each application.
Then, configure the session to allow access from all subdomains:
AppName::Application.config.session_store :active_record_store, :key => '_app_name_session', :domain => :all
For Internal API calls
Use a good shared secret to authenticate over HTTPS connections. Pass the secret in the "Authorization" header value.
You can use the shared secret easily with other architectures (like node.js). Just make sure you always use HTTPS, otherwise the shared secret could be sniffed on the network.
You could look at a Service Oriented Architecture solution as proposed by Jeremy Green at Octolabs during the 2014 RailsConf.
The blog post with all the resources (repos, demos, etc.) is located here: http://www.octolabs.com/so-auth
And the video that explains everything is here: http://www.youtube.com/watch?v=L1B_HpCW8bs
This centralized SSO is no simple task but Jeremy has done an excellent job talking about Service Oriented Architecture and sharing exactly how you might put this system together.
I recently had a similar problem of wanting to share session data between Rails and an Erlang app. My solution was to write a Rack::Session::Abstract::ID class that stored sessions in Redis as hash vaules. It doesn't call Marshal.dump on String types. This allows non-ruby applications to use some of the session values if they have the session_id.
require 'rack/session/abstract/id'
class MaybeMarshalRedisSession < Rack::Session::Abstract::ID
def initialize(app, options = {})
#redis = options.delete(:redis) || Redis.current
#expiry = options[:expire_after] ||= (60 * 60 * 24)
#prefix = options[:key] || 'rack.session'
#session_key = "#{#prefix}:%s"
super
end
def get_session(env, sid)
sid ||= generate_sid
session = #redis.hgetall(#session_key % sid)
session.each_pair do |key, value|
session[key] = begin
Marshal.load(value)
rescue TypeError
value
end
end
[sid, session]
end
def set_session(env, sid, session, options={})
#redis.multi do
session.each_pair do |key, value|
# keep string values bare so other languages can read them
value = value.is_a?(String) ? value : Marshal.dump(value)
#redis.hset(#session_key % sid, key, value)
end
#redis.expire(#session_key % sid, #expiry)
end
sid
end
def destroy_session(env, sid, option={})
#redis.del(#session_key % sid)
generate_sid unless options[:drop]
end
end
You can use this from rails with:
MyApp::Application.config.session_store MaybeMarshalRedisSession
From Rack with:
use MaybeMarshalRedisSession
And from elsewhere with:
redis.hgetall("rack.session:#{session_id}")
If you want to call PhotoApp from your MainApp or Node.js you can make a HTTP request that includes your user's session cookie.
I am running Ruby on Rails 3 and I would know if the code that I am using in order to set the cookie value for user authentication purposes is strong enough.
In my model I have:
require 'digest'
class User < ActiveRecord::Base
...
def make_cookie_id_salt(string)
secure_hash("#{self.id}--#{string}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
In my controller I have:
cookies.signed[:current_user_id] = { :value => [#user.id, #user.make_cookie_id_salt(#user.id)], :expires => 15.days.from_now }
Is it strong enough? If no, how I can improve that (make an example!)?
Everything that gets put into cookies is stored as plain text.
If you set a cookie, and then check the cookies in your browser you will notice (in your case the cookie name would be current_user_id) that it is represented by a string of characters like: G8gcm9sbCB5b3VyIG93biBhdXRoIHRvIGt... (Not quite plain text, right? It is actually Base64 encoded, but you can easily read it - require('base64'); Base64.decode64(string)).
Rails stores a special _yourapp_session cookie that is used to check the cookies validity. If for example someone/something was trying to modify it, it would get rejected.
Now in your case it doesn't really matter if you try to hash something in the cookie or not.
It is just used for authentication (to look up a user in the database by his id) and you are not storing any unique secret data (Which you should not place in a cookie anyway, but it would be the only reason to hash something)
Of course someone could steal the cookie of a user (if he used a public computer and hasn't cleared his cache, etc.) and log in, but there's no way to prevent that (No matter what kind of hashing was used to obfsucate it)
In conclusion you should be fine with what you have.
Rather than try to create your own, I suggest using the Authlogic gem. In a few minutes of configuration you get a complete authentication solution, including cookies and much more. If you really want to roll your own, install the Authlogic gem and take a look at how they do it.
Devise is another option. It's extremely configurable, pretty DRY, with exhausting wiki.
For now-days I prefer it over Authlogic.
I'm working on an API wrapper for Viddler, which will eventually be made public, and I'm trying to figure out the best way to deal with authentication/API keys, specifically with usage within Rails applications in mind.
The easiest way to write the wrapper would be to just have the code create a new client each time, and the developer could store the API key in a constant for future use:
#client = Viddler::Client.new(VIDDLER_API_KEY)
The problem with this is, it's kind of clunky to have to keep creating client objects and passing in the API key. This gets even more complicated when you throw user authentication into the mix.
I'm thinking some sort of solution where I all the the API key to be set in the environment file and then the authentication would be done in a before_filter.
Viddler::Client.api_key = 'abc123'
Viddler::Client.authenticate! 'username', 'password'
Viddler::Client would then store this in a class variable, and you could call Viddler::Client.new without any parameters and make authenticated calls. One thing I'd be concerned about is that this means the developer would have to be sure to clear out the authentication before or after each request, since the class variables would persist between requests.
Any thoughts?
Storing the API key globally would for sure be pretty useful and certainly is the way to go for that kind of information. User authentication on the other hand I think shouldn't be stored globally, never ever, especially for a high level API, because telling your users to "ensure to add an after_filter :reset_viddler_auth" might lead to some unexpected security risks.
# in a config/initializer/*.rb file or something
Viddler::Client.api_key = "abc123"
# in the controller/action/model/wherever
#client = Viddler::Client.new # anonymous
#client.authenticate!("username", "password") # authenticate anon client
#client_auth = Viddler::Client.new("username", "password") # authenticated client
Guess like that you've best of both worlds :) Maybe even provide a way to create a new client with another API key like,
#client_other = Viddler::Client.new("username", "password", :api_key => "xyz890")
So... just my 2 cents.
PS: not sure how up-to-date it is, but there's already a ruby viddler wrapper, just FYI, http://viddler.rubyforge.org/rdoc/
I'd like to keep my database clean of stale almost-accounts, and I was thinking about making new signups and invitations put their data into the welcome email as an encrypted or hashed url. Once the link in the url is visited, the information is then added into the database as an account.
Is there something that currently does this? Any references, thoughts, or warnings about doing user registration this way?
Thanks!
Edit:
I've made a working example, and the url is 127 characters.
http://localhost/confirm?_=hBRCGVqie5PetQhjiagq9F6kmi7luVxpcpEYMWaxrtSHIPA3rF0Hufy6EgiH%0A%2BL3t9dcgV9es9Zywkl4F1lcMyA%3D%3D%0A
Obviously, more data = larger url
def create
# Write k keys in params[:user] as v keys in to_encrypt, doing this saves LOTS of unnecessary chars
#to_encrypt = Hash.new
{:firstname => :fn,:lastname => :ln,:email => :el,:username => :un,:password => :pd}.each do |k,v|
#to_encrypt[v] = params[:user][k]
end
encrypted_params = CGI::escape(Base64.encode64(encrypt(compress(Marshal.dump(#to_encrypt)), "secret")))
end
private
def aes(m,t,k)
(aes = OpenSSL::Cipher::Cipher.new('aes-256-cbc').send(m)).key = Digest::SHA256.digest(k)
aes.update(t) << aes.final
end
def encrypt(text, key)
aes(:encrypt, text, key)
end
def decrypt(text, key)
aes(:decrypt, text, key)
end
# All attempts to compress returned a longer url (Bypassed by return)
def compress(string)
return string
z = Zlib::Deflate.new(Zlib::BEST_COMPRESSION)
o = z.deflate(string,Zlib::FINISH)
z.close
o
end
def decompress(string)
return string
z = Zlib::Inflate.new
o = z.inflate(string)
z.finish
z.close
o
end
Thoughts:
Use true asymmetric cypher for the "cookie" to prevent bots creating accounts. Encrypt the "cookie" using public key, verify it by decoding with private key.
Rationale: If only a base64 or other algorithm was used for encoding the cookie, it would be easy to reverse-engineer the scheme and create accounts automatically. This is undesirable because of spambots. Also, if the account is password protected, the password would have to appear in the cookie. Anyone with access to the registration link would be able not only to activate the account, but also to figure out the password.
Require re-entry of the password after activation through the link.
Rationale: Depending on the purpose of the site you may want to improve the protection against information spoofing. Re-entering the password after activation protects against stolen/spoofed activation links.
When verifying the activation link, make sure the account created by it is not created already.
How do you protect against two users simultaneously creating an account with the same name?
Possible answer: Use email as the login identifier and don't require unique account name.
Verify the email first, than continue account creation.
Rationale: This will minimize the information you need to send in the cookie.
There are some e-mail clients which break URLs after 80 letters. I doubt that you can fit all the information in there.
Some browsers have limitations for the URL, Internet Explorer 8 has a limit of 2083 characters, for example.
Why don't you clean your database regularly (cron script) and remove all accounts that haven't been activated for 24 houres?
I have done pretty much the same before. I only have 2 suggestions for you,
Add a key version so you can rotate the key without breaking outstanding confirmation.
You need a timestamp or expiration so you can set a time limit on confirmation if you want to. We allow one week.
As to the shortest URL, you can do better by making following changes,
Use a stream cipher mode like CFB so you don't have to pad to the block size.
Compress the cleartext will help when the data is big. I have a flag and only use compression when it shrinks data.
Use Base64.urlsafe_encode64() so you don't have to URL encode it.
There's a few problems with your solution.
Firstly, you're not setting the IV of the cipher. In my view this has exposed a serious bug in the Ruby OpenSSL wrapper - it shouldn't let you perform an encryption or decryption until both key and iv have been set, but instead it's going ahead and using an IV of all-zeroes. Using the same IV every time basically removes much of the benefit of using a feedback mode in the first place.
Secondly, and more seriously, you have no authenticity checking. One of the properties of CBC mode is that an attacker who has access to one message can modify it to create a second message where a block in the second message has entirely attacker-controlled contents, at the cost of the prior block being completely garbled. (Oh, and note that CFB mode is just as much a problem in this regard).
In this case, that means that I could request an account with Last Name of AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA and my own email address to recieve a valid URL. I can then, without knowing the key, modify the email address to victim#victim.com (and garble the Last Name in the process, which doesn't matter), and have a valid URL which I can submit to your server and create accounts for email addresses that I don't control.
To solve this, you need to compute a HMAC over the data, keyed with a secret that only the server knows, and send that as part of the URL. Note that the only reason you need encryption at all here is to secure the user's password - other than that it could just be plaintext plus a HMAC. I suggest you simply send as the url something like:
?ln=Last%20Name&fn=First%20Name&email=foo#bar.com&hmac=7fpsQba2GMepELxilVUEfwl3%2BN1MdCsg%2FZ59dDd63QE%3D
...and have the verification page prompt for a password (there doesn't seem to be a reason to bounce the password back and forth).
I will take a crack at describing a design that may work.
Prerequisities:
Cryptography library with support for RSA and some secure hash function H (eg. SHA-1)
One pair of private and public keys
Design:
Unique user identifier is e-mail address
An account has associated password and possible other data
The activation cookie is kept as small as possible
Process:
User is asked for e-mail address and password. Upon submission of the form a cookie is computed as
cookie = ENCRYPT(CONCAT(email, '.', H(password)), public key)
E-mail is sent containing a link to the activation page with the cookie, eg.
http://example.org/activation?cookie=[cookie]
The activation page at http://example.org/activation decrypts the cookie passed as parameter: data = SPLIT(DECRYPT(cookie, private key), '.')
In the same activation page the user is asked for password (which must be hashed to the the same value as in cookie) and any other information necessary for the account creation
Upon submission of the activation page a new account is created
Please point out anything that I have missed or any improvements. I'd be glad to update the answer accordingly.