Layanan web yang tenang - bagaimana cara mengotentikasi permintaan dari layanan lain?

117

Saya merancang layanan web RESTful yang perlu diakses oleh pengguna, tetapi juga layanan dan aplikasi web lain. Semua permintaan yang masuk perlu diautentikasi. Semua komunikasi berlangsung melalui HTTPS. Otentikasi pengguna akan bekerja berdasarkan token otentikasi, yang diperoleh dengan mem-posting nama pengguna dan kata sandi (melalui koneksi SSL) ke sumber / sesi yang disediakan oleh layanan.

Dalam kasus klien layanan web, tidak ada pengguna akhir di belakang layanan klien. Permintaan dimulai oleh tugas terjadwal, acara atau beberapa operasi komputer lainnya. Daftar layanan penghubung sudah diketahui sebelumnya (jelas, saya kira). Bagaimana cara mengautentikasi permintaan ini yang berasal dari layanan (web) lain? Saya ingin proses otentikasi semudah mungkin diterapkan untuk layanan tersebut, tetapi tidak dengan mengorbankan keamanan. Apa standar dan praktik terbaik untuk skenario seperti ini?

Opsi yang dapat saya pikirkan (atau telah disarankan kepada saya):

  1. Minta layanan klien menggunakan nama pengguna dan sandi "palsu", dan lakukan otentikasi dengan cara yang sama seperti pengguna. Saya tidak suka opsi ini - rasanya tidak benar.

  2. Tetapkan ID aplikasi permanen untuk layanan klien, mungkin juga kunci aplikasi. Sejauh yang saya mengerti, ini sama dengan memiliki nama pengguna + kata sandi. Dengan id dan kunci ini, saya dapat mengautentikasi setiap permintaan, atau membuat token otentikasi untuk mengautentikasi permintaan lebih lanjut. Bagaimanapun, saya tidak suka opsi ini, karena siapa pun yang bisa mendapatkan id dan kunci aplikasi dapat menyamar sebagai klien.

  3. Saya bisa menambahkan pemeriksaan alamat IP ke opsi sebelumnya. Ini akan membuat lebih sulit untuk melakukan permintaan palsu.

  4. Sertifikat klien. Siapkan otoritas sertifikat saya sendiri, buat sertifikat dasar, dan buat sertifikat klien untuk layanan klien. Namun, ada beberapa masalah yang muncul di benak saya: a) bagaimana saya masih mengizinkan pengguna untuk mengautentikasi tanpa sertifikat dan b) seberapa rumit penerapan skenario ini dari sudut pandang layanan klien?

  5. Sesuatu yang lain - pasti ada solusi lain di luar sana?

Layanan saya akan berjalan di Java, tetapi saya sengaja meninggalkan informasi tentang kerangka khusus apa yang akan dibangunnya, karena saya lebih tertarik pada prinsip-prinsip dasar dan tidak begitu banyak pada detail implementasi - Saya berasumsi solusi terbaik untuk ini akan dapat diterapkan terlepas dari kerangka kerja yang mendasarinya. Namun, saya sedikit tidak berpengalaman dengan subjek ini, jadi tips dan contoh konkret tentang implementasi aktual (seperti perpustakaan pihak ketiga yang bermanfaat, artikel, dll.) Akan sangat dihargai.

Tommi
sumber
Jika saya mungkin menyarankan, kenali layanan situs web kotak besar dan pilih apa yang Anda suka. Pengguna Anda juga akan menemukan kesamaan dengan praktik terbaik layanan RESTful lainnya.
Yzmir Ramirez
Menemukan pertanyaan lain (hampir dua tahun) yang menyentuh subjek serupa: stackoverflow.com/questions/1138831/…
Tommi
Pada OS apa layanan (baik web dan lainnya) dihosting? Apakah mereka berjalan di server yang merupakan bagian dari infrastruktur yang sama?
Anders Abel
OS dapat bervariasi: Win, * nix dll. Dan layanan klien mungkin atau mungkin tidak berada dalam infrastruktur yang sama dengan layanan saya.
Tommi

Jawaban:

34

