Requests: Header Otorisasi Sesi tidak dikirim saat pengalihan

Dibuat pada 28 Des 2015  ·  35Komentar  ·  Sumber: psf/requests

Saya menggunakan permintaan untuk mengunjungi developer-api.nest.com dan menyetel header Otorisasi dengan token pembawa. Pada beberapa permintaan, API tersebut merespons dengan pengalihan 307. Jika itu terjadi, saya masih memerlukan header Otorisasi untuk dikirim pada permintaan berikutnya. Saya sudah mencoba menggunakan requests.get() serta sesi.

Saya kira saya bisa mengatasi ini dengan tidak mengizinkan pengalihan, mendeteksi 307 dan kemudian mengeluarkan permintaan baru sendiri tetapi saya bertanya-tanya apakah ini bug. Haruskah saya berharap bahwa header Otorisasi akan dikirim pada semua permintaan yang dibuat dalam konteks sesi?

In [41]: s = requests.Session()

In [42]: s.headers
Out[42]: {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.7.0 CPython/3.4.3 Darwin/15.2.0'}

In [43]: s.headers['Authorization'] = "Bearer <snip>"

In [45]: s.get("https://developer-api.nest.com/devices/thermostats/")
Out[45]: <Response [401]>

In [46]: s.get("https://developer-api.nest.com/devices/thermostats/")
Out[46]: <Response [200]>

In [49]: Out[45].history
Out[49]: [<Response [307]>]

In [50]: Out[46].history
Out[50]: []

In [51]: Out[45].request.headers
Out[51]: {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.7.0 CPython/3.4.3 Darwin/15.2.0'}

In [52]: Out[46].request.headers
Out[52]: {'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.7.0 CPython/3.4.3 Darwin/15.2.0', 'Authorization': 'Bearer <snip>'}
Bug

Komentar yang paling membantu

Ada dua solusi khusus Nest.

Salah satunya adalah meneruskan parameter auth dengan access_token daripada menggunakan header Otorisasi. Saya menemukan ini di https://gist.github.com/tylerdave/409ffa08e1d47b1a1e23

Cara lainnya adalah menyimpan kamus dengan header yang akan Anda gunakan, jangan mengikuti pengalihan, lalu membuat permintaan kedua yang meneruskan header lagi:

    headers = {'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/json'}
    initial_response = requests.get('https://developer-api.nest.com', headers=headers, allow_redirects=False)
    if initial_response.status_code == 307:
        api_response = requests.get(initial_response.headers['Location'], headers=headers, allow_redirects=False)

Semua 35 komentar

Ke mana pengalihan tersebut?

Ah, domain yang berbeda. firebase-apiserver03-tah01-iad01.dapi.production.nest.com

Ya, jadi itu agak disengaja: kami sangat agresif dengan menghapus header otorisasi saat dialihkan ke host baru. Ini adalah fitur keamanan untuk menangani CVE 2014-1829 , yang disebabkan oleh kami mempertahankan tajuk pada pengalihan di luar host.

Namun, dari sudut pandang tertentu kami masih mendapat bug di sini, karena Anda menyetel header Otorisasi pada Session , bukan permintaannya. Pada prinsipnya, yang dimaksud adalah "Saya tidak peduli ke mana arah pengalihan, tambahkan header". Saya masih _think_ Saya lebih suka menggunakan pendekatan ini, yang setidaknya memastikan bahwa kami tidak terbuka untuk segala bentuk serangan, bahkan jika itu membuat contoh khusus ini agak rumit. Namun, saya terbuka untuk diyakinkan bahwa kami terlalu paranoid di sini.

Namun, saya terbuka untuk diyakinkan bahwa kami terlalu paranoid di sini.

Saya kurang terbuka untuk diyakinkan tetapi mau mendengarkan.

Meskipun demikian, mekanisme Auth terpisah dapat ditulis untuk mempertahankan header semacam itu di seluruh domain _allowed_ yang akan mengharuskan kami melakukan beberapa pekerjaan di rebuild_auth .

Saya tidak akan berdebat dengan keamanan cara kerjanya sekarang. Akan lebih baik jika memiliki beberapa mekanisme untuk ikut serta dalam perilaku "tidak aman". Mungkin menyetel domain dasar untuk mempertahankan header tersebut ke (nest.com, dalam kasus ini) atau mungkin daftar domain yang boleh dikirimi.

Ya, itu jauh lebih kompleks daripada inti permintaan yang akan pernah berikan. Itulah mengapa saya bertanya-tanya apakah kelas / penangan Auth yang terpisah mungkin bekerja paling baik untuk hal semacam ini. Saya tidak yakin ini akan berhasil karena saya cukup yakin bahwa kami tidak memanggil prepare_auth tanpa syarat.

Ini tidak akan bekerja dalam model standar karena kita tidak memanggil prepare_auth tanpa syarat. Namun, Transport Adapter dapat digunakan untuk memenuhi peran ini, meskipun penggunaan API tersebut sedikit tidak biasa.

Saya pikir TA adalah hal yang salah untuk direkomendasikan di sini.

  • Jika auth diberikan ke sesi, itu harus dikirim untuk setiap permintaan yang membuat sesi.
  • Mungkin kita harus menghapus session.auth . Itu tidak terlalu berguna.

Jika auth diberikan ke sesi, itu harus dikirim untuk setiap permintaan yang membuat sesi.

Saya pada dasarnya tidak setuju. Sesi tidak digunakan untuk satu domain, jika digunakan, saya tidak akan bermasalah dengan ini.

Mungkin kita harus menghapus session.auth. Itu tidak terlalu berguna.

Saya rasa ini berguna. Saya pikir akan lebih baik jika menugaskan tupel untuk itu tidak diperbolehkan. Saya lebih suka melihat kelas Auth yang menentukan domain mana yang akan digunakan. Kita bisa saja mengadopsi AuthHandler dari request-toolbelt yang memungkinkan orang menentukan kredensial untuk domain saat menggunakan permintaan. Ini memberikan cara yang sedikit lebih aman dalam menangani otentikasi berbasis Sesi. Sisi negatifnya adalah bahwa itu mengharuskan pengguna untuk memilih jenis otentikasi semacam itu.

Saya juga perlu memperbaikinya agar saya dapat membuat header tetap ada untuk pengalihan.

@jtherrmann Jika ini adalah tajuk

Apakah ada kemajuan atau pertimbangan tambahan yang diberikan untuk ini?
Saya mengalami masalah yang sama.

@ethanroy Tidak ada pertimbangan tambahan selain saran saya untuk menggunakan pengendali

Terkait: jika sesi mengalihkan dan menghapus auth, memanggil get lagi menerapkan auth dan menggunakan pengalihan yang disimpan dalam cache. Jadi ketuk dua kali dan Anda masuk. Perilaku yang diinginkan?

>>> s = requests.Session()
>>> s.headers.update({"Authorization": "Token {}".format(API_TOKEN)})
>>> s.get(url)

<Response [403]>

>>> s.get(url)

<Response [200]>

@GregBakker Ya, ish. Ini adalah pertemuan perilaku yang diinginkan. Namun, bug ini mencatat bahwa 403 asli seharusnya tidak terjadi.

@Lukasa ketika Anda mengatakan "cara termudah untuk mengatasi masalah ini adalah dengan menyetel penangan autentikasi tingkat sesi", apakah itu sesuatu yang berfungsi hari ini? Berdasarkan apa yang saya lihat di kode, jawabannya tidak tetapi kata-kata Anda membuat saya bertanya-tanya apakah saya melewatkan sesuatu. Anda sedang berbicara tentang menyetel atribut Session auth, bukan?

Ya, itu _should_ bekerja.

@jwineinger jadi bagaimana Anda akhirnya mengatasi masalah ini? sepertinya masih berperilaku sama.

Ada dua solusi khusus Nest.

Salah satunya adalah meneruskan parameter auth dengan access_token daripada menggunakan header Otorisasi. Saya menemukan ini di https://gist.github.com/tylerdave/409ffa08e1d47b1a1e23

Cara lainnya adalah menyimpan kamus dengan header yang akan Anda gunakan, jangan mengikuti pengalihan, lalu membuat permintaan kedua yang meneruskan header lagi:

    headers = {'Authorization': 'Bearer ' + access_token, 'Content-Type': 'application/json'}
    initial_response = requests.get('https://developer-api.nest.com', headers=headers, allow_redirects=False)
    if initial_response.status_code == 307:
        api_response = requests.get(initial_response.headers['Location'], headers=headers, allow_redirects=False)

Saya mengalami masalah yang sama ini dan mengatasinya dengan mengganti metode rebuild_auth dalam implementasi kustom requests.Session :

from requests import Session

class CustomSession(Session):
    def rebuild_auth(self, prepared_request, response):
        return

s = CustomSession()
s.get(url, auth=("username", "password"))

@ sigmavirus24 apa yang salah dengan solusi @ gabriel-loo? Perhatian pada keamanan?

@ j08 ya. Silakan baca utasnya. Ada CVE yang terkait dengan tidak melucuti otentikasi sebelum mengikuti arahan ulang sewenang-wenang ke domain baru. Pikirkan masalahnya seperti ini:

Saya membuat permintaan ke api.github.com dan penyerang berhasil membuat saya mengikuti pengalihan ke another-domain.com yang mereka kontrol dan saya meneruskan token saya dengan akses tulis ke repositori saya (termasuk permintaan) lalu dapat muncul seolah-olah saya membuat komitmen ke permintaan padahal sebenarnya mereka membuat komitmen itu melalui API. Mereka dapat menyertakan kode dalam Permintaan yang akan melemahkan postur keamanannya dan mungkin secara aktif merugikan Anda. Itulah yang bisa terjadi ketika Anda mengirim kredensial otentikasi Anda tanpa syarat pada setiap pengalihan.

Meski begitu, katakanlah pengalihan tidak berbahaya, apakah Anda benar-benar merasa nyaman membocorkan kredensial Anda untuk suatu layanan ke perusahaan atau layanan lain? Layanan asli mungkin menyimpan data rahasia untuk Anda, pelanggan Anda, atau yang lainnya. Meskipun domain baru tempat Anda dialihkan tidak menggunakan kredensial Anda tetapi berpotensi mencatatnya sebagai data yang tidak terduga, seseorang yang menyerang mereka dan dapat mengambil log tersebut kemudian dapat menggunakan kredensial Anda terhadap domain asli jika mereka dapat mencari tahu di mana milik mereka. Apakah Anda benar-benar bersedia mengambil risiko itu?

Terima kasih atas ilustrasinya, @ sigmavirus24. Jika kekhawatiran ini pada akhirnya melarang penerusan header sensitif ke pengalihan, mengapa utas ini masih terbuka? Saya tidak bisa memikirkan kesalahan yang lebih sesuai daripada yang Anda dapatkan (403), jadi tidak ada bug yang perlu dilakukan di sini, bukan? Atau apa yang kamu pikirkan , @Lukasa?

Saya mengalami masalah ini baru-baru ini saat bekerja dengan API non-publik. Masalah keamanan benar-benar masuk akal sebagai alasan untuk menghapus auth pada pengalihan. Menurut saya solusi seperti @ gabriel-loo's adalah sesuatu yang dapat dipertimbangkan orang jika mereka yakin mereka berada di lingkungan yang cukup aman untuk melakukannya. Atau penangan level sesi. Atau temukan cara lain untuk menyiasatinya dengan melewatkan pengalihan sepenuhnya seperti yang disarankan di atas, jika memungkinkan. Jadi sejalan dengan tampilan ini sebenarnya bukan bug.

Namun, saya menghabiskan lebih banyak waktu daripada yang mungkin saya butuhkan untuk bingung tentang mengapa beberapa klien HTTP non-Python _did_ meneruskan header auth dan berfungsi dengan baik ketika ini tidak terjadi. Satu saran: mungkin menyenangkan untuk mengeluarkan peringatan melalui warnings sini untuk membuatnya lebih jelas bagi penelepon ketika tajuk ada dan dihilangkan. Menurut saya, jarang terjadi bahwa penelepon _tidak_ ingin diperingatkan.

@tlantz biasanya itu tampak cukup masuk akal. Permintaan sebagai proyek (serta urllib3, salah satu dependensinya) telah menyebabkan kemarahan yang signifikan saat mengeluarkan peringatan apa pun baik melalui modul peringatan atau melalui logging. Lebih lanjut, modul peringatan adalah untuk hal-hal yang orang harus mengambil tindakan, misalnya, tidak menggunakan versi Python yang telah dikompilasi terhadap versi terbaru dari OpenSSL.

Dalam kebanyakan kasus, perilaku ini tidak terlalu bermasalah seperti, misalnya, tidak dapat memverifikasi sertifikat untuk koneksi TLS. Itu jelas tidak membantu Anda atau siapa pun yang telah mengungkapkan rasa frustrasi mereka yang tulus dan sah tentang masalah ini. Dengan mengingat hal itu, saya bertanya-tanya apakah tidak lebih baik mencoba mencatat ini pada level DEBUG . Jika seseorang menggunakan logging (umumnya praktik yang layak) dan memungkinkan level itu muncul untuk mereka. Selanjutnya, mengingat sedikitnya log Permintaan itu sendiri, ini akan cukup menonjol sebagai log debug. Apakah itu tampak seperti pertukaran yang adil?

Ya, itu sepertinya pertukaran yang benar-benar adil. Berpikir sekitar warnings masuk akal bagi saya. Saya pikir pada saat Anda bingung selama 30 menit atau jadi Anda biasanya menambahkan logging sekitar barang Anda sendiri pada DEBUG , jadi saya pikir pesan DEBUG akan mencapai 95% kasus di mana orang terjebak mencoba mencari tahu apa yang tidak berhasil.

Saya menggunakan sesi untuk menahan header Otorisasi, tetapi tidak dikirim dalam pengalihan
permintaan (2.18.4)

Seorang rekan kerja dan saya menghabiskan setidaknya beberapa jam untuk men-debug masalah yang terkait langsung dengan perilaku ini. Kasus penggunaan saya mengalihkan panggilan API dari api.my-example-site.org ke www.api.my-example-site.org . Header dihilangkan pada pengalihan.

Jika ini adalah perilaku yang disengaja (atau jika tidak akan diubah dalam waktu dekat), dapatkah kami setidaknya menambahkannya ke dokumentasi? Saya membaca dan membaca ulang dokumen mencoba mencari tahu apa yang saya lakukan dengan tidak benar, dan saya bahkan membaca semua kode di kelas Request . Jika saya telah melihat peringatan tentang perilaku ini di dokumentasi, saya akan memperbaiki masalah saya dalam beberapa menit (yang merupakan waktu yang dibutuhkan setelah saya menemukan utas ini). Namun, mungkin kami membaca di bagian dokumentasi yang salah.

Hai @ndmeiri , kami memiliki panggilan tentang ini di panduan memulai cepat untuk Permintaan di bawah tajuk Header Kustom . Jika Anda merasa ada tempat yang lebih baik untuk meletakkan ini, kami akan dengan senang hati meninjau saran yang Anda miliki. Saya lebih suka kami memindahkannya ke masalah atau PR terpisah karena tidak terkait langsung dengan tiket ini. Terima kasih!

Halo @nateprewitt , terima kasih telah menunjukkan bagian Header Kustom! Ternyata, saya tidak berpikir untuk memeriksa bagian dokumentasi itu.

Saya pikir akan sangat membantu untuk juga menyertakan panggilan, atau referensi untuk panggilan keluar, di bagian Otentikasi . Meskipun saat ini saya cukup sibuk, saya akan mempertimbangkan untuk membuka PR ketika keadaan sudah tenang untuk memperbarui dokumen.

Jika ini perilaku yang diinginkan

@ndmeiri Ya, merupakan perilaku yang dimaksudkan untuk tidak membocorkan kredensial otentikasi sensitif Anda ke sumber yang berpotensi tidak tepercaya. (Hanya untuk memperjelas)

Tampaknya "domain tepercaya" dari # 4983 tidak lagi dalam implementasi session.py.

Dalam situasi di mana saya membuat permintaan ke URL yang saya _ tahu_ alihkan ke URL tertentu yang berbeda-tetapi-aman, dan saya ingin mengaktifkan pengalihan dengan header Otorisasi yang tetap ada, bagaimana saya bisa mencapainya?

bagaimana saya bisa mencapainya?

Anda dapat menambal metode rebuild_auth . Ini berfungsi untuk saya: https://github.com/DHI-GRAS/earthdata-download/blob/master/earthdata_download/download.py#L27 -L49

@ j08lue Terima kasih! Sebelum komentar Anda muncul, saya mengatasi masalah ini dengan menyetel allow_redirects menjadi False , dan menambahkan kode untuk secara eksplisit mengikuti beberapa, pengalihan spesifik yang diharapkan dalam kasus penggunaan saya. Ini adalah situasi jangka pendek bagi saya, jadi saya berharap ini adalah solusi sementara yang memadai, tetapi senang mengetahui bahwa ada cara yang lebih baik untuk melakukannya dalam jangka panjang jika diperlukan.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

remram44 picture remram44  ·  4Komentar

ReimarBauer picture ReimarBauer  ·  4Komentar

jakul picture jakul  ·  3Komentar

xsren picture xsren  ·  3Komentar

thadeusb picture thadeusb  ·  3Komentar