diff --git a/spec/middleware/csrf_spec.cr b/spec/middleware/csrf_spec.cr deleted file mode 100644 index 198c4b4..0000000 --- a/spec/middleware/csrf_spec.cr +++ /dev/null @@ -1,72 +0,0 @@ -require "../spec_helper" - -describe "Kemal::Middleware::CSRF" do - it "sends GETs to next handler" do - handler = Kemal::Middleware::CSRF.new - request = HTTP::Request.new("GET", "/") - io_with_context = create_request_and_return_io(handler, request) - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.status_code.should eq 404 - end - - it "blocks POSTs without the token" do - handler = Kemal::Middleware::CSRF.new - request = HTTP::Request.new("POST", "/") - io_with_context = create_request_and_return_io(handler, request) - client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) - client_response.status_code.should eq 403 - end - - it "allows POSTs with the correct token in FORM submit" do - handler = Kemal::Middleware::CSRF.new - request = HTTP::Request.new("POST", "/", - body: "authenticity_token=cemal&hasan=lamec", - headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded"}) - io, context = process_request(handler, request) - client_response = HTTP::Client::Response.from_io(io, decompress: false) - client_response.status_code.should eq 403 - - current_token = context.session["csrf"] - - handler = Kemal::Middleware::CSRF.new - request = HTTP::Request.new("POST", "/", - body: "authenticity_token=#{current_token}&hasan=lamec", - headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded", - "Set-Cookie" => client_response.headers["Set-Cookie"]}) - io, context = process_request(handler, request) - client_response = HTTP::Client::Response.from_io(io, decompress: false) - client_response.status_code.should eq 404 - end - - it "allows POSTs with the correct token in HTTP header" do - handler = Kemal::Middleware::CSRF.new - request = HTTP::Request.new("POST", "/", - body: "hasan=lamec", - headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded"}) - io, context = process_request(handler, request) - client_response = HTTP::Client::Response.from_io(io, decompress: false) - client_response.status_code.should eq 403 - - current_token = context.session["csrf"].as(String) - - handler = Kemal::Middleware::CSRF.new - request = HTTP::Request.new("POST", "/", - body: "hasan=lamec", - headers: HTTP::Headers{"Content-Type" => "application/x-www-form-urlencoded", - "Set-Cookie" => client_response.headers["Set-Cookie"], - "x-csrf-token" => current_token}) - io, context = process_request(handler, request) - client_response = HTTP::Client::Response.from_io(io, decompress: false) - client_response.status_code.should eq 404 - end -end - -def process_request(handler, request) - io = IO::Memory.new - response = HTTP::Server::Response.new(io) - context = HTTP::Server::Context.new(request, response) - handler.call(context) - response.close - io.rewind - {io, context} -end diff --git a/spec/session_spec.cr b/spec/session_spec.cr deleted file mode 100644 index 1a121d6..0000000 --- a/spec/session_spec.cr +++ /dev/null @@ -1,86 +0,0 @@ -require "./spec_helper" - -describe "Session" do - it "can establish a session" do - sid = nil - existing = nil - get "/" do |env| - sess = env.session - existing = sess["token"]? - sess.delete("token") - sid = sess.id - sess["token"] = "abc" - "Hello" - end - - # make first request without any cookies/session - request = HTTP::Request.new("GET", "/") - response = call_request_on_app(request) - - # verify we got a cookie and session ID - cookie = response.headers["Set-Cookie"]? - cookie.should_not be_nil - response.cookies[Kemal.config.session["name"].as(String)].value.should eq(sid) - lastsid = sid - existing.should be_nil - - # make second request with cookies to get session - request = HTTP::Request.new("GET", "/", response.headers) - response = call_request_on_app(request) - - # verify we got cookies and we could see values set - # in the previous request - cookie2 = response.headers["Set-Cookie"]? - cookie2.should_not be_nil - cookie2.should eq(cookie) - response.cookies[Kemal.config.session["name"].as(String)].value.should eq(lastsid) - existing.should eq("abc") - end - - it "can prune old sessions" do - s = Kemal::Sessions::STORE - s.clear - - Kemal::Sessions.prune! - - id = "foo" - s[id] = Kemal::Sessions::Session.new(id) - s.size.should eq(1) - Kemal::Sessions.prune! - s.size.should eq(1) - - s[id].last_access_at = (Time.now - 1.week).epoch_ms - Kemal::Sessions.prune! - s.size.should eq(0) - end - - it "supports many types" do - who = nil - age = nil - awesome = nil - velocity = nil - get "/" do |env| - sess = env.session - who = sess["who"]? - age = sess["age"]? - velocity = sess["velocity"]? - awesome = sess["awesome"]? - arr = sess["arr"]? - sess["who"] = "Kemal" - sess["age"] = 2016 - sess["velocity"] = 9999.9 - sess["awesome"] = true - "Hello" - end - - request = HTTP::Request.new("GET", "/") - response = call_request_on_app(request) - request = HTTP::Request.new("GET", "/", response.headers) - response = call_request_on_app(request) - - who.should eq "Kemal" - age.should eq 2016 - velocity.should eq 9999.9 - awesome.should eq true - end -end diff --git a/src/kemal/context.cr b/src/kemal/context.cr index 26836e8..0ca9af7 100644 --- a/src/kemal/context.cr +++ b/src/kemal/context.cr @@ -29,11 +29,6 @@ class HTTP::Server route_lookup.found? end - def session - @session ||= Kemal::Sessions.new(self) - @session.not_nil! - end - def get(name) @store[name] end diff --git a/src/kemal/middleware/csrf.cr b/src/kemal/middleware/csrf.cr deleted file mode 100644 index c350303..0000000 --- a/src/kemal/middleware/csrf.cr +++ /dev/null @@ -1,44 +0,0 @@ -require "secure_random" - -module Kemal::Middleware - # This middleware adds CSRF protection to your application. - # - # Returns 403 "Forbidden" unless the current CSRF token is submitted - # with any non-GET/HEAD request. - # - # Without CSRF protection, your app is vulnerable to replay attacks - # where an attacker can re-submit a form. - # - class CSRF < HTTP::Handler - HEADER = "X_CSRF_TOKEN" - ALLOWED_METHODS = %w(GET HEAD OPTIONS TRACE) - PARAMETER_NAME = "authenticity_token" - - def call(context) - unless context.session["csrf"]? - context.session["csrf"] = SecureRandom.hex(16) - end - - return call_next(context) if ALLOWED_METHODS.includes?(context.request.method) - - req = context.request - submitted = if req.headers[HEADER]? - req.headers[HEADER] - elsif context.params.body[PARAMETER_NAME]? - context.params.body[PARAMETER_NAME] - else - "nothing" - end - current_token = context.session["csrf"] - - if current_token == submitted - # reset the token so it can't be used again - context.session["csrf"] = SecureRandom.hex(16) - return call_next(context) - else - context.response.status_code = 403 - context.response.print "Forbidden" - end - end - end -end