Solusi apa pun untuk masalah ini bermuara pada rahasia bersama. Saya juga tidak suka opsi nama pengguna dan kata sandi yang di-hardcode tetapi memiliki keuntungan karena cukup sederhana. Sertifikat klien juga bagus, tetapi apakah jauh berbeda? Ada sertifikat di server dan satu di klien. Keuntungan utamanya adalah lebih sulit untuk melakukan kekerasan. Semoga Anda memiliki perlindungan lain untuk melindunginya.

Saya rasa poin A Anda untuk solusi sertifikat klien tidak sulit untuk diselesaikan. Anda hanya menggunakan cabang. if (client side certificat) { check it } else { http basic auth }Saya bukan ahli Java dan saya tidak pernah bekerja dengannya untuk membuat sertifikat sisi klien. Namun Google yang cepat membawa kita ke tutorial ini yang sesuai dengan keinginan Anda.

Terlepas dari semua diskusi "apa yang terbaik" ini, izinkan saya menunjukkan bahwa ada filosofi lain yang mengatakan, "lebih sedikit kode, lebih sedikit kecerdasan lebih baik." (Saya pribadi memegang filosofi ini). Solusi sertifikat klien terdengar seperti banyak kode.

Saya tahu Anda mengajukan pertanyaan tentang OAuth, tetapi proposal OAuth2 menyertakan solusi untuk masalah Anda yang disebut " token pembawa " yang harus digunakan bersama dengan SSL. Saya pikir, demi kesederhanaan, saya akan memilih pengguna / pass hard-code (satu per aplikasi sehingga mereka dapat dicabut secara individual) atau token pembawa yang sangat mirip.

newz2000.dll
sumber
27
Sertifikat klien BUKAN rahasia bersama. Itulah mengapa mereka ada. Klien memiliki kunci pribadi dan server memiliki kunci publik. Klien tidak pernah membagikan rahasianya, dan kunci publik bukanlah rahasia.
Tim
5
Tautan tutorial tidak mengarah ke artikel tutorial tetapi ke beberapa halaman indeks Java di situs Oracle ...
Marjan Venema
2
@MarjanVenema Ya, itu karena Anda mencoba tautan +2 tahun setelah newz2000 dijawab, tetapi Anda selalu dapat mencoba Mesin WayBack
Fábio Duque Silva
1
@MarjanVenema: Maaf, tetapi Anda mengharapkan newz2000 datang ke sini dan memperbarui tautan setelah mati? Seperti yang Anda katakan, itu link rot, jadi itu terjadi cepat atau lambat. Entah Anda mencoba mengakses arsip untuk melihat apa yang dilihat penulis saat itu, atau Anda menemukan tautan baru dan memberikan kontribusi positif. Saya tidak melihat bagaimana komentar Anda membantu siapa pun. Tapi di sini, ikuti tautan ini: oracle.com/technetwork/articles/javase/… (berhati-hatilah karena pada akhirnya akan membusuk juga)
Fábio Duque Silva
2
@ FábioSilva: Tidak. Saya tidak mengharapkan dia melakukan itu. Saya memang membaca arsipnya tetapi saya tidak punya waktu untuk mencari tautan baru, jadi saya melakukan hal terbaik berikutnya: memberi komentar bahwa itu sudah mati sehingga orang lain dari komunitas dapat menemukan lokasi baru dan memperbarui pos. Karena Anda jelas punya waktu untuk menemukan tautan baru, mengapa Anda tidak memperbarui tautan di kiriman alih-alih memasukkannya ke dalam komentar yang menyesatkan saya?
Marjan Venema
36

Setelah membaca pertanyaan Anda, saya akan katakan, buat token khusus untuk melakukan permintaan yang diperlukan. Token ini akan hidup dalam waktu tertentu (katakanlah dalam satu hari).

Berikut adalah contoh untuk menghasilkan token otentikasi:

(day * 10) + (month * 100) + (year (last 2 digits) * 1000)

misalnya: 3 Juni 2011

(3 * 10) + (6 * 100) + (11 * 1000) = 
30 + 600 + 11000 = 11630

lalu gabungkan dengan kata sandi pengguna, contoh "my4wesomeP4ssword!"

11630my4wesomeP4ssword!

Kemudian lakukan MD5 dari string itu:

05a9d022d621b64096160683f3afe804

Kapan Anda memanggil permintaan, selalu gunakan token ini,

https://mywebservice.com/?token=05a9d022d621b64096160683f3afe804&op=getdata

