Fix SSLError in django request to external API - django

So I have a couple sites made with django and never seen this type of error before.
this site presents data from a API, so I hooked the API up with
try:
r = requests.post(url, data=json.dumps(dados), headers=headers, timeout=30)
except Timeout:
raise EmptyResultSet(f'Erro API TIMEOUT')
if r.status_code == requests.codes.ok:
search = r.json()
else:
search = []
So, I hook he request with the API server, and check for a timeout so django sends me an e-mail about that (with the EmptyResultSet because the site can't display properly when no data is received)
then, if the code is ok it gets the data, and if there is an error it sets search = [] which gets data from a cache later in the code
this snippet was working normally, but then my production server started receiving this error:
HTTPSConnectionPool(host='****', port=443): Max retries exceeded with url: /api/Site/Busca (Caused by SSLError(SSLError('No cipher can be selected.')))
host hidden for safety
So, in local machines the site runs just fine and the people behind the API said my server isn't blacklisted this time, so I don't know where to search for some solution. Django version is 3.2.14, requests is 2.28.1 and urllib3 is 1.26.11

Related

Flask API, browser requests stopped working, no log the request was received

I have a very simple flask app that has been working for years, but last week requests from the built app return a 500, and from the Flask side, I can't even see the request. I am not seeing an OPTIONS request.
The below lines worked previously to keep CORS happy.
#app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,Auth-Token')
response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
return response
I have tried in a few browsers and all of them fail to successfully make any requests. Since the server doesn't even acknowledge the request has been made, I am not sure where to trouble shoot. I did confirm the app returns data as expected when I use Postman to make the request, as well as confirming that if I use the app locally (gulp serve on my computer) that the requests are successful. I have to believe its CORS, but what might I have to add / do to get the browser to be happy? Thanks.
The solution to my problem was that chrome started to "restrict the ability of websites to communicate with devices on the local network"
Communicating from Chrome 94+ with LAN devices that do not support HTTPS from a web app

How to call external API in django application with username and password

I am trying to receive data from an API endpoint in my django application. I found the solution here: Django rest framework & external api.
But when I tried this solution I got error:
SSLError at /external-api
HTTPSConnectionPool(host='example.com', port=443): Max retries exceeded with url: /example (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')])")))
When I open the external API directly on browser, I need to provide the username and password of the account which has access to that API. But while calling through API I am not providing it anywhere.
I am not sure how to set the username and password in the URL or in the settings file to access the external API.
Edited: Also I am not sure what could be the reason behind the error.
Then the certificate on the remote host failed verification (search the error message for a number of causes). If you think that's fine, then you can pass turn off verification:
r = requests.get("https://example.com/consumers", timeout=10, verify=False)
Try this
import requests
from requests.auth import HTTPBasicAuth
response = requests.get('your-url', auth=HTTPBasicAuth('your-username', 'your-password'))
print(response)

File upload to third party API with HTTP 307 Temporary Redirect in Flask

I have a scenario where I have to upload a file from flask app to a third party API. I have wrapped all API requests in Flask to control API usage. For this I have redirected the traffic from main route towards the api wrapper route with http 307 status to preserve the request body and in the API wrapper I have used request to post into third party API endpoint.
The problem is only file < 100KB gets send through the redirection request, having a file larger than 100 KB gets somehow terminated in the sending phase.
Is there any limit in the 307 redirection and payload size?
I tried debugging by watching the network timing stack trace, from there it seems the request is dropped in the sending phase.
Main Blueprint
#main.route('/upload/',methods=['POST','GET'])
def upload():
#for ajax call
if request.method == 'POST'
return redirect(url_for('api.file_push'),code=307)
else:
return render_template('file-upload.html')
API Blueprint
#api.route('/upload/',methods=['POST'])
def file_push():
upload_file = request.files['file']
filename = urllib.parse.quote(upload_file.filename)
toUpload = upload_file.read()
result=requests.post(apiInterfaces.FILE_UPLOAD_INTERFACE+'/'+filename,files{'file':toUpload})
return result
Yes, I can directly send post request to API endpoint from main route but I don't want to, it will destroy my system design and architecture.
I assume you're using Python, and possibly requests so this answer will be based on what I've learned figuring this out (debugging with a colleague). I filed a bug report with psf/requests. There is a related answer here which confirms my suspicions.
It seems that when you initiate a PUT request using requests (and urllib3), the entire request is sent before a response from the server is looked at, but some servers can send a HTTP 307 during this time. One of two things happen:
the server closes the connection by sending the response, even if the client has not finished sending the entire file. In this case, the client might see a closed connection and you won't have a response you can use for redirect (this happens with urllib3>1.26.10 (roughly)) but requests is not handling this situation correctly
the server sends the response and you re-upload the file to the second location (urllib3==1.26.3 has this behavior when using requests). Technically, there is a bug in urllib3 and it should have failed, but silently lets you upload...
However, it seems that if you are expecting a redirect, the most obvious solution might be to send a null byte via PUT first, get a valid response back for the new URL [don't follow redirects], and then use that response to do the PUT of the full file. With requests, it's probably something like
>>> import requests
>>> from io import BytesIO
>>> data = BytesIO(b'\x00')
>>> response = request.put(url, data=data, allow_redirects=False)
>>> request.put(response.headers['Location'], data=fp, allow_redirects=False)
and at that point, you'll be ok (assuming you only expect a single redirect here).

Unity 2017.3 HTTP POST request always empty when it hits web app (Django)

I'm trying to use Unity 2017.3 to send a basic HTTP POST request from Unity scripting. I want to send an image data, which I can access in my script either as file.png or in a byte[]. I am only posting to a local server running Python/Django, and the server does register the POST request coming in - but no matter what I do the request arrives empty of any content, body, files or raw data at my web app.
IEnumerator WriteAndSendPng() {
#extra code that gets bytes from a render texture goes here
#can verify that drawing.png is a valid image for my purposes
System.IO.File.WriteAllBytes("drawing.png", bytes);
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add( new MultipartFormFileSection ("drawing", bytes, "drawing.png", "image/png") );
UnityWebRequest www = UnityWebRequest.Post("http://127.0.0.1:8000/predict/", formData);
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) Debug.Log(www.error);
else Debug.Log("Form upload complete!");
}
I'm following the docs and there are a bunch of constructors for MultipartFormFileSection, most of which I feel like I've tried. Also tried UploadHandlers, or the old AddBinaryField WWW API - still the request is always empty when it hits my app... I've read the thorough response to this SO ticket, Sending http requests in C# with Unity. I have tried many of the implementations here but again, Django receives empty requests. Even submitting the simplest possible request from Unity sends empty requests. So weird.
List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
formData.Add (new MultipartFormDataSection ("someVar=something"));
The Python server just sees:
[11/Feb/2018 14:14:12] "POST /predict/ HTTP/1.1" 200 1
>>> print(request.method) # POST
>>> print(request.encoding) # None
>>> print(request.content_type) #multipart/form-data
>>> print(request.POST) # <QueryDict: {}>
>>> print(request.body) # b''
I thought it might be a Django issue, but posting to the same app w/ Postman or from other sources, it sees the incoming data just fine. Anyone done this recently? I thought this would be a piece of cake and many hours into into it I remain stymied. All help appreciated! Thanks, all.
Figured it out courtesy of Unity staffer Aurimas-Cernius on their forums: "The issue most likely is that your Django server does not support HTTP 1.1 chunked transfer. You can either try updating your server, or set chunkedTransfer property on UnityWebRequest to false."
He was right. Setting that flag to false allowed me to start sending simple test case data and receiving it as expected in the Django app - bet I'll be able to get images working in no time. I was also experiencing side effects of using Python 3.5.x (mistakenly assuming I needed to). Upgrading that fixed the chunk issue, too. Cheers!

Login using python request module on a aspx webpage

I've being trying to log in to this web page but I fail every time. This is the code i used
import requests
headers = {'User-Agent': 'Chrome'}
payload = {'_GlobalLoginControl$UserLogin':'myUser','_GlobalLoginControl$Password':'myPass'}
s = requests.Session()
r = s.post('https://www.scadalynx.com/GlobalLogin.aspx',headers=headers,data=payload)
r = s.get('https://www.scadalynx.com/Default.aspx')
print r.url
The result I get from: print r.url is this:
https://www.scadalynx.com/GlobalLogin.aspx?Timeout=Y
You can't.
The main problem is, your payload isn't complete. Check chrome's networking tab. There are much more required payloads.
ScriptMgr:_GlobalLoginControl$UpdatePanel1|_GlobalLoginControl$LoginBtn
ScriptMgr_HiddenField:;;AjaxControlToolkit, Version=4.1.40412.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e:en-US:2d0688b9-5fe7-418f-aeb1-6ecaa4dca45f:475a4ef5:effe2a26:751cdd15:5546a2b:dfad98a5:1d3ed089:497ef277:a43b07eb:3cf12cf1
__EVENTTARGET:
__EVENTARGUMENT:
__VIEWSTATE:/wEPDwUKMTQxMjQ3NTE5MA9kFgICAQ8WAh4Ib25zdWJtaXQFkgFpZiAoJGdldCgnX0dsb2JhbExvZ2luQ29udHJvbF9QYXNzd29yZCcpICE9IG51bGwpICRnZXQoJ19HbG9iYWxMb2dpbkNvbnRyb2xfUGFzc3dvcmQnKS52YWx1ZSA9IGVzY2FwZSgkZ2V0KCdfR2xvYmFsTG9naW5Db250cm9sX1Bhc3N3b3JkJykudmFsdWUpOxYEAgEPZBYCZg9kFgICBQ9kFgICAg9kFgICAQ9kFgJmD2QWCgIND2QWAgIJDw9kFgIeB29uY2xpY2sFugFqYXZhc2NyaXB0OmlmKCRnZXQoJ19HbG9iYWxMb2dpbkNvbnRyb2xfX0ZvcmdvdFBhc3N3b3JkRU1haWxUZXh0Qm94JykgIT0gbnVsbCkkZ2V0KCdfR2xvYmFsTG9naW5Db250cm9sX19Gb3Jnb3RQYXNzd29yZEVNYWlsVGV4dEJveCcpLnZhbHVlID0gJGdldCgnX0dsb2JhbExvZ2luQ29udHJvbF9Vc2VyTG9naW4nKS52YWx1ZTtkAg8PZBYCAgUPEGRkFgBkAhEPD2QWAh8BBZUDamF2YXNjcmlwdDppZigkZ2V0KCdfR2xvYmFsTG9naW5Db250cm9sX1VzZXJMb2dpblZhbGlkYXRvcicpICE9IG51bGwpJGdldCgnX0dsb2JhbExvZ2luQ29udHJvbF9Vc2VyTG9naW5WYWxpZGF0b3InKS5lbmFibGVkID0gdHJ1ZTtpZigkZ2V0KCdfR2xvYmFsTG9naW5Db250cm9sX19Vc2VyTG9naW5SZWd1bGFyRXhwcmVzc2lvblZhbGlkYXRvcicpICE9IG51bGwpJGdldCgnX0dsb2JhbExvZ2luQ29udHJvbF9fVXNlckxvZ2luUmVndWxhckV4cHJlc3Npb25WYWxpZGF0b3InKS5lbmFibGVkID0gdHJ1ZTtpZigkZ2V0KCdfR2xvYmFsTG9naW5Db250cm9sX1Bhc3N3b3JkVmFsaWRhdG9yJykgIT0gbnVsbCkkZ2V0KCdfR2xvYmFsTG9naW5Db250cm9sX1Bhc3N3b3JkVmFsaWRhdG9yJykuZW5hYmxlZCA9IHRydWU7ZAITD2QWAgIBDw8WAh4HVmlzaWJsZWhkZAIVD2QWBAIBDw9kFgIfAQUtJGdldCgnX0dsb2JhbExvZ2luQ29udHJvbF9QYXNzd29yZCcpLmZvY3VzKCk7ZAILDw9kFgIfAQVhamF2YXNjcmlwdDokZ2V0KCdfR2xvYmFsTG9naW5Db250cm9sX19Gb3Jnb3RQYXNzd29yZEVNYWlsUmVxdWlyZWRGaWVsZFZhbGlkYXRvcicpLmVuYWJsZWQgPSB0cnVlO2QCAg8PFgIfAmhkFgICAw8WAh4LXyFJdGVtQ291bnRmZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAwUpX0dsb2JhbExvZ2luQ29udHJvbCRSZW1lbWJlckxvZ2luQ2hlY2tCb3gFK19HbG9iYWxMb2dpbkNvbnRyb2wkX0ZvcmdvdFBhc3N3b3JkQ2xvc2VCdG4FEV9FcnJvckltYWdlQnV0dG9uIXu7XOl6z8WoghCWdElD7kNBanI=
__VIEWSTATEGENERATOR:ABDC7715
__SCROLLPOSITIONX:0
__SCROLLPOSITIONY:0
__EVENTVALIDATION:/wEdAA8j+x15hTpBOEjDv1LxVan3AUijrFjxy9PpisoGxfMqnNduSMVw1RChh3aZsdCK82jXRUWkWThaqEhU3Gr5iw98GHoUhEtg6gp73QcFIR1tGEGQHmQGQos+5LR8l78kIyNCGm6wvkKBlG3Z3EngFWzmX3gMRUNTCvY9T8lfFGMsRkvp3s0LtAU9sya5EgaP5MNrqxxx0HTfWwHJy49saUYlPDg6OL5q3VoZ6biOkvIG8l/ujxMESq+8VmX4sGwXcQBJxOm7RbAd1IEojVITrtk4hx8VhfPuqTNrqWHRrUAMgBj1ffXkwiR7kcJxJ3ixy43iLukJszI09WI7xsAFyAKxG82PcA==
_GlobalLoginControl$ScrWidth:1536
_GlobalLoginControl$ScrHeight:864
_GlobalLoginControl$UserLogin:asdsad#asdas.com
_GlobalLoginControl$Password:asdasd
_GlobalLoginControl$PasswordStore:
_GlobalLoginControl$HiddenField1:
_GlobalLoginControl$_HiddenSessionContentID:
_ErrorHiddenField:
__ASYNCPOST:true
_GlobalLoginControl$LoginBtn:Login
Probably, you could outsource this (I think it isn't possible, you have to use selenium or get the page first and scrape the informations.
But check this topic: How to make HTTP POST on website that uses asp.net?
We considered that, the login should be pass-through with phantomjs/chrome with selenium, the you should pass the cookies and the headers to requests. After you pass the required informations for requests, you could work with request for the further steps.