Pourquoi Google ajoute-t-il avant (1); à leurs réponses JSON?

4079

Pourquoi Google ajoute-t-il while(1);à ses réponses JSON (privées)?

Par exemple, voici une réponse lors de l'activation et de la désactivation d'un calendrier dans Google Agenda :

while (1);
[
  ['u', [
    ['smsSentFlag', 'false'],
    ['hideInvitations', 'false'],
    ['remindOnRespondedEventsOnly', 'true'],
    ['hideInvitations_remindOnRespondedEventsOnly', 'false_true'],
    ['Calendar ID stripped for privacy', 'false'],
    ['smsVerifiedFlag', 'true']
  ]]
]

Je suppose que c'est pour empêcher les gens d'en faire un eval(), mais tout ce que vous auriez vraiment à faire est de remplacer le whileet ensuite vous seriez prêt. Je suppose que la prévention eval est de s'assurer que les gens écrivent du code d'analyse JSON sûr.

J'ai également vu cela utilisé dans quelques autres endroits, mais beaucoup plus avec Google (Mail, Calendrier, Contacts, etc.) Curieusement, Google Docs commence par à la &&&START&&&place et Google Contacts semble commencer par while(1); &&&START&&&.

Que se passe t-il ici?

Jess
la source
45
Je pense que votre première impression est correcte. Si vous commencez à chercher du code et essayez de découper le flux d'entrée en fonction de la source, vous reconsidérerez et le ferez de manière sûre (et en raison des actions de Google, plus facile).
Esteban Küber
34
probablement une question de suivi: pourquoi google ajoute-t-il )]}'maintenant au lieu de while(1);? Les réponses seraient-elles les mêmes?
Gizmo
3
Empêcherait eval, mais pas avec une boucle infinie.
Mardoxx
9
Cela )]}'peut aussi être d'économiser des octets, comme Facebook utilisé for(;;);qui enregistre un octet :)
Gras Double
3
Afin d'empêcher la divulgation de json ieJSON hijacking
Ashraf.Shk786

Réponses:

4294

Il empêche le détournement JSON , un problème de sécurité JSON majeur qui est formellement résolu dans tous les principaux navigateurs depuis 2011 avec ECMAScript 5.

Exemple artificiel: supposons que Google ait une URL comme celle mail.google.com/json?action=inboxqui renvoie les 50 premiers messages de votre boîte de réception au format JSON. Les sites Web malveillants sur d'autres domaines ne peuvent pas faire de demandes AJAX pour obtenir ces données en raison de la même politique d'origine, mais ils peuvent inclure l'URL via une <script>balise. L'URL est visitée avec vos cookies, et en remplaçant le constructeur de tableau global ou les méthodes d'accesseur, ils peuvent avoir une méthode appelée chaque fois qu'un attribut d'objet (tableau ou hachage) est défini, leur permettant de lire le contenu JSON.

Le while(1);ou &&&BLAH&&&empêche cela: une requête AJAX mail.google.comaura un accès complet au contenu texte et pourra le supprimer. Mais une <script>insertion de balise exécute aveuglément le JavaScript sans aucun traitement, ce qui entraîne soit une boucle infinie soit une erreur de syntaxe.

Cela ne résout pas le problème de la contrefaçon de demande intersite .

rjh
la source
228
Pourquoi la demande d'obtention de ces données ne nécessite-t-elle pas un jeton CSRF à la place?
Jakub P.
235
Fait for(;;);le même travail? J'ai vu cela dans les réponses ajax de Facebook.
King Julien
183
@JakubP. Le stockage et la maintenance des jetons CSRF à l'échelle de Google nécessitent une grande quantité d'infrastructure et de coûts.
Abraham
127
@JakubP. les jetons anti-CSRF gâchent la mise en cache et nécessitent une certaine quantité d'évaluation cryptographique côté serveur. À l'échelle de Google, cela nécessiterait beaucoup de CPU. Ce type de déchargement au client.
bluesmoon
96
Il me semble qu'une meilleure façon serait de laisser le serveur envoyer le JSON uniquement si l'en-tête correct a été défini. Vous pouvez le faire dans un appel AJAX, mais pas avec des balises de script. De cette façon, les informations sensibles ne sont même jamais envoyées et vous n'avez pas à vous fier à la sécurité côté navigateur.
mcv
574

