You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/03/13 11:34:05 UTC

[apisix] branch master updated: feat: add expires timestamp for CSRF plugin (#6201)

This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git


The following commit(s) were added to refs/heads/master by this push:
     new cd3e7cd  feat: add expires timestamp for CSRF plugin (#6201)
cd3e7cd is described below

commit cd3e7cdb490071d5231490b623e6b35a5fc985ac
Author: Baoyuan <ba...@gmail.com>
AuthorDate: Sun Mar 13 19:34:00 2022 +0800

    feat: add expires timestamp for CSRF plugin (#6201)
---
 apisix/plugins/csrf.lua        |  10 ++-
 docs/en/latest/plugins/csrf.md |   2 +
 docs/zh/latest/plugins/csrf.md |   2 +
 t/plugin/csrf.t                | 179 +++++++++++++++++++++++++++++++++++++++--
 4 files changed, 186 insertions(+), 7 deletions(-)

diff --git a/apisix/plugins/csrf.lua b/apisix/plugins/csrf.lua
index 40ea519..233fe1d 100644
--- a/apisix/plugins/csrf.lua
+++ b/apisix/plugins/csrf.lua
@@ -75,11 +75,12 @@ end
 
 local function gen_csrf_token(conf)
     local random = math.random()
-    local sign = gen_sign(random, conf.expires, conf.key)
+    local timestamp = ngx_time()
+    local sign = gen_sign(random, timestamp, conf.key)
 
     local token = {
         random = random,
-        expires = conf.expires,
+        expires = timestamp,
         sign = sign,
     }
 
@@ -112,6 +113,11 @@ local function check_csrf_token(conf, ctx, token)
         core.log.error("no expires in token")
         return false
     end
+    local time_now = ngx_time()
+    if conf.expires > 0 and time_now - expires > conf.expires then
+        core.log.error("token has expired")
+        return false
+    end
 
     local sign = gen_sign(random, expires, conf.key)
     if token_table["sign"] ~= sign then
diff --git a/docs/en/latest/plugins/csrf.md b/docs/en/latest/plugins/csrf.md
index 8f2efe3..5300d6e 100644
--- a/docs/en/latest/plugins/csrf.md
+++ b/docs/en/latest/plugins/csrf.md
@@ -35,6 +35,8 @@ In the following we define `GET`, `HEAD` and `OPTIONS` as the `safe-methods` and
 | expires |  number | optional | `7200` | | Expiration time(s) of csrf cookie. |
 | key | string | required |  |  | The secret key used to encrypt the cookie. |
 
+**Note: When expires is set to 0 the plugin will ignore checking if the token is expired or not.**
+
 ## How To Enable
 
 1. Create the route and enable the plugin.
diff --git a/docs/zh/latest/plugins/csrf.md b/docs/zh/latest/plugins/csrf.md
index a007cbf..9fdea38 100644
--- a/docs/zh/latest/plugins/csrf.md
+++ b/docs/zh/latest/plugins/csrf.md
@@ -35,6 +35,8 @@ title: csrf
 | expires |  number | optional | `7200` | | CSRF Cookie 的过期时间(秒) |
 | key | string | required |  |  | 加密 token 的秘钥 |
 
+**注意:当 expires 设置为 0 时插件将忽略检查 Token 是否过期**
+
 ## 如何启用
 
 1. 创建一条路由并启用该插件。
diff --git a/t/plugin/csrf.t b/t/plugin/csrf.t
index 2f293ac..2bbd700 100644
--- a/t/plugin/csrf.t
+++ b/t/plugin/csrf.t
@@ -73,7 +73,8 @@ done
                     },
                     "plugins": {
                         "csrf": {
-                            "key": "userkey"
+                            "key": "userkey",
+                            "expires": 1000000000
                         }
                     }
                 }]]
@@ -145,8 +146,8 @@ Cookie: apisix-csrf-token=testcookie
 --- request
 POST /hello
 --- more_headers
-apisix-csrf-token: eyJleHBpcmVzIjo3MjAwLCJyYW5kb20iOjAuMjE2ODAxOTYyNTEwNDEsInNpZ24iOiJqZnhDckk1TVwvMHI3VjdyWWRBSXNCeEg3emljY3VnV0dySGtYQkZ0QT0ifQ==
-Cookie: apisix-csrf-token=eyJleHBpcmVzIjo3MjAwLCJyYW5kb20iOjAuMjE2ODAxOTYyNTEwNDEsInNpZ24iOiJqZnhDckk1TVwvMHI3VjdyWWRBSXNCeEg3emljY3VnV0dySGtYQkZ0QT0ifQ==
+apisix-csrf-token: eyJyYW5kb20iOjAuMTYwOTgzMDYwMTg0NDksInNpZ24iOiI2YTEyYmViYTI4MzAyNDg4MDRmNGU0N2VkZDY5MWFmNjg5N2IyNzQ4YTY1YWMwMDJiMGFjMzFlN2NlMDdlZTViIiwiZXhwaXJlcyI6MTc0MzExOTkxMX0=
+Cookie: apisix-csrf-token=eyJyYW5kb20iOjAuMTYwOTgzMDYwMTg0NDksInNpZ24iOiI2YTEyYmViYTI4MzAyNDg4MDRmNGU0N2VkZDY5MWFmNjg5N2IyNzQ4YTY1YWMwMDJiMGFjMzFlN2NlMDdlZTViIiwiZXhwaXJlcyI6MTc0MzExOTkxMX0=
 --- error_code: 401
 --- error_log: Invalid signatures
 --- response_body
