Closed reinout closed 8 years ago
@byrman, kun je ernaar kijken?
Bedankt, zal ik doen.
2016-09-12 15:04 GMT+02:00 Reinout van Rees notifications@github.com:
@byrman https://github.com/byrman, kun je ernaar kijken?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/lizardsystem/lizard-auth-server/pull/51#issuecomment-246340550, or mute the thread https://github.com/notifications/unsubscribe-auth/AA_cSX3M0AXIzPpQTZDBPzT5XScOnNbJks5qpU3sgaJpZM4J6j3Z .
Ik heb nog een tikkie documentatie toegevoegd en nog een view/url hernoemd.
Stapeltje pep8/pyflakes fixes toegevoegd.
@reinout : ik heb de boel lokaal nog niet werkend gekregen. Kan ook aan mijn configuratie liggen.
Het gaat op dit punt fout:
user = django_authenticate(username=form.cleaned_data.get('username'),
password=form.cleaned_data.get('password'))
Mijn cleaned_data ziet er op dit punt zo uit:
{'key': 'u2pLw5zDCGuJ9tYeezfsZCgp05G087VknbDy05V5Cn0bO98aT5elgksQzI9Hv9z2', 'exp': 1473767790, 'domain': None}
De keys 'username' en 'password' ontbreken volledig en de authenticatie mislukt dus. Wat me verder opvalt is dat 'domain' een None waarde heeft.
Domain hoeft alleen gezet te worden als het onduidelijk is, zoals bij nxt die op meerdere urls reageert. Normaliter heeft één site één url. Dus dat klopt wel.
Wat niet klopt is dat lizard-auth-client geen username/password doorgeeft. Ik heb de fix daarvoor net gepusht.
Zodra lizard-auth-client gemerged is wil ik met een docker setup daarvoor beginnen om testen makkelijker te maken, zucht.
Er zijn ook wat zaken die eigenlijk beter moeten: api URLs die in het /api/v2/ beginpunt expliciet worden genoemd, bijvoorbeeld. En de inhoud van de JWT message zou eigenlijk ook als een form gespecificeerd moeten worden, maar dan zit je met een form in een form te werken.
Ik had een JWTField voorgesteld waarin dit afgehandeld kon worden, maar @jackieleng was daar sterk op tegen omdat er dan iets dubbel zou gebeuren.
Krijgen we nu ook niet alle username/passwords in onze access logs (als base64)? Deze info zit nu immers in de parameters van de GET requests. Dat lijkt me zeer onwenselijk.
Goed punt. Het klopt dat de data geëncoded is, niet geëncrypt. En met een GET komt het in de logfile terecht.
Dan ben ik toch wat in de war gebracht door de verschillende views (die bijna allemaal ook GET ondersteunen).
Op lange termijn (=morgen) moet er toch eens django-rest-framework in. Zal ik in de tussentijd de GET op deze view maar uitzetten?
Wacht nog maar even, want ik kom daar geloof ik langs...
Run anders ook even bin/sphinx
, misschien dat her en der de documentatie ook wat helpt. doc/build/html/index.html
Hier wordt een GET gedaan waar ik langskom tijdens het inloggen op onze API:
Als je de GET op deze view uitzet, kan ik niet meer inloggen.
Da's toch een kwestie van allebei de kanten op POST ombouwen?
Of het password encrypted in de payload stoppen. De site en de SSO-server delen een SSO_SECRET, dus decrypten aan de SSO zijde moet geen probleem zijn.
Bonus: dan kan het ook over HTTP.
decrypten: zat ik ook even aan te denken, maar ik vroeg me af hoe veilig dat was. Dan ga je met hetzelfde secret zowel encrypten als een jwt signeren: geeft dat geen mogelijkheid tot een crypto-aanval?
Aan de andere kant: laag risico. En in de oude situatie gebeurde het ook, meen ik me te herinneren.
(Niet github-gerelateerde opmerking: ik ga nu wat eerder naar huis, ik moet nog inkopen doen voor eten. Morgen ga ik weer a/d slag. Kun je de docker PR voor lizard-auth-client bekijken? Dan zit dat ook lekker in de Jenkins tests.)
Ik ben geen cryptoloog, maar zie je bezwaar niet:
Voelt een stuk beter dan de POST in plain (base64) text.
Oh, het voelt inderdaad een stuk beter dan een POST in (effectief) plain text.
Mijn cryptologisch bezwaar is dat de Duitse Enigma code gebroken kon worden omdat een letter mechanisch nooit op zichzelf kon mappen! Dus hoe meer verschillende dingen je met dezelfde key doet, hoe beter je gekraakt kan worden.
Maar.... in de praktijk is je suggestie gewoon een goede :)
JWT kan zowel met non-encrypted als met encrypted data worden gebruikt, dat is geloof ik het probleem niet. Maar misschien maken we het onszelf te moeilijk.
Wat er nu gebeurt is het volgende:
Dit lijkt een betere workflow:
Zouden we de eerste workflow wel moeten willen ondersteunen?
De tweede workflow is de normale.
De eerste heb je nodig als je via /admin of met kale http upload requests vanuit een toeleverancier loopt te werken.
Dus je wilt de eerste workflow blijven ondersteunen? Dan moeten we het password echt encrypten (of desnoods de hele payload).
Een leverancier zou natuurlijk ook eerst een token kunnen ophalen bij de SSO en dat kunnen meesturen met zijn requests, maar dat zijn ze nu niet gewend.
Password encrypten hoeft niet. Het gaat nu ook onversleuteld over de lijn als je je wachtwoord intypt. Dus we hoeven het niet moeilijker te maken dan het is.
Wat wel moet gebeuren: POST ipv GET voor dit request. Ik ga dat even fixen.
Hmmm, ik weet niet wat ik daar van vind: door er gevoelige informatie in te stoppen, gebruiken we tokens niet zoals ze zijn bedoeld. Eigenlijk hebben we dit dan nodig: https://github.com/jpadilla/pyjwt/issues/143.
We gebruiken JWT nu eigenlijk alleen om op een ondertekende manier info door te sturen. Gebruik als access token kàn, maar da's echt wat anders.
JWT vervangt nu het voordeur/achterdeur mechanisme van de SSO.
In de oude situatie ging username/password (voor de eerste workflow) via de achterdeur. Nu via de voordeur, met een handtekening. Niks mis mee dat user/pass over de lijn gaat, want dat gaat het ook bij een inlogformulier. Alleen even zorgen dat het niet als GET parameter in de access log terecht komt.
JWT voegt in deze workflow dus weinig toe (we gebruiken al end-to-end encryption)?
Wat JWT hier toevoegt is een beetje extra controle dat het verzoek van een geconfigureerde server af komt.
En een beetje consistentie met de andere api calls.
Werkt prima. Wel zie ik in de logs van de SSO-server dubbele informatie langskomen: in requests wordt een (public) key als query parameter meegestuurd, terwijl die key ook al in de payload van de JWT te vinden is:
[14/Sep/2016 11:31:31] "GET /api/v2/authenticate/?message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb3JjZV9zc29fbG9naW4iOnRydWUsImRvbWFpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC8iLCJleHAiOjE0NzM4NDU3OTEsImtleSI6InUycEx3NXpEQ0d1Sjl0WWVlemZzWkNncDA1RzA4N1ZrbmJEeTA1VjVDbjBiTzk4YVQ1ZWxna3NRekk5SHY5ejIifQ.lQbmlQkLz7AYChhUMTqIqPTVCYP-JQiqje96gjySO3s&key=u2pLw5zDCGuJ9tYeezfsZCgp05G087VknbDy05V5Cn0bO98aT5elgksQzI9Hv9z2 HTTP/1.1" 302 0
[14/Sep/2016 11:31:31] "GET /accounts/login/?next=%2Fapi%2Fv2%2Fauthenticate%2F%3Fkey%3Du2pLw5zDCGuJ9tYeezfsZCgp05G087VknbDy05V5Cn0bO98aT5elgksQzI9Hv9z2%26message%3DeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb3JjZV9zc29fbG9naW4iOnRydWUsImRvbWFpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC8iLCJleHAiOjE0NzM4NDU3OTEsImtleSI6InUycEx3NXpEQ0d1Sjl0WWVlemZzWkNncDA1RzA4N1ZrbmJEeTA1VjVDbjBiTzk4YVQ1ZWxna3NRekk5SHY5ejIifQ.lQbmlQkLz7AYChhUMTqIqPTVCYP-JQiqje96gjySO3s HTTP/1.1" 200 6079
[14/Sep/2016 11:33:54] "POST /accounts/login/?next=%2Fapi%2Fv2%2Fauthenticate%2F%3Fkey%3Du2pLw5zDCGuJ9tYeezfsZCgp05G087VknbDy05V5Cn0bO98aT5elgksQzI9Hv9z2%26message%3DeyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb3JjZV9zc29fbG9naW4iOnRydWUsImRvbWFpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC8iLCJleHAiOjE0NzM4NDU3OTEsImtleSI6InUycEx3NXpEQ0d1Sjl0WWVlemZzWkNncDA1RzA4N1ZrbmJEeTA1VjVDbjBiTzk4YVQ1ZWxna3NRekk5SHY5ejIifQ.lQbmlQkLz7AYChhUMTqIqPTVCYP-JQiqje96gjySO3s HTTP/1.1" 302 0
[14/Sep/2016 11:33:54] "GET /api/v2/authenticate/?key=u2pLw5zDCGuJ9tYeezfsZCgp05G087VknbDy05V5Cn0bO98aT5elgksQzI9Hv9z2&message=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb3JjZV9zc29fbG9naW4iOnRydWUsImRvbWFpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMC8iLCJleHAiOjE0NzM4NDU3OTEsImtleSI6InUycEx3NXpEQ0d1Sjl0WWVlemZzWkNncDA1RzA4N1ZrbmJEeTA1VjVDbjBiTzk4YVQ1ZWxna3NRekk5SHY5ejIifQ.lQbmlQkLz7AYChhUMTqIqPTVCYP-JQiqje96gjySO3s HTTP/1.1" 302 0
Dat is een dubbeling inderdaad. Ik weet niet meer precies de reden. Volgens mij wilde @jackieleng het omdat je anders de jwt message twee keer uit moet pakken. Een keertje zonder handtekening-controle om de key uit de message te halen en vervolgens een tweede keer MET controle omdat je dan de key al hebt waarmee je de secret key op kan halen. Zoiets.
Mij lijkt het op zich prima om het dubbel te doen (dus eerst een keertje zonder, zie https://pyjwt.readthedocs.io/en/latest/usage.html#reading-the-claimset-without-validation) en daarna met verificatie.
Maar... daar moeten we eerst even over overleggen, liefst met @jackieleng erbij, die weet nog wat meer details. OK om dat in een andere PR te doen? Dan kunnen we deze en die van -client mergen.
Denk dat dit de reden is: https://github.com/lizardsystem/lizard-auth-server/blob/master/lizard_auth_server/forms.py#L79
Wat overigens overgenomen is van de vorige DecryptForm. Dus een soort extra nacontrole.
Op zich is nacontrole niet nodig als je alleen de key uit de message gebruikt. Die klopt dan per definitie.
Het vorige decrypt form is volgens mij iets dat echt encrypt i.p.v. alleen maar ondertekent/verifieerd. Dacht ik. Wij kunnen gewoon bij de key..
Hm, nee. Vorige decrypt form doet ongeveer hetzelfde.
Dus op zich kunnen we het nog versimpelen. In een ander pull request.
Ik heb nog iets gevonden dat niet backwards compatible is. Het lijkt mij een verbetering, maar ik meld het toch maar even.
Ik heb me verbaast over deze regel:
if self.user.is_staff:
# staff can access any site
return True
Iemand met staff status kan dus op iedere site inloggen. Ik zie niet in waarom dit zo zou moeten zijn. En waarom mag iemand met staff status dit wel en iemand met (alleen) superuser status dit niet?
In V2 kun je met staff status alleen niet meer inloggen op een site. Ik kan niet overzien of dit voor sommige gebruikers en/of sites gevolgen heeft.
Die "is_staff" is meer "alles en iedereen van N&S mag overal bij". Iedereen met de juiste ldap groep krijgt dat vinkje.
Maar inderdaad, we horen zelf niet overal meer bij te kunnen. En als dat toch moet dan moet het maar op een betere manier geregeld worden.
Server-side fix for https://github.com/lizardsystem/lizard-auth-client/issues/48