web-dev-qa-db-fra.com

OAuth2 Cross Site Request Forgery et paramètre d'état

http://tools.ietf.org/html/draft-ietf-oauth-v2-30#section-10.12 dit:

Le client DOIT implémenter la protection CSRF [...] généralement réalisée en exigeant que toute demande envoyée au point de terminaison URI de redirection inclue une valeur qui lie la demande à l'état authentifié de l'agent utilisateur (par exemple un hachage du cookie de session [...] ]

Cela ne dit pas grand chose sur la mise en œuvre, cependant. Cela ne fait que mettre en lumière le fonctionnement du CSRF:

Une attaque CSRF contre l'URI de redirection du client permet à un attaquant d'injecter son propre code d'autorisation ou jeton d'accès, ce qui peut amener le client à utiliser un jeton d'accès associé aux ressources protégées de l'attaquant plutôt que celui de la victime (par exemple, enregistrer les informations de compte bancaire de la victime dans une ressource protégée contrôlée par l'attaquant)

Mais l'utilisation du mot "plutôt" rend plutôt la déclaration sans valeur.

Je réfléchis à l'implémentation de "l'état" dans GAE (en utilisant Webapp2). Il serait plus simple de commencer à savoir comment un pirate pourrait utiliser un CSRF contre OAuth2. Je n'ai trouvé qu'un seul bon article à ce sujet: "Cross Site Request Forgery and OAuth2" .

Malheureusement, bien que ce billet de blog soit bien écrit, il n'y a pas beaucoup d'informations à part expliquer OAuth2. Les exemples ne fonctionnent pas et je ne connais pas Spring. Pourtant, j'ai trouvé une recommandation intéressante: le serveur se connectant à un fournisseur OAuth2 devrait stocker "state" comme une clé de session aléatoire (par exemple "this_is_the_random_state": "this_doesn't_matter") , et non une valeur sous une clé statique (par exemple "état": "random_state_string").


Ma question est, quelle est la mise en œuvre sensée de l '"état"?

  • Doit-on hacher l'état généré aléatoirement, ou la même valeur peut-elle être stockée et envoyée au fournisseur OAuth2?
  • Y a-t-il une différence ici si le backend de session est des cookies sécurisés ou une technologie de stockage côté serveur (par exemple dans GAE Memcache ou une base de données)?
  • L'état doit-il être stocké sous forme de clé comme suggéré?
  • L'État doit-il avoir une période de validité, ou la session (s'il y en a une) est-elle suffisante à vie?
39
Markus von Broady

Je vais simplifier ce problème. Cross-Site Request Forgery et Clikjacking les attaques sont utiles car elles peuvent forcer le navigateur d'une victime à effectuer des actions contre sa volonté.

La mention de 10.12. Cross-Site Request Forgery et 10.13. Clickjacking dans le OAuth v2 RFC ont fondamentalement la même préoccupation. Si un attaquant peut forcer le navigateur d'une victime à s'authentifier, il s'agit d'une étape utile pour forcer le navigateur de la victime à effectuer d'autres actions.

   in a clickjacking attack, an attacker registers a legitimate client
   and then constructs a malicious site in which it loads the
   authorization server's authorization endpoint web page in a
   transparent iframe overlaid on top of a set of dummy buttons, which
   are carefully constructed to be placed directly under important
   buttons on the authorization page.  When an end-user clicks a
   misleading visible button, the end-user is actually clicking an
   invisible button on the authorization page (such as an "Authorize"
   button).  This allows an attacker to trick a resource owner into
   granting its client access without their knowledge.

Source: 10.13. Détournement de clics

