Python, HTTPS GET avec authentification de base

89

J'essaie de faire un HTTPS GET avec une authentification de base en utilisant python. Je suis très nouveau en python et les guides semblent utiliser différentes bibliothèques pour faire les choses. (http.client, httplib et urllib). Quelqu'un peut-il me montrer comment c'est fait? Comment pouvez-vous dire à la bibliothèque standard d'utiliser?

Tom Squires
la source
2
Voulez-vous vous assurer que le certificat est valide?
Andrew Cox
1
Consultez stackoverflow.com/questions/635113/… . Il semble couvrir exactement ce que vous recherchez.
Geo

Réponses:

120

Dans Python 3, ce qui suit fonctionnera. J'utilise le niveau inférieur http.client de la bibliothèque standard. Consultez également la section 2 de la rfc2617 pour plus de détails sur l'autorisation de base. Ce code ne vérifiera pas la validité du certificat, mais établira une connexion https. Consultez la documentation http.client pour savoir comment procéder.

from http.client import HTTPSConnection
from base64 import b64encode
#This sets up the https connection
c = HTTPSConnection("www.google.com")
#we need to base 64 encode it 
#and then decode it to acsii as python 3 stores it as a byte string
userAndPass = b64encode(b"username:password").decode("ascii")
headers = { 'Authorization' : 'Basic %s' %  userAndPass }
#then connect
c.request('GET', '/', headers=headers)
#get the response back
res = c.getresponse()
# at this point you could check the status etc
# this gets the page text
data = res.read()  
Andrew Cox
la source
5
La requestdocumentation de la méthode [1] mentionne que "les chaînes sont encodées comme" ISO-8859-1 ", le jeu de caractères par défaut pour HTTP". Je suggère donc de décoder avec "ISO-8859-1" au lieu de "ASCII". [1] docs.python.org/3/library/…
jgomo3
22
Pour utiliser des variables au lieu de b"username:password", utilisez: bytes(username + ':' + password, "utf-8").
kenorb
1
@ jgomo3: Ce .decode("ascii")n'est que pour la conversion bytes-> str. Le résultat de b64encodeest de toute façon ASCII uniquement.
Torsten Bronger
1
Mon Sauveur. Après 4 heures de lutte et une charge de manque de direction.
Conrad B le
Comment utiliser les informations d'identification par défaut?, Cela ne fonctionnera pas si j'exécute le code dans un autre système, non?
anandhu
91

Utilisez la puissance de Python et appuyez-vous sur l'une des meilleures bibliothèques du marché: les requêtes

import requests

r = requests.get('https://my.website.com/rest/path', auth=('myusername', 'mybasicpass'))
print(r.text)

La variable r (demande de réponse) a beaucoup plus de paramètres que vous pouvez utiliser. La meilleure chose à faire est de faire un saut dans l'interpréteur interactif et de jouer avec, et / ou de lire les documents des demandes .

ubuntu@hostname:/home/ubuntu$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> r = requests.get('https://my.website.com/rest/path', auth=('myusername', 'mybasicpass'))
>>> dir(r)
['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_content', '_content_consumed', 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'iter_content', 'iter_lines', 'json', 'links', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']
>>> r.content
b'{"battery_status":0,"margin_status":0,"timestamp_status":null,"req_status":0}'
>>> r.text
'{"battery_status":0,"margin_status":0,"timestamp_status":null,"req_status":0}'
>>> r.status_code
200
>>> r.headers
CaseInsensitiveDict({'x-powered-by': 'Express', 'content-length': '77', 'date': 'Fri, 20 May 2016 02:06:18 GMT', 'server': 'nginx/1.6.3', 'connection': 'keep-alive', 'content-type': 'application/json; charset=utf-8'})
IvanD
la source
23

Mise à jour: OP utilise Python 3. Ajout d'un exemple utilisant httplib2

import httplib2

h = httplib2.Http(".cache")

h.add_credentials('name', 'password') # Basic authentication

resp, content = h.request("https://host/path/to/resource", "POST", body="foobar")

Ce qui suit fonctionne pour python 2.6:

J'utilise pycurlbeaucoup en production pour un processus qui fait plus de 10 millions de demandes par jour.

Vous devrez d'abord importer les éléments suivants.

import pycurl
import cStringIO
import base64

Une partie de l'en-tête d'authentification de base se compose du nom d'utilisateur et du mot de passe codés en Base64.

headers = { 'Authorization' : 'Basic %s' % base64.b64encode("username:password") }

Dans l'en-tête HTTP, vous verrez cette ligne Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=. La chaîne codée change en fonction de votre nom d'utilisateur et de votre mot de passe.

Nous avons maintenant besoin d'un emplacement pour écrire notre réponse HTTP et d'un handle de connexion curl.

response = cStringIO.StringIO()
conn = pycurl.Curl()

Nous pouvons définir diverses options de curl. Pour une liste complète des options, voir ceci . La documentation liée concerne l'API libcurl, mais les options ne changent pas pour les autres liaisons de langage.

conn.setopt(pycurl.VERBOSE, 1)
conn.setopt(pycurlHTTPHEADER, ["%s: %s" % t for t in headers.items()])

conn.setopt(pycurl.URL, "https://host/path/to/resource")
conn.setopt(pycurl.POST, 1)

Si vous n'avez pas besoin de vérifier le certificat. Avertissement: ce n'est pas sûr. Similaire à exécuter curl -kou curl --insecure.

conn.setopt(pycurl.SSL_VERIFYPEER, False)
conn.setopt(pycurl.SSL_VERIFYHOST, False)

Appel cStringIO.writepour stocker la réponse HTTP.

conn.setopt(pycurl.WRITEFUNCTION, response.write)

Lorsque vous faites une demande POST.

post_body = "foobar"
conn.setopt(pycurl.POSTFIELDS, post_body)

Faites la demande réelle maintenant.

conn.perform()

Faites quelque chose en fonction du code de réponse HTTP.

http_code = conn.getinfo(pycurl.HTTP_CODE)
if http_code is 200:
   print response.getvalue()
Ocaj Nires
la source
Cela semble être pour pyhthon 2.5 im utilisant 3
Tom Squires
Utilisez-vous Easy Install ou Pip? Le package pycurl est-il indisponible pour python 3?
Ocaj Nires
Mis à jour avec un httplib2. Ceci est disponible pour python 3.
Ocaj Nires
Pour ceux qui sont nouveaux: il manque un point à l'exemple ci-dessus: "pycurl.HTTPHEADER" (je modifierais mais c'est 1 caractère et le minimum est 6).
Graeme Wicksted
OP a dit GET, not POST
Joe C
17

