waf应对csrf攻击防护方案

常见检查 csrf token方式

在表单里隐藏一个随机变化的 token,当用户提交表单时,将这个 token 提交到后台进行验证,如验证通过则可以继续执行操作,这种情况有效的主要原因是网站 B 拿不到网站 A 表单里的 token。

当向服务器发出请求时生成一个随机值,将这个随机值既放在 cookie 中,也放在请求的参数中,服务器同时验证这两个值是否匹配。

csrf攻击防护方案

如果是post请求
{
    如果cookie_csrf有
    {
        连接redis以csrf_.. ngx.var.cookie_csrf为key查找
        如果找到了返回value和ok,否则 not found
        返回ok后从请求参数中get_post_args得到csrf字段,如果这个csrf字段value
        和从redis查找的value一致
        那么放行,ngx.var.csrf_validate = ngx.HTTP_OK
        然后proxy_set_header X-Csrf-Valid $csrf_validate; --把结果作为头传给backend
    }
}

请求 判断源站返回请求头是否有upstream_http_x_set_csrf
{
    判断cookie里是否有cookie_csrf
    如果没有random产生一个random_token,然后Set_Cookie到cookie的csrf=random_token,(key)
    然后再random生一个csrf_form_token,连接redis把csrf_random_token为key,value为csrf_form_token保存起来
    
    然后在body里增加csrf_form_token
    ngx.arg[1] = ngx.re.gsub(ngx.arg[1], "::csrf::", ngx.var.csrf_form_token)
}

配置及代码

server {
    listen 80;    
    root /root/docroot;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Request-URI $request_uri;
    proxy_set_header X-Backend example;
    proxy_pass_header Set-Cookie;

    location /validate-csrf {
        internal;
        content_by_lua
        '
        if ngx.var.cookie_csrf then
            -- 如果有csrf cookie则验证
            local redis = require "redis"
            local client = redis.connect("127.0.0.1", 6379)
            local csrf_cookie = "csrf_" .. ngx.var.cookie_csrf
            local value = client:get(csrf_cookie)
            if value then 
                ngx.say(value)
                return ngx.exit(ngx.HTTP_OK)
            end
        end
        -- not found cookie or csrf
        return ngx.exit(ngx.HTTP_NOT_FOUND)
        ';
    }

    location @backend {
        # 自定义变量
        set $csrf_validate "";
        access_by_lua 
        '
        if ngx.req.get_method() == "POST" then
            -- 默认设置长forbidden
            ngx.var.csrf_validate = ngx.HTTP_FORBIDDEN

            if ngx.var.cookie_csrf then
                local res = ngx.location.capture("/validate-csrf")
                if ngx.HTTP_OK == res.status then
                    ngx.req.read_body()
                    local args = ngx.req.get_post_args()
                    local posted_token = tostring(args["csrf"])
                    # 验证步骤
                    if posted_token == res.body then
                        ngx.var.csrf_validate = ngx.HTTP_OK
                    end
                end
            end
        end
        ';

        proxy_set_header X-Csrf-Valid $csrf_validate;
        
        proxy_pass http://127.0.0.1:6081;

        # 以尽可能轻量级的方式过滤来自后端的响应
        set $csrf_form_token "";
        set $csrf_cookie_token "";

        header_filter_by_lua
        '
        if ngx.var.upstream_http_x_set_csrf then
            -- 后端请求设置CSRFtoken
            local csrf_cookie_token = nil
            if ngx.var.cookie_csrf then
                local csrf_cookie_token = ngx.var.cookie_csrf
            end

            local resty_random = require "resty.random"
            local str = require "resty.string"

            if not csrf_cookie_token then
                -- 找不到有效的csrf cookie 手动产生一个
                
                local cookie_random = resty_random.bytes(16,true)

                while cookie_random == nil do
                    cookie_random = resty_random.bytes(16,true)
                end

                ngx.var.csrf_cookie_token = str.to_hex(cookie_random)
            end


            ngx.header.Content_Length = ""

            ngx.header.Set_Cookie = "csrf=" .. ngx.var.csrf_cookie_token

            --现在为表单token生成一个
            while form_random == nil do
                form_random = resty_random.bytes(16,true)
            end

            ngx.var.csrf_form_token = str.to_hex(form_random)
            -- 将这两个随机数保存到redis
            local redis = require "redis"
            local client = redis.connect("127.0.0.1", 6379) -- change as appropriate
            client:set("csrf_" .. ngx.var.csrf_cookie_token, ngx.var.csrf_form_token)
        end
        ';
        # 解析csrf body 并替换内容
        body_filter_by_lua
        '
        if ngx.var.csrf_form_token then
            ngx.arg[1] = ngx.re.gsub(ngx.arg[1], "::csrf::", ngx.var.csrf_form_token)
        end
        ';
    }
}

  

你可能感兴趣的:(应用安全,csrf,nginx,waf,web安全)