Par exemple, Stack Overflow utilise OAuth et est vulnérable à cette attaque. Si vous visitez StackOverflow et que vous êtes actuellement connecté à votre fournisseur OAuth, vous serez automatiquement connecté à StackOverflow. Par conséquent, un attaquant pourrait automatiquement connecter une victime en chargeant Stack Oveflow dans un iframe. Si Stack Overflow avait également une vulnérabilité CSRF ( et il l'a eu! ), alors un attaquant pourrait authentifier automatiquement le navigateur d'une victime et effectuer une attaque CSRF (Session Riding), Clickjacking ou XSS contre stackoverflow.com dans une attaque en chaîne .

14
rook

Voyons comment cette attaque fonctionne.

L'attaque

  1. Je visite certains site Web du client et entame le processus d'autorisation de ce client pour accéder à certains fournisseur de services en utilisant OAuth

  2. Le client demande au fournisseur de services l'autorisation de demander l'accès en mon nom, ce qui lui est accordé

  3. Je suis redirigé vers le site Web du fournisseur de services, où j'entrerais normalement mon nom d'utilisateur/mot de passe afin d'autoriser l'accès

  4. Au lieu de cela, je piège/empêche cette demande et enregistre son URL

  5. Je vous demande en quelque sorte de visiter cette URL à la place. Si vous êtes connecté au fournisseur de services avec votre propre compte, alors vos informations d'identification seront utilisées pour émettre un code d'autorisation

  6. Le code d'autorisation est échangé contre un jeton d'accès

  7. Maintenant mon compte sur le client est autorisé à accéder à votre compte sur le fournisseur de services

Alors, comment pouvons-nous empêcher cela en utilisant le paramètre state?

La prévention

  1. Le client doit créer une valeur qui est en quelque sorte basée sur le compte de l'utilisateur d'origine (un hachage de la clé de session de l'utilisateur, par exemple). Peu importe ce que c'est tant qu'il est unique et généré en utilisant des informations privées sur l'utilisateur d'origine.

  2. Cette valeur est transmise au fournisseur de services dans la redirection de l'étape trois ci-dessus

  3. Maintenant, je vous invite à visiter l'URL que j'ai enregistrée (étape cinq ci-dessus)

  4. Le code d'autorisation est émis et renvoyé au client dans votre session avec le paramètre state

  5. Le client génère une valeur state basée sur vos informations de session et la compare à la valeur state qui a été renvoyée de la demande d'autorisation au prestataire de services. Cette valeur ne correspond pas au paramètre state de la demande, car cette valeur state a été générée en fonction de ma session informations, il est donc rejeté.

Vos questions

  • Est-ce que l'état généré aléatoirement doit être haché ou la même valeur peut-elle être stockée et envoyée au fournisseur OAuth2?

Le fait est que l'attaquant ne devrait pas être en mesure de générer une valeur d'état spécifique à un utilisateur donné. Cela devrait être impossible à deviner.

  • Y a-t-il une différence ici, si le back-end de session est des cookies sécurisés ou un stockage côté serveur (dans GAE Memcache ou une base de données)?

Je ne pense pas que cela compte (si je vous comprends bien)

  • Doit-on enregistrer l'état sous forme de clé comme suggéré?

Je ne sais pas ce que ça veut dire.

  • L'État doit-il avoir une période de validité, ou une session (s'il y en a une) la durée de vie est-elle suffisante?

Oui, l'état doit avoir une expiration. Cela ne doit pas nécessairement être lié à la session, mais cela pourrait l'être.

43
Wayne Burkett

Tiré de L'importance du paramètre d'état dans OAuth2 :

C'est là que l'objet "état" dans OAuth 2 entre en jeu. En soumettant toujours un état non devinable lors du POSTage au point de terminaison d'autorisation, l'application cliente peut être certaine que le code d'accès obtenu du serveur d'autorisation sont en réponse à des demandes faites par lui plutôt qu'à une autre application cliente.

Exemple:

https://example.com/as/authorization?client_id=client1&response_type=code&scope=openid &state=7tvPJiv8StrAqo9IQE9xsJaDso4

Pour que le paramètre d'état soit utile pour empêcher des attaques CSRF comme celle-ci, toutes les demandes adressées au serveur OAuth doivent inclure un paramètre d'état que le client peut utiliser pour s'authentifier. Lors de l'envoi d'un paramètre d'état, la spécification OAuth stipule que le serveur d’autorisations doit le renvoyer textuellement au client. Cela se fera en le plaquant sur l’URL de rappel du client. Le client doit recevoir cet état et être programmé pour accepte uniquement les redirections avec un état vérifiable. S'il s'agit d'un dictionnaire conservé en mémoire ou si une valeur recalculable appartient au programmeur client.

Pour décider comment implémenter cela, une suggestion est d'utiliser une clé privée avec quelques variables facilement vérifiables, par exemple, l'ID client et un cookie de session, pour calculer une valeur hachée. Cela se traduira par une valeur d'octet qui sera difficile à deviner sans la clé privée. Après avoir calculé un tel HMAC, codez en base 64 et transmettez-le au serveur OAuth comme paramètre d'état. Une autre suggestion est de hacher la date et l'heure actuelles. Cela nécessite que votre application économise le temps de transmission afin de le vérifier ou pour permettre une période mobile de validité (par exemple, en utilisant TOTP).

Le sien est un simple Python qui utilise la deuxième suggestion en utilisant datetime:

def generate_state_parameter(client_id, private_key):
    date = datetime.datetime.today()
    raw_state = str(date) + client_id
    hashed = hmac.new(private_key, raw_state, sha1) state = base64.b64encode(hashed.digest())
    return (state, date)
4
Aydin K.