在header中添加自定义属性防止CSRF

在header中添加自定义属性防止CSRF
一、 概述
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账等造成的问题包括:个人隐私泄露以及财产安全。 

二、目前行业常见的防御方式
1.验证Referer;
2.使用验证码,用户体验比较差;
3.使用CSRF token,又分为在form表单中添加隐藏域携带token和在header中添加自定义参数携带token;
4.在请求地址中添加token并验证。
三、具体实现步骤
本文对于二中第3种方式的在请求header中添加自定义参数进行csrf参数进行防御进行实战介绍,该方式适合使用ajax异步提交表单和请求时使用。
1)在服务端做全局的token生成,token为一次性令牌(one-time token)令牌周期依赖于session的生命周期
示例:

            $csrf_token = md5(uniqid(rand(), TRUE));
            $_SESSION['csrf_token_name'] = $csrf_token;

2) 在前端得到token值

<form method="POST" action='target.php'>
money:<input type='text' name='money'>
<input type='hidden' name='csrf_token_name' value="<?=$_SESSION['csrf_token_name'];?>">
bank:<input type='text' name='toBankId'>

<input type='submit' name='submit' value='submit'>
 </form>

为了书写方便,可以将<input type='hidden' name='csrf_token_name' value="<?=$_SESSION['csrf_token_name'];?>">
封装成一个方法,前端页面直接调用即可。

3)ajax提交表单数据
改写jQuery中以下部分

    if ( s.crossDomain == null ) {
            parts = rurl.exec( s.url.toLowerCase() );
            s.crossDomain = !!( parts &&
                ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
                    ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
                        ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
            );
        }

        // Convert data if not already a string
        if ( s.data && s.processData && typeof s.data !== "string" ) {
            if(s.type.toUpperCase()=="POST"){
                if(s.data && s.data['csrf_token_name'] != undefined){
                    s.headers =  s.headers || {},
                    s.headers['Csrf-Token-Name'] = s.data['csrf_token_name'];
                    delete s.data['csrf_token_name'];
                }
            }

            s.data = jQuery.param( s.data, s.traditional );
        }

这样可以全局获取token并且将token附在header中Csrf-Token-Name上,提交至服务端。
4)服务端进行验证

        if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST')
        {
            return true;
        }
        if($this->is_need_check_csrf()==false){
            return true;
        }
        $session_hash = $_SESSION['csrf_token_name'];
  $request_method = 2;

        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) &&  $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
           $request_method = 1;
            $_scrf_key = "HTTP_Csrf_Token_Name";
            $valid = isset($_SERVER[$_scrf_key], $session_hash) && hash_equals($_SERVER[$_scrf_key],$session_hash);

        }
        else {
            $valid = isset($_POST['csrf_token_name'], $session_hash)
                && hash_equals($_POST['csrf_token_name'], $session_hash);
unset($_POST['csrf_token_name']);

        }
        csrf_regenerate(); 

        if ($valid !== TRUE)
        {
            if($request_method == 1){
                echo json_encode(array("statusCode"=>500,"errors"=>"页面停留时间过长,请刷新页面"));
                exit;
            }else if($request_method ==2){
                if(isset($_SERVER['HTTP_REFERER'])){
                    $referer=$_SERVER['HTTP_REFERER'];
                    header("Location: $referer");
                    exit;
                }else{
                    header("Location: /");
                    exit;
                }
            }
        }

验证请求是否合法,对于ajax请求返回json数据。
5)前端接收后端返回数据
如果请求验证通过,进行后续的业务处理,如果验证不通过,返回500状态码,前端页面进行刷新操作,提示停留时间过长造成的(可能的原因是长时间没操作,session过期,或者同一个页面多次打开,该token值已被其他tab使用过了),此时系统自动刷新当前页面。对于请求验证通过,但是页面不成功的情况,需要用户再次进行操作,此时需要请求回带新的token(x_token)回来。

// If not modified
                if ( status === 304 ) {

                    statusText = "notmodified";
                    isSuccess = true;

                // If we have data
                } else {

                    try {
                        success = ajaxConvert( s, response );
                        statusText = "success";
                        isSuccess = true;
                    } catch(e) {
                        // We have a parsererror
                        statusText = "parsererror";
                        error = e;
                    }
                }
                var t = JSON.stringify(response);
                if(t && t.statusCode != undefined && t.statusCode!=200 && t.data!=undefined && t.data.x_token != undefined){
                    $("input[name=csrf_token_name]").val(t.data.x_token);
                }
                if(t && t.statusCode != undefined && t.statusCode == 500){
                   setTimeout(function() {
                        location.reload();
                    }, 1000);
                }