Il empêche la divulgation de la réponse via le détournement JSON.

En théorie, le contenu des réponses HTTP est protégé par la même politique d'origine: les pages d'un domaine ne peuvent pas obtenir d'informations à partir des pages de l'autre domaine (sauf autorisation explicite).

Un attaquant peut demander des pages sur d'autres domaines en votre nom, par exemple en utilisant une balise <script src=...>ou <img>, mais il ne peut obtenir aucune information sur le résultat (en-têtes, contenu).

Ainsi, si vous visitez la page d'un attaquant, il ne pourra pas lire votre e-mail depuis gmail.com.

Sauf que lors de l'utilisation d'une balise de script pour demander du contenu JSON, le JSON est exécuté en Javascript dans un environnement contrôlé par un attaquant. Si l'attaquant peut remplacer le constructeur Array ou Object ou une autre méthode utilisée pendant la construction de l'objet, tout élément du JSON passerait par le code de l'attaquant et serait divulgué.

Notez que cela se produit au moment où le JSON est exécuté en Javascript, pas au moment de son analyse.

Il existe plusieurs contre-mesures:

S'assurer que le JSON ne s'exécute jamais

En plaçant une while(1);déclaration avant les données JSON, Google s'assure que les données JSON ne sont jamais exécutées en Javascript.

Seule une page légitime peut obtenir l'intégralité du contenu, le while(1);supprimer et analyser le reste en JSON.

Des choses comme for(;;);ont été vues sur Facebook par exemple, avec les mêmes résultats.

S'assurer que le JSON n'est pas valide Javascript

De même, l'ajout de jetons invalides avant le JSON, comme &&&START&&&, garantit qu'il n'est jamais exécuté.

Toujours renvoyer JSON avec un objet à l'extérieur

C'est OWASPle moyen recommandé de se protéger contre le détournement JSON et c'est le moins intrusif.

Semblable aux contre-mesures précédentes, il s'assure que le JSON n'est jamais exécuté en Javascript.

Un objet JSON valide, lorsqu'il n'est entouré de rien, n'est pas valide en Javascript:

eval('{"foo":"bar"}')
// SyntaxError: Unexpected token :

C'est cependant du JSON valide:

JSON.parse('{"foo":"bar"}')
// Object {foo: "bar"}

Donc, assurez-vous de toujours renvoyer un objet au niveau supérieur de la réponse, assurez-vous que le JSON n'est pas du Javascript valide, tout en étant un JSON valide.

Comme indiqué par @hvd dans les commentaires, l'objet vide {}est du Javascript valide, et savoir que l'objet est vide peut lui-même être une information précieuse.

Comparaison des méthodes ci-dessus

La méthode OWASP est moins intrusive, car elle ne nécessite aucune modification de la bibliothèque cliente et transfère un JSON valide. Cependant, il n'est pas sûr que les bogues du navigateur passés ou futurs puissent échouer. Comme l'a noté @oriadam, il n'est pas clair si les données pourraient être divulguées dans une erreur d'analyse via une gestion des erreurs ou non (par exemple window.onerror).

Pour Google, la bibliothèque cliente doit prendre en charge la désérialisation automatique et peut être considérée comme plus sûre contre les bogues du navigateur.

Les deux méthodes nécessitent des modifications côté serveur pour éviter que les développeurs envoient accidentellement du JSON vulnérable.

Arnaud Le Blanc
la source
23
La recommandation de l'OWASP est intéressante en raison de sa simplicité. Quelqu'un sait-il pourquoi Google est plus sûr?
funroll
18
Je crois que ce n'est pas plus sûr en aucune façon. Fournir OWASP ici semble une raison suffisante pour +1.
30
Il peut être intéressant de noter pourquoi le retour d'un littéral d'objet échoue la scriptbalise ou la evalfonction. Les accolades {}peuvent être interprétées comme un bloc de code ou un littéral d'objet, et en soi, JavaScript préfère le premier. En tant que bloc de code, il est bien sûr invalide. Par cette logique, je ne vois aucun changement prévisible dans le comportement futur du navigateur.
Manngo
16
Un mauvais code ne suffit pas car un attaquant peut également pirater le gestionnaire d'erreurs de script du navigateur ( window.onerror) Je ne sais pas quel est le comportement des onerrorerreurs de syntaxe. Je suppose que Google n'était pas sûr non plus.
oriadam
12
"Un objet JSON valide, lorsqu'il n'est entouré de rien, n'est pas valide en Javascript:" - À l'exception du cas trivial d'un objet vide ( {}), qui est également un bloc vide valide. Si le fait de savoir que l'objet est vide peut être en soi une information précieuse, cela peut être exploitable.
371