Une manière correcte de faire une authentification de base dans Python3 urllib.requestavec la validation de certificat suit.

Notez que ce certifin'est pas obligatoire. Vous pouvez utiliser votre bundle OS (probablement * nix uniquement) ou distribuer vous-même le bundle CA de Mozilla . Ou si les hôtes avec lesquels vous communiquez ne sont que quelques-uns, concaténez vous-même le fichier CA à partir des CA des hôtes, ce qui peut réduire le risque d' attaque MitM causée par une autre CA corrompue.

#!/usr/bin/env python3


import urllib.request
import ssl

import certifi


context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.verify_mode = ssl.CERT_REQUIRED
context.load_verify_locations(certifi.where())
httpsHandler = urllib.request.HTTPSHandler(context = context)

manager = urllib.request.HTTPPasswordMgrWithDefaultRealm()
manager.add_password(None, 'https://domain.com/', 'username', 'password')
authHandler = urllib.request.HTTPBasicAuthHandler(manager)

opener = urllib.request.build_opener(httpsHandler, authHandler)

# Used globally for all urllib.request requests.
# If it doesn't fit your design, use opener directly.
urllib.request.install_opener(opener)

response = urllib.request.urlopen('https://domain.com/some/path')
print(response.read())
saaj
la source
C'est bien. La vérification du certificat est importante lors de l'envoi d'informations d'identification en texte brut (HTTP Basic Auth). Vous devez vous assurer que votre couche TLS (HTTPS) est sécurisée, car vous comptez sur cette couche pour être sécurisée.
four43
Cela semble correct, mais n'a pas fonctionné dans mon cas, il lance une erreur du type ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify a échoué: impossible d'obtenir le certificat de l'émetteur local (_ssl.c: 1056)
neelmeg
Je l'ai compris en passant un certificat pem valide au paramètre de vérification et au paramètre de cookies.
neelmeg
1

en utilisant uniquement des modules standard et pas de codage d'en-tête manuel

... ce qui semble être le moyen prévu et le plus portable

le concept de python urllib est de regrouper les nombreux attributs de la requête en différents managers / directeurs / contextes ... qui traitent ensuite leurs parties:

import urllib.request, ssl

# to avoid verifying ssl certificates
httpsHa = urllib.request.HTTPSHandler(context= ssl._create_unverified_context())

# setting up realm+urls+user-password auth
# (top_level_url may be sequence, also the complete url, realm None is default)
top_level_url = 'https://ip:port_or_domain'
# of the std managers, this can send user+passwd in one go,
# not after HTTP req->401 sequence
password_mgr = urllib.request.HTTPPasswordMgrWithPriorAuth()
password_mgr.add_password(None, top_level_url, "user", "password", is_authenticated=True)

handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
# create OpenerDirector
opener = urllib.request.build_opener(handler, httpsHa)

url = top_level_url + '/some_url?some_query...'
response = opener.open(url)

print(response.read())
Alexey
la source
0

Basé sur la réponse de @AndrewCox avec quelques améliorations mineures:

from http.client import HTTPSConnection
from base64 import b64encode


client = HTTPSConnection("www.google.com")
user = "user_name"
password = "password"
headers = {
    "Authorization": "Basic {}".format(
        b64encode(bytes(f"{user}:{password}", "utf-8")).decode("ascii")
    )
}
client.request('GET', '/', headers=headers)
res = client.getresponse()
data = res.read()

Remarque, vous devez définir l'encodage si vous utilisez la bytesfonction au lieu de b"".

I159
la source
-1
requests.get(url, auth=requests.auth.HTTPBasicAuth(username=token, password=''))

Si avec un jeton, le mot de passe doit être ''.

Ça marche pour moi.

yidong li
la source