diff options
author | Paul Duncan <pabs@pablotron.org> | 2016-05-21 13:05:20 -0400 |
---|---|---|
committer | Paul Duncan <pabs@pablotron.org> | 2016-05-21 13:05:20 -0400 |
commit | b1d1a7c6c5c13c1496fa87a0eddaf4e724ecb299 (patch) | |
tree | 9ab1bac834199321fa6d3cb8568def02ed200cc6 | |
parent | 59e64495121447c988d6aef243b7b3c17cb5f483 (diff) | |
download | guff-b1d1a7c6c5c13c1496fa87a0eddaf4e724ecb299.tar.bz2 guff-b1d1a7c6c5c13c1496fa87a0eddaf4e724ecb299.zip |
add csrf protection to login page
-rw-r--r-- | src/guff.cr | 58 | ||||
-rw-r--r-- | src/views/login-page.ecr | 6 |
2 files changed, 64 insertions, 0 deletions
diff --git a/src/guff.cr b/src/guff.cr index 8046af7..b610e96 100644 --- a/src/guff.cr +++ b/src/guff.cr @@ -239,6 +239,53 @@ module Guff r end end + + class CSRFModel < Model + getter :minutes + + def initialize(context : Context) + super(context) + @cache = {} of String => Int64 + + # expire form after 5 minutes + # TODO: make this configurable + @minutes = 5 + end + + def create_token + remove_expired_tokens + p @cache + + # generate and cache new token + r = SecureRandom.hex(16) + @cache[r] = Time.now.epoch + 60 * @minutes + + # return token + r + end + + def use_token(id : String) + remove_expired_tokens + + if @cache.has_key?(id) + # remove token, return success + @cache.delete(id) + true + else + # return failure + false + end + end + + private def remove_expired_tokens + now = Time.now.epoch + + # remove expired entries + @cache.delete_if do |key, val| + val < now + end + end + end end class ModelSet @@ -249,6 +296,7 @@ module Guff define_model_set_getters({ user: Models::UserModel, session: Models::SessionModel, + csrf: Models::CSRFModel, }) end @@ -383,6 +431,10 @@ module Guff super(context) end + def get_csrf_token + @context.models.csrf.create_token + end + ECR.def_to_s("src/views/login-page.ecr") end @@ -595,10 +647,16 @@ module Guff raise "missing login parameters" unless %w{ username password + csrf_token }.all? do |key| params.has_key?(key) && params[key].size > 0 end + # check csrf token + unless @context.models.csrf.use_token(params["csrf_token"]) + raise "invalid csrf token" + end + # try login user_id = @context.models.user.login( params["username"], diff --git a/src/views/login-page.ecr b/src/views/login-page.ecr index 9f2082a..e0404ce 100644 --- a/src/views/login-page.ecr +++ b/src/views/login-page.ecr @@ -75,6 +75,12 @@ Log In </button> </div><!-- form-group --> + + <input + type='hidden' + name='csrf_token' + value='<%= h(get_csrf_token) %>' + /> </form> </div><!-- panel-body --> </div><!-- panel --> |