@@ -158,5 +159,173 @@ Cookie: apisix-csrf-token=eyJleHBpcmVzIjo3MjAwLCJyYW5kb20iOjAuMjE2ODAxOTYyNTEwND
 --- request
 POST /hello
 --- more_headers
-apisix-csrf-token: eyJzaWduIjoiZTlhNWVkOTBmZDc2YjRhMTYyMzg1ZDU2Y2ZhZDI1N2MxNmI0MWY1MjFjZWUwODczNzExM2NlYzZkZDQwMWJmNyIsInJhbmRvbSI6MC4zNjcxNDg2NDI2MjE0MywiZXhwaXJlcyI6NzIwMH0=
-Cookie: apisix-csrf-token=eyJzaWduIjoiZTlhNWVkOTBmZDc2YjRhMTYyMzg1ZDU2Y2ZhZDI1N2MxNmI0MWY1MjFjZWUwODczNzExM2NlYzZkZDQwMWJmNyIsInJhbmRvbSI6MC4zNjcxNDg2NDI2MjE0MywiZXhwaXJlcyI6NzIwMH0=
+apisix-csrf-token: eyJyYW5kb20iOjAuNDI5ODYzMTk3MTYxMzksInNpZ24iOiI0ODRlMDY4NTkxMWQ5NmJhMDc5YzQ1ZGI0OTE2NmZkYjQ0ODhjODVkNWQ0NmE1Y2FhM2UwMmFhZDliNjE5OTQ2IiwiZXhwaXJlcyI6MjY0MzExOTYyNH0=
+Cookie: apisix-csrf-token=eyJyYW5kb20iOjAuNDI5ODYzMTk3MTYxMzksInNpZ24iOiI0ODRlMDY4NTkxMWQ5NmJhMDc5YzQ1ZGI0OTE2NmZkYjQ0ODhjODVkNWQ0NmE1Y2FhM2UwMmFhZDliNjE5OTQ2IiwiZXhwaXJlcyI6MjY0MzExOTYyNH0=
+
+
+
+=== TEST 10: change expired
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "csrf": {
+                            "key": "userkey",
+                            "expires": 1
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 11: expired csrf token
+--- request
+POST /hello
+--- more_headers
+apisix-csrf-token: eyJyYW5kb20iOjAuMDY3NjAxMDQwMDM5MzI4LCJzaWduIjoiOTE1Yjg2MjBhNTg1N2FjZmIzNjIxOTNhYWVlN2RkYjY5NmM0NWYwZjE5YjY5Zjg3NjM4ZTllNGNjNjYxYjQwNiIsImV4cGlyZXMiOjE2NDMxMjAxOTN9
+Cookie: apisix-csrf-token=eyJyYW5kb20iOjAuMDY3NjAxMDQwMDM5MzI4LCJzaWduIjoiOTE1Yjg2MjBhNTg1N2FjZmIzNjIxOTNhYWVlN2RkYjY5NmM0NWYwZjE5YjY5Zjg3NjM4ZTllNGNjNjYxYjQwNiIsImV4cGlyZXMiOjE2NDMxMjAxOTN9
+--- error_code: 401
+--- error_log: token has expired
+
+
+
+=== TEST 12: token has expired after sleep 2s
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/hello"
+
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri, {method = "GET"})
+            if not res then
+                ngx.say(err)
+                return
+            end
+            local cookie = res.headers["Set-Cookie"]
+            local token = cookie:match("=([^;]+)")
+
+            ngx.sleep(2)
+
+            local res, err = httpc:request_uri(uri, {
+                method = "POST",
+                headers = {
+                    ["apisix-csrf-token"] = token,
+                    ["Cookie"] = cookie,
+                }
+            })
+            if not res then
+                ngx.say(err)
+                return
+            end
+
+            if res.status >= 300 then
+                ngx.status = res.status
+            end
+        }
+    }
+--- error_code: 401
+--- error_log: token has expired
+
+
+
+=== TEST 13: set expires 0
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                    "uri": "/hello",
+                    "upstream": {
+                        "type": "roundrobin",
+                        "nodes": {
+                            "127.0.0.1:1980": 1
+                        }
+                    },
+                    "plugins": {
+                        "csrf": {
+                            "key": "userkey",
+                            "expires": 0
+                        }
+                    }
+                }]]
+            )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 14: token no expired after sleep 1s
+--- config
+    location /t {
+        content_by_lua_block {
+            local http = require "resty.http"
+
+            local uri = "http://127.0.0.1:" .. ngx.var.server_port
+                        .. "/hello"
+
+            local httpc = http.new()
+            local res, err = httpc:request_uri(uri, {method = "GET"})
+            if not res then
+                ngx.say(err)
+                return
+            end
+
+            ngx.sleep(1)
+
+            local cookie = res.headers["Set-Cookie"]
+            local token = cookie:match("=([^;]+)")
+
+            local res, err = httpc:request_uri(uri, {
+                method = "POST",
+                headers = {
+                    ["apisix-csrf-token"] = token,
+                    ["Cookie"] = cookie,
+                }
+            })
+            if not res then
+                ngx.say(err)
+                return
+            end
+
+            if res.status >= 300 then
+                ngx.status = res.status
+            end
+            ngx.status = res.status
+            ngx.print(res.body)
+        }
+    }
+--- response_body
+hello world