imroc / req

Simple Go HTTP client with Black Magic
https://req.cool
MIT License
4.12k stars 334 forks source link

进行HTTP摘要认证出错,没有qop参数的时候会报错 #269

Open DarkiT opened 10 months ago

DarkiT commented 10 months ago

例如使用HTTP摘要认证登陆华为设备会提示:digest: qop must be specified 华为设备返回的摘要信息如下: WWW-Authenticate: Digest realm="Huawei",nonce="66f38e5ec6ebedc7c65c16f1c0ea3f46", algorithm=SHA-256

imroc commented 10 months ago

看起来设备没遵循RFC规范,qop是WWW-Authenticate响应中必带的参数:

qop

This parameter MUST be used by all implementations. It is a quoted string of one or more tokens indicating the "quality of protection" values supported by the server. The value "auth" indicates authentication; the value "auth-int" indicates authentication with integrity protection. See the descriptions below for calculating the response parameter value for the application of this choice. Unrecognized options MUST be ignored.

参考:https://httpwg.org/specs/rfc7616.html#authorization.request.header.field

imroc commented 10 months ago

我在想,为了提升使用的便利性,req也不必遵循规范,当响应中没有 qop 参数时,默认指定为 auth

imroc commented 10 months ago

已经release了个小版本,你可以升级试下

DarkiT commented 10 months ago

试了下,不行,我提交了一个Pull请求来处理了这个问题,您看看 更新

imroc commented 10 months ago

@DarkiT 最新版做了微调,没用正则,试试看

DarkiT commented 10 months ago

@imroc 获取摘要参数没问题,validateQop() 和 resp() 方法需要修改下,不然计算出来的摘要信息过不了验证。


func (c *credentials) validateQop() error {
    if c.messageQop == "" {
        return nil
    }
    possibleQops := strings.Split(c.messageQop, ", ")
    var authSupport bool
    for _, qop := range possibleQops {
        if qop == "auth" {
            authSupport = true
            break
        }
    }
    if !authSupport {
        return errDigestQopNotSupported
    }

    return nil
}

func (c *credentials) resp() (string, error) {
    c.nc++

    b := make([]byte, 16)
    _, err := io.ReadFull(rand.Reader, b)
    if err != nil {
        return "", err
    }
    c.cNonce = fmt.Sprintf("%x", b)[:32]

    ha1 := c.ha1()
    ha2 := c.ha2()

    if len(c.messageQop) == 0 {
        return c.h(fmt.Sprintf("%s:%s:%s", ha1, c.nonce, ha2)), nil
    }
    return c.kd(ha1, fmt.Sprintf("%s:%08x:%s:%s:%s",
        c.nonce, c.nc, c.cNonce, c.messageQop, ha2)), nil
}
imroc commented 10 months ago

@DarkiT 默认qop已经赋值auth了,相当于默认按auth方式计算摘要,怎么会过不了验证呢

DarkiT commented 10 months ago

主要是这种不标准的摘要在摄像头及流媒体认证里面使用的很广 ,跟常见的网站摘要认证有区别,比如海康、华为的摄像头北向接口基本上都是这种摘要认证,是没有qop参数的,都是这样的: WWW-Authenticate: Digest realm="Huawei",nonce="66f38e5ec6ebedc7c65c16f1c0ea3f46", algorithm=SHA-256 他们计算摘要的方式略有有点区别,只传入了 HA1、NONCE、HA2进行计算。 c.h(fmt.Sprintf("%s:%s:%s", ha1, c.nonce, ha2))

所以我们要在validateQop()方法这里判断messageQop为空也是允许的,不能给他指定auth,然后在resp()方法里面根据messageQop来判断使用哪种方式计算摘要信息。

imroc commented 10 months ago

明白了

imroc commented 10 months ago

@DarkiT 又 release了小版本,试试