7.2 secure the password

chapter 7.2

 

again, let's start from TDD again!!!!!!!!!!!!!!

 

1. since we define the encrypt_password into private area, how do we test it????????

 

ok, we need some public interface to use it. (TDD by acting as a client, the test motivate us to design a useful interface right from the start)

 

authentication involves comparing the encrypted version of submitted password to the encreptyed_password in the database. This means we need to define a method:

 

has_password?

 

this will be public interface.

 

 

def has_password?(submitted_password)
    # to do
end

 

for the test:

 

 

describe "has_password? method" do
    it "should be true if the passwords match" do
        @user.has_password?(@attr[:password]).should be_true
    end
     it "should be false if the passwords don't match" do
        @user.has_password?("invalid").should be_false
    end
end

 

2. study some secure password theory:

 

a. instead of storing the raw password, we store a hashed password,

b. this hashed password is inreversible, this means even the hacher get the encrypted password, he can't infer the original.

c. to do the authentication, we first encrypt the submitted password, then do compare.

 

for examle, we use SHA2 to hash the password:

 

require "digest"

def secure_hash(string)

Digest::SHA2.hexdigest(string)

end

password = "secret"

encrypted_password = secure_hash(password)

submitted_password = "secret"

encrypted_password == secure_hash(submitted_password)  ======> true

 

 

Digest::SHA2.hexdigest(string)

this is one-way, it is impossible to deduce the original password from the hashed value.

 

But:

we still have a problem: if the hacker ever hold the hashed password, and he can guess we use SHA2, and write a program to compare the given hash to the hashed values of many common password, to find the original password!!

 

so we are still in a security hole!!!


how to solve it?


the answer is "salt"!!!

 

for example:

 

Time.now.utc

password = "secret"

salt = secure_hash("#{Time.now.utc}--#{password}")

encrypted_password = secure_hash("#{salt}--#{password}")

 

This password is impossible to crack!!

 

For clarity, arguments to hashing functions are often separated with "--"

 

 

3. now we are ready to implement has_password? method:

 

 

def has_password?(submitted_password)
    encrypted_password == encrypt(submitted_password)
end

 as long as the submitted password using the same salt, it will work fine.

 

 

5. we need to add a new column called salt to the users table.

 

 

rails g migration add_salt_to_users salt:string
rake db:migrate
rake db:test:prepare

 

6. finally, we will implement the full.

 

 

require 'digest'
class User < ActiveRecord::Base
	before_save :encrypt_password

	def has_password?(submitted_password)
		encrypted_password == encrypt(submitted_password)
	end

	private
		def encrypt_password
			self.salt = make_salt unless has_password?(password)
			self.encrypted_password = encrypt(password)
		end
		def encrypt(string)
			secure_hash("#{salt}--#{string}")
		end
		def make_salt
			secure_hash("#{Time.now.utc}--#{password}")
		end
		def secure_hash(string)
			Digest::SHA2.hexdigest(string)
		end
		
end

 

ok, let's run the test, it will pass:

 

rspec spec/models/user_spec.rb -e "has_password\? method"

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值