生产事故-走近科学之消失的JWT

科技资讯 投稿 5300 0 评论

生产事故-走近科学之消失的JWT

注意:为了避免不必要的麻烦和商密问题,文中提到的特定名称都将是化名、代称。

0x00 大纲

目录
    0x00 大纲
  • 0x01 事故背景
  • 0x02 事故分析
  • 0x03 事故原因
  • 0x04 事故复盘
  • 0x05 事故影响

0x01 事故背景

0x02 事故分析

在 RFC 7519 规范中对于 JWT 是这样描述的:

JWT (JSON Web Token 是一种紧凑、URL 安全的表示方式,用于表达要在两个参与方之间传输的安全声明。JWT 中的声明被编码为 JSON 对象,作为 JSON Web Signature (JWS 结构的有效载荷或 JSON Web Encryption (JWE 结构的明文,使得声明可以使用消息认证码 (MAC 进行数字签名或完整性保护和/或加密。

    从故障现象来看,步骤①出问题的可能性基本被排除,从前端请求和后端日志来看账号和密码的验证过程已经正确完成;
  1. 那么步骤②有没有可能出问题呢?当时也是怀疑过的,但是使用浏览器的 F12 开发者工具,看到 login 的网络请求响应中已经将后端生成的 JWT 返回来了;
  2. 莫非是步骤③没有将 JWT 正确携带,导致后续验证不通过?但是查看登陆后,对其他接口的请求,里面确实已经携带了步骤②中提供的 JWT,而且数值也一致;
  3. 验证JWT的代码逻辑会不会有问题呢?可能性不大,因为在测试环境和 UAT 环境已经反复验证过。

随机抽取一个运维小伙子,让他说说生产的系统结构,从他口中得知,生产上除了为了部署多个节点,使用了 Nginx 作为负载均衡和反向代理外,其他地方没有区别。凭借往常的经验呢,P公司的员工们首先呢就没有怀疑过反代和负载会影响这个业务功能,但是我们的理性分析又提示我们问题很有可能出在这里。

Debug日志加上。然后果不出所料,前端虽然在请求头中携带了 JWT,但是到了后端,却显示没有这个信息,这个头,它丢到哪里去了呢?

0x03 事故原因

JWT_TOKEN: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbb2x0...

在后端日志中,除了 JWT_TOKEN 以外,其他的头部信息都正常传递,我们注意到,它的 HTTP_HEADER_NAME 包含了下划线,这是它与众不同的地方。难道是被 Nginx 过滤了?

Missing (disappearing HTTP Headers

消失的 HTTP Headers

在 ngx_http_parse.c 中,这个开关是这样处理的:

/* header name */
case sw_name:
    c = lowcase[ch];

    if (c {
        hash = ngx_hash(hash, c;
        r->lowcase_header[i++] = c;
        i &= (NGX_HTTP_LC_HEADER_LEN - 1;
        break;
    }

    if (ch == '_' {
        if (allow_underscores {
            hash = ngx_hash(hash, ch;
            r->lowcase_header[i++] = ch;
            i &= (NGX_HTTP_LC_HEADER_LEN - 1;

        } else {
            r->invalid_header = 1;
        }

        break;
    }
    // ……(太长只截取关键部分)
    break;

如果没有开启underscores_in_headers开关,对应变量allow_underscores,则默认情况下,带有下划线的 HTTP_HEADER 会被标记为 INVALID_HEADER.而标记为 INVALID_HEADER 的信息默认情况下,会被忽略掉,为什么说默认呢?因为这个行为同时还受到另一个开关ignore_invalid_headers控制,如果它被开启,那么带有下划线的 HTTP_HEADER 就真的神秘消失了。

Syntax: underscores_in_headers on | off;

Default: underscores_in_headers off;

Context: http, server

关于 ignore_invalid_headers 选项:

Syntax: ignore_invalid_headers on | off;

Default: ignore_invalid_headers on;

Context: http, server

可以看到underscores_in_headers选项默认情况下是关闭的,而ignore_invalid_headers选项默认情况下是开启的,这也就导致了我们 JWT_TOKEN 的神秘失踪,至此问题已经定位完毕。

0x04 事故复盘

    再穷也好,至少也要申请一个与生产环境相同/相仿的复刻环境。
  • 统一且规范的命名,或许可以避免很多不必要的麻烦。
  • 所谓Debug日志就是,没事的时候,你看到它嫌它烦;出事的时候,你烦看不到它……
  • 排查问题时,还是大意了,没有去看 Nginx 的日志,因为通过源码可以发现 INVALID_HEADER 默认情况下是会触发 ERROR 日志的:
    if (rc == NGX_OK {
    
        r->request_length += r->header_in->pos - r->header_name_start;
    
        if (r->invalid_header && cscf->ignore_invalid_headers {
    
            /* there was error while a header line parsing */
    
            ngx_log_error(NGX_LOG_INFO, c->log, 0,
                          "client sent invalid header line: \"%*s\"",
                          r->header_end - r->header_name_start,
                          r->header_name_start;
            continue;
        }
        // ……(太长只截取关键部分)
    }
    

0x05 事故影响

使P公司新业务系统上线时间延长了3小时,相关人员连夜跟老板申请服务器经费。(知道了,下次还是不批)。

编程笔记 » 生产事故-走近科学之消失的JWT

赞同 (30) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