<think>好的,我现在需要解决如何在HTTP请求头中正确添加token认证的问题。用户提到遇到了在请求头中无法添加token的情况,我需要根据提供的引用资料和已有的知识来找出解决方法。 首先,我要回顾一下用户提供的引用内容。引用[1]提到可以通过XMLHttpRequest在HTTP头中自定义属性,比如加入csrftoken,并且这种方法避免了将token放在URL参数中的不便,同时防止Referer泄露。引用[2]展示了客户端在访问接口时成功将token写入请求头,服务端也能正确接收。引用[3]和[4]则涉及JWT的结构和如何在Java中使用,提到token由Header、Payload、Signature三部分组成,客户端需要在header中加入token,服务器通过拦截器验证。 接下来,我需要分析用户可能遇到的问题场景。无法添加token可能有几种情况:前端代码未正确设置请求头、后端未正确解析请求头、或者跨域问题导致请求头被拦截。例如,用户可能在用JavaScript发送请求时,没有正确设置headers,或者在跨域请求时,服务器没有允许Authorization头。 然后,根据引用资料中的解决方案,分步骤解决。首先,前端部分需要使用XMLHttpRequest或者Fetch API来设置请求头,添加token。例如,在JavaScript中,设置headers的Authorization字段为Bearer加上token。引用[1]和[3]都提到了这一点。同时要注意,如果是跨域请求,需要确保服务器设置了CORS策略,允许相应的头信息,比如Access-Control-Allow-Headers包含Authorization。 接着,后端需要验证token。服务器端应配置拦截器或中间件,从请求头中提取token,并进行解密和验证。引用[3]提到使用拦截器方式获取header中的token,并进行校验。例如,在Spring Boot中,可以使用JWT库解析token,检查签名是否有效,是否过期等。 此外,需要注意token的生成是否符合JWT标准,如引用[4]所述,确保Header、Payload、Signature正确生成,并且签名部分使用正确的密钥。如果生成token时出现问题,可能导致后端无法解析。 可能的错误点包括:前端未正确设置请求头字段名称(比如大小写不一致),token未正确拼接Bearer前缀,或者服务器CORS配置未包含必要头信息。需要逐一排查这些方面。 最后,给出具体的代码示例,帮助用户理解如何在前端和后端实现token的添加和验证。同时,提醒用户注意安全措施,如使用HTTPS传输、定期更换密钥等,以防止token泄露或被篡改。</think>### 如何在HTTP请求头中正确添加Token认证? #### 一、添加Token的核心步骤 1. **生成Token** 使用标准方法(如JWT)生成包含Header、Payload、Signature的Token字符串[^4]。 示例生成JWT Token的Python代码: ```python import jwt secret_key = "your-secret-key" payload = {"user_id": 123} token = jwt.encode(payload, secret_key, algorithm="HS256") ``` 2. **前端设置请求头** - **XMLHttpRequest(传统方式)** 通过`setRequestHeader`方法添加`Authorization`字段: ```javascript var xhr = new XMLHttpRequest(); xhr.open("GET", "https://api.example.com/data"); xhr.setRequestHeader("Authorization", "Bearer " + token); xhr.send(); ``` - **Fetch API(现代方式)** 在`headers`属性中定义: ```javascript fetch("https://api.example.com/data", { headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" } }); ``` > 注意:`Bearer`是常见前缀,需与后端约定格式[^3]。 3. **后端验证Token** - **拦截请求**:通过中间件或拦截器读取请求头中的Token。 - **解析验证**:使用JWT库检查签名、过期时间等。 示例(Node.js Express): ```javascript const jwt = require('jsonwebtoken'); app.use((req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; try { const decoded = jwt.verify(token, secret_key); req.user = decoded; next(); } catch (err) { res.status(401).send("Invalid token"); } }); ``` #### 二、常见问题排查 | 问题现象 | 可能原因 | 解决方案 | |---------|----------|----------| | 请求头未携带Token | 前端未正确设置Header | 检查代码是否调用`setRequestHeader`或`headers`属性 | | 后端返回`401 Unauthorized` | Token格式错误或过期 | 检查是否包含`Bearer`前缀,验证Token有效期 | | 跨域请求失败(CORS) | 服务器未允许`Authorization`头 | 后端需设置:`Access-Control-Allow-Headers: Authorization`[^1] | #### 三、安全建议 - **HTTPS传输**:避免Token在明文传输中被截获。 - **短期有效期**:设置Token过期时间(如1小时)。 - **存储安全**:前端避免将Token存在LocalStorage(易受XSS攻击),推荐使用HttpOnly Cookie。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值