C'est pour s'assurer qu'un autre site ne peut pas faire de trucs désagréables pour essayer de voler vos données. Par exemple, en remplaçant le constructeur du tableau , puis en incluant cette URL JSON via une <script>balise, un site tiers malveillant pourrait voler les données de la réponse JSON. En mettant un while(1);au début, le script se bloque à la place.

Une demande de même site utilisant XHR et un analyseur JSON distinct, d'autre part, peut facilement ignorer le while(1);préfixe.

bdonlan
la source
6
Techniquement, un analyseur JSON "normal" devrait donner une erreur si vous avez un préfixe.
Matthew Crumley
12
Les attaquants utiliseraient simplement un vieil <script>élément, pas un XHR.
Laurence Gonsalves
9
@Matthew, bien sûr, mais vous pouvez le supprimer avant de transmettre les données à l'analyseur JSON. Vous ne pouvez pas faire ça avec un <script>tag
bdonlan
7
Y en a-t-il des exemples? Le remplacement du constructeur de tableau est à nouveau référencé, mais c'est un bug corrigé depuis longtemps. Je ne comprends pas comment on aurait accès aux données reçues via la balise de script. J'adorerais voir une implémentation factice qui fonctionne dans un navigateur récent.
Dennis G
7
@joeltine, non, ce n'est pas le cas. Voir stackoverflow.com/questions/16289894/… .
111

Ce serait difficile pour un tiers d'insérer la réponse JSON dans un document HTML avec la <script>balise. N'oubliez pas que la <script>balise est exemptée de la politique de même origine .

Daniel Vassallo
la source
21
Ce n'est qu'une demi-réponse. S'il n'y avait pas l'astuce de remplacer les constructeurs Objectet Array, exécuter une réponse JSON valide comme s'il s'agissait de JavaScript serait totalement inoffensif en toutes circonstances. Oui, le while(1);empêche la réponse d'être exécutée en JavaScript si elle est ciblée par une <script>balise, mais votre réponse n'explique pas pourquoi c'est nécessaire.
Mark Amery
mais comment cela empêchera les iframes
Ravinder Payal
La balise de script n'est jamais exemptée de la même politique d'origine. Pourriez-vous clarifier cela pour moi?
Suraj Jain
82

Remarque : à partir de 2019, la plupart des anciennes vulnérabilités qui conduisent aux mesures préventives discutées dans cette question ne sont plus un problème dans les navigateurs modernes. Je vais laisser la réponse ci-dessous comme une curiosité historique, mais vraiment tout le sujet a radicalement changé depuis 2010 (!!) lorsque cela a été demandé.


Il l'empêche d'être utilisé comme cible d'une simple <script>balise. (Eh bien, cela ne l'empêche pas, mais cela le rend désagréable.) De cette façon, les méchants ne peuvent pas simplement mettre cette balise de script dans leur propre site et s'appuyer sur une session active pour permettre de récupérer votre contenu.

modifier - notez le commentaire (et les autres réponses). Le problème concerne les installations intégrées subverties, en particulier les constructeurs Objectet Array. Ceux-ci peuvent être modifiés de telle sorte que JSON autrement inoffensif, une fois analysé, pourrait déclencher le code de l'attaquant.

Pointu
la source
17
Ce n'est qu'une demi-réponse. S'il n'y avait pas l'astuce de remplacer les constructeurs Objectet Array, exécuter une réponse JSON valide comme s'il s'agissait de JavaScript serait totalement inoffensif en toutes circonstances. Oui, le while(1);empêche la réponse d'être exécutée en JavaScript si elle est ciblée par une <script>balise, mais votre réponse n'explique pas pourquoi c'est nécessaire.
Mark Amery
11

Étant donné que la <script>balise est exemptée de la même politique d'origine qui est une nécessité de sécurité dans le monde Web, while(1)lorsqu'elle est ajoutée à la réponse JSON, elle empêche son utilisation abusive dans la <script>balise.

Krishna Ganeriwal
la source