0

PYTHON

import requests

url = "https://REDACTED/pb/s/api/auth/login"

r = requests.post(
    url,
    data = {
        'username': 'username',
        'password': 'password'
    }
)

NIM

import httpclient, json

let client = newHttpClient()

client.headers = newHttpHeaders({ "Content-Type": "application/json" })
let body = %*{
    "username": "username",
    "password": "password"
}

let resp = client.request("https://REDACTED.com/pb/s/api/auth/login", httpMethod = httpPOST, body = $body)

echo resp.body

I'm calling an API to get some data. Running the python code I get the traceback below. However, the nim code works perfectly so there must be something wrong with the python code or setup.

I'm running Python version 2.7.15. requests lib version 2.19.1

Traceback (most recent call last):
  File "C:/Python27/testht.py", line 21, in <module>
    "Referer": "https://REDACTED.com/pb/a/"
  File "C:\Python27\lib\site-packages\requests\api.py", line 112, in post
    return request('post', url, data=data, json=json, **kwargs)
  File "C:\Python27\lib\site-packages\requests\api.py", line 58, in request
    return session.request(method=method, url=url, **kwargs)
  File "C:\Python27\lib\site-packages\requests\sessions.py", line 512, in request
    resp = self.send(prep, **send_kwargs)
  File "C:\Python27\lib\site-packages\requests\sessions.py", line 622, in send
    r = adapter.send(request, **kwargs)
  File "C:\Python27\lib\site-packages\requests\adapters.py", line 511, in send
    raise SSLError(e, request=request)
SSLError: HTTPSConnectionPool(host='REDACTED.com', port=443): Max retries exceeded with url: /pb/s/api/auth/login (Caused by SSLError(SSLError(1, u'[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:726)'),))
UrbKr
  • 621
  • 2
  • 11
  • 27
  • 1
    Your Nim code may not properly verify the certificate according to the standards. Without having the certificate at hand we have no way of knowing whether there is a field that could trigger this behaviour. – Bakuriu Oct 22 '18 at 17:58

2 Answers2

1

The requests module will verify the cert it gets from the server, much like a browser would. Rather than being able to click through and say "add exception" like you would in your browser, requests will raise that exception.

There's a way around it though: try adding verify=False to your post call.

wholevinski
  • 3,658
  • 17
  • 23
  • I'd rather fix the certificate/certificate store instead of just disabling certificate verification... – Bakuriu Oct 22 '18 at 17:59
  • True, but in a dev environment that might not be prudent and in a prod (as in not owned by you) environment that might be impossible. This will get him past that, but you're right, it's unsafe to always trust invalid certs. – wholevinski Oct 22 '18 at 18:00
  • Correct, Nim does not seem to verify the certificate. Verify=False does fix the issue, but that may be not what I should do, as Bakuriu says. – UrbKr Oct 22 '18 at 18:21
  • 1
    I'll have to consult with the people that provided me with the API regarding the certificate. I'll accept the answer when I do that – UrbKr Oct 22 '18 at 18:22
  • 1
    @UrbKr Like Bakuriu said in his comment on your question, getting the original cert would help as well. If it's an untrusted CA, you can add their CA cert as trusted. If it's something like a mismatched URL or the cert's expired, they definitely need to fix it on their end. – wholevinski Oct 22 '18 at 18:27
  • 1
    @UrbKr FYI, if you visit the URL in a browser, you can probably get more details about what's wrong with the cert. – wholevinski Oct 22 '18 at 18:28
1

However, the nim code works perfectly so there must be something wrong with the python code or setup.

Actually, your Python code or setup is less to blame but instead the nim code or better the defaults on the httpclient library. In the documentation for nim can be seen that httpclient.request uses a SSL context returned by getDefaultSSL by default which according to this code creates a context which does not verify the certificate:

proc getDefaultSSL(): SSLContext =
  result = defaultSslContext
  when defined(ssl):
    if result == nil:
      defaultSSLContext = newContext(verifyMode = CVerifyNone)

Your Python code instead attempts to properly verify the certificate since the requests library does this by default. And it fails to verify the certificate because something is wrong - either with your setup or the server.

It is unclear who has issued the certificate for your site but if it is not in your default CA store you can use the verify argument of requests to specify the issuer CA. See this documentation for details.

If the site you are trying to access works with the browser but fails with your program it might be that it uses a special CA which was added as trusted to the browser (like a company certificate). Browsers and Python use different trust stores so this added certificate needs to be added to Python or at least to your program as trusted too. It might also be that the setup of the server has problems. Browsers can sometimes work around problems like a missing intermediate certificate but Python doesn't. In case of a public accessible site you could use SSLLabs to check what's wrong.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172