Token ini selalu unik setiap hari, jadi saya rasa perlindungan semacam ini lebih dari cukup untuk selalu melindungi layanan Anda.

Semoga membantu

:)

kororo
sumber
1
Saya sangat suka cara Anda menambahkan token keamanan di setiap permintaan tetapi apa yang terjadi ketika programmer telah membuat 100 halaman jsp dan setelah itu t0 menerapkan keamanan dalam 100 halaman yang dibuat sebelumnya serta halaman yang akan dibuat. Dalam hal ini menambahkan token di setiap permintaan bukanlah pilihan yang benar. Lagi pula, +1 untuk teknik Anda. :)
Ankur Verma
4
Apa yang terjadi jika jam tidak disinkronkan? Tidakkah klien akan menghasilkan token yang salah dalam hal ini? Bahkan jika keduanya menghasilkan tanggal waktu dalam UTC, jam mereka masih bisa berbeda sehingga menghasilkan Jendela waktu setiap hari, saat token tidak berfungsi?
NickG
@NickG, Saya mengalami masalah ini sebelumnya, satu-satunya cara untuk mengamankan ini dengan meminta waktu server. Ini akan 99% membunuh masalah UTC. Tentu saja sisi negatifnya adalah satu panggilan ekstra ke server.
kororo
tetapi saya masih dapat menggunakan layanan web Anda menggunakan token ini selama sehari, bukan? saya tidak melihat bagaimana ini akan membantu
Mina Gabriel
@MinaGabriel Anda dapat menambahkan lebih banyak kerangka waktu dalam pembuatan token. (menit * 10) + (jam * 100) + (hari * 1000) + (bulan * 10000) + (tahun (2 digit terakhir) * 100000)
kororo
11

Ada beberapa pendekatan berbeda yang dapat Anda lakukan.

  1. Puritan RESTful akan ingin Anda menggunakan otentikasi BASIC, dan mengirim kredensial pada setiap permintaan. Alasan mereka adalah tidak ada yang menyimpan negara bagian mana pun.

  2. Layanan klien dapat menyimpan cookie, yang mempertahankan ID sesi. Secara pribadi saya tidak menganggap ini menyinggung seperti beberapa orang puritan yang saya dengar - bisa jadi mahal untuk mengautentikasi berulang kali. Sepertinya Anda tidak terlalu menyukai ide ini.

  3. Dari uraian Anda, sepertinya Anda benar-benar tertarik dengan OAuth2 Pengalaman saya sejauh ini, dari apa yang saya lihat, adalah agak membingungkan, dan agak berdarah. Ada implementasi di luar sana, tetapi mereka sedikit dan jarang. Di Java, saya memahami bahwa ini telah diintegrasikan ke dalam modul keamanan Spring3 . ( Tutorial mereka ditulis dengan baik.) Saya telah menunggu untuk melihat apakah akan ada perpanjangan di Restlet , tapi sejauh ini, meskipun sudah diusulkan, dan mungkin ada di inkubator, itu masih belum dimasukkan sepenuhnya.

jwismar.dll
sumber
Saya tidak menentang opsi 2 - menurut saya ini adalah solusi yang bagus dalam aplikasi RESTful - tetapi dari mana layanan klien mendapatkan token itu? Bagaimana mereka mengotentikasi pertama kali? Mungkin saya berpikir ini salah, tetapi tampaknya aneh bahwa layanan klien perlu memiliki nama pengguna dan kata sandi sendiri untuk ini.
Tommi
Jika pengguna akhir adalah pengguna Anda, maka layanan perantara dapat memberikan kredensial mereka kepada Anda pada permintaan pertama, dan Anda dapat mengembalikan cookie atau token lainnya.
jwismar
Demikian pula, dalam skenario OAuth, pengguna akhir mendelegasikan akses mereka ke layanan web Anda ke layanan perantara.
jwismar
Tampaknya ada kesalahpahaman - tidak ada pengguna akhir sama sekali di belakang layanan klien . Saya telah memperbarui pertanyaan saya untuk menjelaskan situasinya dengan lebih baik.
Tommi
1
Saya hanya akan menambahkan bahwa opsi # 1 yang tercantum di atas hanya boleh dilakukan melalui HTTPS.
mr-sk
3

