diff options
-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 --> |