Python请求401未经授权

时间:2020-06-18 00:28:16

标签: python-3.x authentication python-requests http-status-code-401

我正在尝试登录基于cloudflare服务器的网站。我绕过了使用cloudserver的登录问题,但是下一个停止点是尝试发送获取请求以访问某些登录后令牌的请求。

我的代码:

headers = {
          'authority': 'www.paf.es',
          'accept': 'application/json, text/plain, */*',
          'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36',
          'x-requested-with': 'XMLHttpRequest',
          'sec-fetch-site': 'same-origin',
          'sec-fetch-mode': 'cors',
          'sec-fetch-dest': 'empty',
          'referer': 'https://www.paf.es/my-paf',
          'accept-language': 'es-ES,es;q=0.9,en;q=0.8',
          'cookie': '__cfduid=d4247b3fce5d260d7c5257b5d65a572001592254859; com.paf.frontend.common.LocaleCookie=es_ES; _gcl_au=1.1.193127229.1592254863; _ga=GA1.2.484179826.1592254863; _gid=GA1.2.1016568931.1592254863; _fbp=fb.1.1592254863595.468566668; com.paf.frontend.cookiesAccepted=true; com.paf.frontend.common.device=desktop; __cf_bm=10366cb3cb936cf0d9188cdf37dfaf276961f164-1592257614-1800-AbXayDicXL3zBDecjcoUuzlv+Qb5YhjhhqZO6goD80+W/J7ahYM+mwNHdcav405NnNOcPxyErOcdvPzijcdXGhk=; BIGipServerprod01_pool=1067697930.22811.0000; _gat_UA-641842-15=1; com.paf.frontend.common.showMenu=account; _gali=loginButton; JSESSIONID=tuO580BlkBaMw5v3txOBy0v2hqQV-61ZBQCVRqdqcTEQN4-5Z6tuu0021151447412; com.paf.frontend.common.LoginTime=1592257626364; com.paf.frontend.common.LoggedIn=true; __cfruid=5fe9d18ceeda0612668c20982f65d634686cb526-1592227626; com.paf.frontend.common.LifeCycleCookie=HAS_LOGGED_IN; trackingParams={"_ga":{"value":"GA1.2.484179826.1592254863","expiration":1600033627916},"utm_nooverride":{"value":"1","expiration":1600033627916}}; com.paf.frontend.common.LocaleCookie=es_ES; JSESSIONID=m4650rp-X0aJYw_6b8-3ghQIOU-h1luYOQV4dAwZz6UGVc4RanvZ!151447412; com.paf.frontend.common.LoginTime=1592255494930; com.paf.frontend.common.LoggedIn=true; __cfruid=3ffb40834f313a4c2b4d351r24f9a946uu5f7db9-1592255495'
        }
cloudserver.get(url=url,headers=headers,data=json.dumps({}))

这将返回401作为答案:

The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.46) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity MAY include relevant diagnostic information.

及其标题是