Saya percaya pendekatannya:

  1. Permintaan pertama, klien mengirimkan id / kode sandi
  2. Tukarkan id / pass untuk token unik
  3. Validasi token pada setiap permintaan berikutnya hingga kedaluwarsa

cukup standar, terlepas dari bagaimana Anda menerapkan dan detail teknis spesifik lainnya.

Jika Anda benar-benar ingin mendorong amplop, mungkin Anda dapat menganggap kunci https klien dalam keadaan sementara tidak valid hingga kredensial divalidasi, membatasi informasi jika tidak pernah ada, dan memberikan akses saat divalidasi, berdasarkan lagi pada masa kedaluwarsa.

Semoga ini membantu

Dynrepsys
sumber
3

Sejauh pendekatan sertifikat klien berjalan, tidak akan terlalu sulit untuk diterapkan sambil tetap mengizinkan pengguna tanpa sertifikat klien masuk.

Jika Anda ternyata membuat Otoritas Sertifikasi yang ditandatangani sendiri, dan menerbitkan sertifikat klien untuk setiap layanan klien, Anda akan mendapatkan cara mudah untuk mengautentikasi layanan tersebut.

Bergantung pada server web yang Anda gunakan, harus ada metode untuk menentukan otentikasi klien yang akan menerima sertifikat klien, tetapi tidak memerlukannya. Misalnya, di Tomcat saat menentukan konektor https, Anda dapat menyetel 'clientAuth = want', bukan 'true' atau 'false'. Anda kemudian akan memastikan untuk menambahkan sertifikat CA yang ditandatangani sendiri ke truststore Anda (secara default file cacerts di JRE yang Anda gunakan, kecuali Anda menentukan file lain dalam konfigurasi server web Anda), jadi satu-satunya sertifikat tepercaya adalah yang dikeluarkan dari CA yang Anda tandatangani sendiri.

Di sisi server, Anda hanya akan mengizinkan akses ke layanan yang ingin Anda lindungi jika Anda dapat mengambil sertifikat klien dari permintaan (bukan null), dan melewati pemeriksaan DN apa pun jika Anda menginginkan keamanan tambahan. Untuk pengguna tanpa sertifikat klien, mereka masih dapat mengakses layanan Anda, tetapi tidak memiliki sertifikat yang ada dalam permintaan.

Menurut pendapat saya ini adalah cara yang paling 'aman', tetapi tentunya memiliki kurva belajar dan overhead, jadi mungkin belum tentu menjadi solusi terbaik untuk kebutuhan Anda.

bobz32.dll
sumber
3

5. Sesuatu yang lain - pasti ada solusi lain di luar sana?

Anda benar, ada! Dan itu disebut JWT (JSON Web Tokens).

JSON Web Token (JWT) adalah standar terbuka (RFC 7519) yang mendefinisikan cara kompak dan mandiri untuk mentransmisikan informasi dengan aman antar pihak sebagai objek JSON. Informasi ini dapat diverifikasi dan dipercaya karena ditandatangani secara digital. JWT dapat ditandatangani menggunakan rahasia (dengan algoritme HMAC) atau pasangan kunci publik / pribadi menggunakan RSA.

Saya sangat merekomendasikan melihat JWT. Mereka adalah solusi yang jauh lebih sederhana untuk masalah jika dibandingkan dengan solusi alternatif.

https://jwt.io/introduction/

justin.hughey
sumber
1

Anda dapat membuat Sesi di server dan berbagi sessionIddi antara klien dan server dengan setiap panggilan REST.

  1. Pertama permintaan ISTIRAHAT mengotentikasi: /authenticate. Mengembalikan tanggapan (sesuai format klien Anda) dengan sessionId: ABCDXXXXXXXXXXXXXX;

  2. Simpan ini sessionIddi Mapdengan sesi yang sebenarnya. Map.put(sessionid, session)atau gunakan SessionListeneruntuk membuat dan menghancurkan kunci untuk Anda;

    public void sessionCreated(HttpSessionEvent arg0) {
      // add session to a static Map 
    }
    
    public void sessionDestroyed(HttpSessionEvent arg0) {
      // Remove session from static map
    }
    
  3. Dapatkan sessionid dengan setiap panggilan REST, seperti URL?jsessionid=ABCDXXXXXXXXXXXXXX(atau cara lain);

  4. Mundur HttpSessiondari peta menggunakan sessionId;
  5. Validasi permintaan untuk sesi itu jika sesi aktif;
  6. Kirim kembali tanggapan atau pesan kesalahan.
arviarya
sumber
0

Saya akan menggunakan memiliki aplikasi yang mengarahkan pengguna ke situs Anda dengan parameter id aplikasi, setelah pengguna menyetujui permintaan tersebut menghasilkan token unik yang digunakan oleh aplikasi lain untuk otentikasi. Dengan cara ini aplikasi lain tidak menangani kredensial pengguna dan aplikasi lain dapat ditambahkan, dihapus, dan dikelola oleh pengguna. Foursquare dan beberapa situs lain mengotentikasi cara ini dan sangat mudah diterapkan sebagai aplikasi lain.

Devin M
sumber
Hmm, saya tidak yakin apakah saya bisa mengikuti penjelasannya. Pengguna mana yang kita bicarakan? Saya berbicara tentang aplikasi yang berkomunikasi dengan aplikasi lain. Saya kira Anda mengerti ini, tetapi saya masih tidak begitu mengerti. Apa yang terjadi ketika "token" ini kedaluwarsa, misalnya?
Tommi
Nah, token yang Anda buat dan kirim kembali ke aplikasi lain adalah token yang tetap, itu terkait dengan pengguna dan aplikasi. Berikut ini adalah tautan ke foursquares docs developer.foursquare.com/docs/oauth.html dan pada dasarnya ini hanya oauth2 jadi lihatlah itu untuk solusi otentikasi yang baik.
Devin M
Tampaknya ada kesalahpahaman - tidak ada pengguna akhir sama sekali di belakang layanan klien . Dokumentasi foursquare yang Anda tautkan secara singkat menyebutkan akses tanpa pengguna, jadi setidaknya agak membantu - terima kasih! Tapi saya masih belum bisa memberikan gambaran lengkap tentang bagaimana itu akan bekerja dalam kenyataan.
Tommi
Buat saja kunci untuk aplikasi, jika yang Anda lakukan hanyalah mengizinkan akses untuk aplikasi, maka application_id dan application_key sederhana harus berfungsi untuk otentikasi. Jika Anda ingin mereka mengautentikasi dengan token, periksa menggunakan opsi otentikasi token devise karena itu hanya akan menjadi parameter yang diteruskan dengan permintaan url ke aplikasi Anda.
Devin M
Tapi bukankah ini kemudian persis sama dengan skenario token otentikasi nama pengguna + kata sandi = sesi? Bukankah application_id dan application_key hanyalah sinonim untuk nama pengguna dan sandi? :) Tidak apa-apa jika ini benar-benar praktik standar untuk situasi seperti ini - seperti yang saya katakan, saya tidak berpengalaman dalam hal ini - tetapi saya hanya berpikir mungkin ada pilihan lain ...
Tommi
-3

Selain otentikasi, saya sarankan Anda memikirkan gambaran besarnya. Pertimbangkan untuk membuat layanan RESTful backend Anda tanpa otentikasi apa pun; kemudian letakkan beberapa otentikasi yang sangat sederhana yang diperlukan layanan lapisan tengah antara pengguna akhir dan layanan backend.

Dagang
sumber
Antara pengguna akhir dan layanan backend? Bukankah hal itu akan membuat layanan klien tidak diautentikasi sama sekali? Bukan itu yang kuinginkan. Saya tentu saja dapat menempatkan lapisan tengah itu antara layanan web saya dan layanan klien, tetapi itu masih menyisakan pertanyaan terbuka: apa model otentikasi yang sebenarnya?
Tommi
Lapisan tengah dapat berupa server web seperti Nginx, Anda dapat melakukan otentikasi di sana. Model otentikasi dapat berbasis sesi.
Dagang
Seperti yang telah saya coba jelaskan dalam pertanyaan saya, saya tidak menginginkan skema otentikasi berbasis sesi untuk layanan klien ini. (Silakan lihat pembaruan saya untuk pertanyaan tersebut.)
Tommi
Saya sarankan Anda menggunakan daftar ip putih atau rentang ip alih-alih nama & kata sandi. Biasanya, ip layanan klien stabil.
Dagang