{'Date': 'Wed, 17 Jun 2020 23:56:04 GMT', 'Content-Type': 'text/html; charset=UTF-8', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'CF-Ray': '5a50b4816fddd665-MAD', 'Set-Cookie': 'com.paf.frontend.common.LocaleCookie=es_ES; expires=Thu, 17-Jun-2021 23:56:04 GMT; path=/, com.paf.frontend.common.LoggedIn=; expires=Thu, 01-Jan-1970 01:00:00 GMT; path=/, __cf_bm=73f0a2b2216d419f8a19f3e8ff74e8eca2458229-1592438164-1800-AaPuIPRQVOJGvI9l1DBiMeXXmyczqpm7Owaf2XUHFqZ+FJ9PT44TdL4kxAU4FCOWDWQmztz9Ff1FTHrCcDQw88w=; path=/; expires=Thu, 18-Jun-20 00:26:04 GMT; domain=.paf.es; HttpOnly; Secure; SameSite=None', 'Strict-Transport-Security': 'max-age=15552000; includeSubDomains; preload', 'CF-Cache-Status': 'DYNAMIC', 'cf-request-id': '03664f24e50000d66506af8200000001', 'Expect-CT': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"', 'X-Content-Type-Options': 'nosniff', 'Vary': 'Accept-Encoding', 'Server': 'cloudflare', 'alt-svc': 'h3-27=":443"; ma=86400'}

好,所以我首先尝试进行身份验证。像这样

s.get(url=url,headers=headers,data=json.dumps({}),auth=HTTPBasicAuth('somemail@mail.com','password'))

我再次遇到相同的错误,即401,但这一次响应的标头确实具有我应该挑战的www-authenticate

{'Date': 'Wed, 17 Jun 2020 23:58:42 GMT', 'Content-Type': 'text/html; charset=UTF-8', 'Transfer-Encoding': 'chunked', 'Connection': 'keep-alive', 'CF-Ray': '5a50b85dd8fdd665-MAD', 'Strict-Transport-Security': 'max-age=15552000; includeSubDomains; preload', 'WWW-Authenticate': 'Basic realm="weblogic"', 'CF-Cache-Status': 'DYNAMIC', 'cf-request-id': '0366518ea80000d66506a1b200000001', 'Expect-CT': 'max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"', 'X-Content-Type-Options': 'nosniff', 'Set-Cookie': '__cf_bm=c153a7277cf7abbaa004cdb46da821906f04b7c6-1592438322-1800-AR+KhjkQM5NHTTqKA0scqXrUGJpKNOnG0ZASOG386zEILi28YAh28BV+pWYGXzly+7ptsr9TJSGurY2nBOTv75I=; path=/; expires=Thu, 18-Jun-20 00:28:42 GMT; domain=.paf.es; HttpOnly; Secure; SameSite=None', 'Vary': 'Accept-Encoding', 'Server': 'cloudflare', 'alt-svc': 'h3-27=":443"; ma=86400'}

从我读到的内容来看,它是基本的,这意味着我必须在标题中添加诸如'Authentication': "Basic fhsejdjsjejdsj"之类的键,该键是Basic之后的'username:password'base64编码字符串。

但是,除了我的标头之外,我再次发送了一个请求,但仍然停留在同一点,使用相同的401。

我的问题是,在“ WWW-Authenticate”中:“ Basic realm =“ weblogic””与域之后的内容有关系吗?我该如何克服这个问题并能够完成我的请求?

3 个答案:

答案 0 :(得分:2)

如果在这里对HTTP身份验证的理解不清楚,我将解释其工作原理。

  1. 您以通常的方式请求HTTP资源,其中包含您认为需要的所有标头

  2. 如果内容或区域(即领域)受密码保护,则服务器返回401错误。响应包含WWW-Authenticate标头,该标头告诉您响应服务器时必须使用哪种身份验证方法以及适用于哪个领域。

  3. 如果收到401,则将请求重复到服务器,但是将Authorization标头添加到请求。其内容根据使用的方法(即服务器在第一响应中请求的方法)而有所不同。对于基本身份验证方法,您的授权应使用单词Basic作为第一个“参数”,然后使用冒号“:”符号分隔用户名和密码,但是此字符串必须使用Base64编码。您可以这样做:

    headers [“ Authorization”] =“基本” +((“%s:%s”%(用户名,密码))。encode(“ base64”)

这旨在支持浏览器和GUI对话框。当浏览器收到401消息时,它会弹出一个对话框,询问您用户名和密码。当您单击“确定”时,它将重复添加了“授权”标头的请求。从这一点开始,浏览器将始终发送Authorization标头以继续登录,直到为另一个领域接收到另一个401。使用领域,以便浏览器可以根据领域服务器所请求的自动更改凭据来处理会话,而无需在每次请求相同领域时提示您再次输入它们。这是因为您可能使用不同的密码保护了文件的不同区域,或者同一服务器上有多个帐户,等等。

现在,出于安全和标准化的原因,服务器可能总是拒绝第一个请求,无论它是否已经收到Authorization标头。实际上,如果您在未请求WWW-Authenticate之前发送授权,则可能会将其视为安全漏洞。一些服务器将发送一次,并期望从此刻开始存在授权,并且仅在客户端的进一步请求中消失时才重复401。其他人将始终发送401,然后期待重复的请求。有些还会完全切断您的访问权限,如果您发送了错误的凭据,例如,您会收到403禁止访问。连续3次。另外,如果服务器使用的是基本身份验证,这通常意味着服务器使用的是HTTPS,而不是HTTP。由于发送的数据易于在HTTP链接上进行拦截和读取,因此,如果使用HTTP,则将要求使用其他一些加密方法来保持安全性。因此,如果您通过HTTP而非HTTPS使用基本方法发送Authorization标头,则服务器也可能拒绝您。

我上面描述的服务器功能(HTTP标准身份验证过程)是您有时进入并有时再次收到401的原因。您处理的方法是永远不要发送Authorization标头,除非您首先收到401。使用urllib处理此问题很容易,因为将为401 Unauthorized引发HTTPError()。因此,您只需:

def get (*args, **kwargs):
    user = kwargs.pop("user", "")
    pwd  = kwargs.pop("pwd", "")
    r = Request(*args, **kwargs) # A request with URL and headers and data
    try:
        u = urlopen(r)
    except HTTPError as e:
        if e.code==401 and "WWW-Authenticate" in e.headers:
            if not e.headers.get("WWW-Authenticate").lower().startswith("basic "):
                raise
            r.add_header("Authorization", "Basic "+(user+":"+pwd).encode("base64"))
            u = urlopen(r)
        else:
            raise
    c = u.read()
    u.close()
    return c

您需要查看服务器是每次发送401还是仅发送一次。当您使用请求时,必须使用不带try-except块的responses属性检查响应代码。或切换到stdlib urllib / urllib2。 这就是我要做的,因为您似乎并没有使用诸如会话之类的请求中的功能,而是每次手动发送Cookie。

这里的真正问题是,JSON需要什么身份验证,HTTP标头需要什么身份验证。都是必需的还是仅其中之一。您将只需要试验。在我看来,即使您仅通过数据发送凭据,该API也会执行HTTP身份验证。但是...

答案 1 :(得分:0)

我不确定100%确定您在这里使用哪个库,但是假设该库基于请求,则我的阅读方式是:

您尝试将请求发送到服务器,该请求失败,并显示401错误。这意味着您的请求缺少访问您尝试访问的内容所需的凭据。

因此,您发送了另一个使用用户名和密码的请求。

服务器回复另一个401错误,这仍然意味着同一件事:您的请求缺少访问您要访问的内容所需的凭据。

鉴于服务器提供的响应标头:'WWW-Authenticate': 'Basic realm="weblogic"',您正确地需要使用基本身份验证。

weblogic领域是服务器告诉您需要登录的区域。您的请求必须具有该服务器上该领域的有效用户名和密码。

再次假设您的库基于请求,则您不需要自己对base64进行任何操作,此调用:

s.get(url=url,headers=headers,data=json.dumps({}),auth=HTTPBasicAuth('somemail@mail.com','password'))

或此速记版本:

s.get(url=url,headers=headers,data=json.dumps({}),auth=('somemail@mail.com','password'))

将为您处理,但是您需要有效的用户名和密码组合。

答案 2 :(得分:0)

好,所以我尝试过的工作似乎部分起作用。回顾一下,我尝试了所有事情:

Authorization: Basic <token>添加到请求的标题中

Authorization: Bearer <token>添加到请求的标题中

在请求中使用auth=HTTPBasicAuth(username,password)

在请求中使用auth=HTTPDigestAuth(username,password)

似乎没有任何作用。提醒一下,我使用cloudscraper而不是常规请求,因为服务器处于cloudflare状态,否则返回403。

因此,实际的工作是重用登录尝试中使用的标头。所以代替这个

s=cloudscraper.create_scraper()
s.post(url=url,headers=headers,data=payload) #Being url the XHR for the login, headers the ones from postman, and  data a string with my actual username and password
s.get(url=url,headers=headers) #New url requests and new headers from postman

我做到了

s=cloudscraper.create_scraper()
s.post(url=url,headers=headers,data=payload)
s.get(url=url) #no headers here, just a reuse from the ones in the previous request

这不是完美的,因为有时它会起作用,有时却不会,因为没有原因并以完全相同的顺序发布完全相同的参数。但是,这是一个进步