Perte de session sur WebService Rails depuis iOS 7
Salut,
Quelques utilisateurs m'ont signalé un problème de connexion au serveur depuis qu'ils ont mis à jour leur iPhone ou iPad en iOS 7. L'application est encore compilée pour iOS 6 en plus...
Pour ma part, tout baigne, je n'ai pas réussi à reproduire le problème.
Je suis absolument sûr que le problème est lié au nouvel OS.
Je suis allé voir les logs du serveur Rails (3.0.20). Au départ, je pensais à un problème serveur mais l'origine du problème semble bien être iOS 7.
Donc en analysant les logs du serveur, j'ai découvert que la session serveur n'était pas toujours conservée, pour certains utilisateurs.
Je suis allé plus loin, et en faisant des tests avec un utilisateur, sur 4 heures d'intervalle, il y a eu 10 échecs puis 1 réussite à la fin, suivie d'autres réussites. C'est très dur à débuguer car je n'arrive pas à reproduire le problème.
Voilà ce qu'il se passe en gros:
Sous iOS 6, la session est bien conservée sur le serveur, à coup sûr:
Started POST "/users/login" for 91.45.167.111 at Fri Sep 27 13:47:34 +0000 2013
Processing by UsersController#login as JSON
Parameters: {"password"=>"[FILTERED]", "username"=>"Horst", "bundle"=>"VinoCellar"}
USER_AGENT vinocellar/2.1.1 (ipad; ios 6.1.3; scale/1.00)
Completed 200 OK in 137ms (Views: 0.3ms | ActiveRecord: 26.2ms)
Started GET "/services/pull" for 91.45.167.111 at Fri Sep 27 13:47:35 +0000 2013
Processing by Vinocloud::ServicesController#pull as JSON
Parameters: {"locale"=>"ws"}
USER_AGENT vinocellar/2.1.1 (ipad; ios 6.1.3; scale/1.00)
SESSION user_id 5454 la session est bien là !
Completed 200 OK in 8262ms (Views: 1256.6ms | ActiveRecord: 6438.8ms)
Sous iOS 7, la session est perdue (pas toujours, je le répète):
Started POST "/users/login" for 91.45.139.190 at Sat Sep 28 13:09:08 +0000 2013
Processing by UsersController#login as JSON
Parameters: {"password"=>"[FILTERED]", "username"=>"Horst", "bundle"=>"VinoCellar"}
USER_AGENT: vinocellar/2.1.1 (iphone; ios 7.0.2; scale/2.00)
Completed 200 OK in 133ms (Views: 0.3ms | ActiveRecord: 12.4ms)
Started GET "/services/pull" for 91.45.139.190 at Sat Sep 28 13:09:09 +0000 2013
Processing by ServicesController#pull as JSON
Parameters: {"locale"=>"ws"}
USER_AGENT: vinocellar/2.1.1 (iphone; ios 7.0.2; scale/2.00)
SESSION Le user_id dans la session est perdu ici !!! Donc redirection vers le login.
Redirected to http://www.viniapps.com/ws/vinocloud/login
Completed 302 Found in 1ms
Là , où je perds la boule, c'est que ça marche pour certains utilisateurs même sous iOS 7 !
Started POST "/users/login" for 93.133.25.65 at Thu Sep 26 19:32:09 +0000 2013
Processing by UsersController#login as JSON
Parameters: {"password"=>"[FILTERED]", "username"=>"Hut", "bundle"=>"VinoCellar"}
USER_AGENT vinocellar/2.1.1 (iphone; ios 7.0; scale/2.00)
Completed 200 OK in 125ms (Views: 0.3ms | ActiveRecord: 6.0ms)
Started GET "/services/pull" for 93.133.25.65 at Thu Sep 26 19:32:12 +0000 2013
Processing by ServicesController#pull as JSON
Parameters: {"locale"=>"ws"}
USER_AGENT vinocellar/2.1.1 (iphone; ios 7.0; scale/2.00)
SESSION user_id 1675 la session est bien conservée !!
Completed 200 OK in 6292ms (Views: 1129.8ms | ActiveRecord: 5390.4ms)
Par contre, jamais de problème de session sous iOS 6.1.3...
J'utilise AFNetworking pour envoyer les requêtes JSON.
Savez-vous si Apple changé quelque chose au niveau des requêtes sur iOS 7 ? OU autre ?
Quelque chose qui ne plairait pas à la mémorisation de la session sur mon serveur Rails ?
Réponses
Salut,
Oui ils ont introduit NSURLSession qui remplace NSURLConnection ( mais qui n'est pas deprecated pour l'instant).
Moi je créerai une autre branche et je migrerai sur AFNewtworking 2.0 ( qui se base sur NSURLSession sous iOS 7 et garde la comptabilité pour iOS 6.. avec l'utilisation de NSURLConnection) et je testerai avec ça.
Dans tous les cas (NSURLSession ou NSURLConnection), ça n'explique pas pourquoi la session n'est pas conservée sur le serveur.
Vous allez me dire que c'est un problème lié au serveur mais alors pourquoi jamais ce problème sous iOS 6 ou même iOS 5 ?
Encore un test ou j'ai ajouté des logs:
On voit clairement que une seconde après, la session est vide.
Bon, alors je continue mon enquête.
Avec les logs et les heures, je suis allé fouiller ma table de sessions.
Il s'avère que les sessions qui sont vides lors de la deuxième requête ont l'air tronquées en base.
Le contenu d'une session normale ressemble à ça:
On dirait qu'il manque plein d'infos... (même si dans ce cas, l'utilisateur est bien identifié, cf logs plus hauts)
Pourtant, elles sont toutes créées de la même manière évidemment.
Sinon tu es sûr (surtout vu le côté aléatoire de la chose) que ce n'est pas une fausse analyse que de mettre ça sur iOS7 ?
Peut-être que le problème était là avant mais vient d'un autre fait, et le fait qu'il surgisse au moment où tout le monde passe à iOS7 est peut-être un hasard ? (Genre tu as plus d'utilisateurs qui se connectent en même temps car tu viens de mettre à jour ton appli donc ça l'a remise au goût du jour et leur a rappelé qu'elle existe, mais ça n'a rien à voir avec leur passage à iOS7?)
Je veux dire fais gaffe à pas partir sur une mauvaise piste en supposant une cause qui n'est peut-être pas la bonne quoi
Bein je vais aller essayer de faire ça... (jamais fait).
C'est sûr que c'est p-e une mauvaise piste.
Pourtant, je viens d'analyser 80 Mo de logs et je n'ai pas vu le problème sur iOS 6...
Oh ça va vite, y'a même des sites pour ça (pour décoder) !
Je savais même pas.
Le _csrf_token n'est pas créé quand il y a problème.
Reste à savoir pourquoi.
Mais le user_id est bien en session.
En même temps, la requête pull qui suit est GET et ne doit pas utiliser le _csrf_token...
EDIT: je suis allé voir une autre Rails app que j'ai et toutes mes sessions sont parfaites dans ma table session.
(C'est des idées en vrac, je sais pas du tout si c'est lié je capte pas trop ton histoire de csrf_token machin)
Non, je ne pense pas.
Il n'y a qu'une requête pour la création d'une session: /login
Dès que le user est logué, il crée la session. Cette dernière semble être erratique. Il manque des infos dans les sessions qui posent problème. Le _csrf_token est très important en Rails pour la vérification des requêtes, il vérifie que les tokens envoyés sont les bons... sauf que là , il n'y a rien à vérifier puisqu'il n'a pas été créé en session apparemment.
Peux être que tu utilise le même login sur plusieurs devices ( tu te connecte avec le même login alors que ya déjas une session ouverte comme c'est déjas connecté ) et que ton API serveur gère mal ça ?
Problème d'encoding des strings ? Normalement ton base64 est composé de quoi ?
Non, aucun problème avec ça, je peux le faire moi-même.
Je sais pas.
Je sais que Rails génère les token comme ça:
session[:_csrf_token] ||= SecureRandom.base64(32)
Bon, je viens de regarder tous les logs de la nuit.
1) Tous les problèmes sont sous iOS 7.
2) Sur iOS 7, ça concerne 1 utilisateur sur 10.
ça va pas être évident à résoudre !
1 sur 10, cela veut dire que cela peut-être une combinaison entre l'os et le contenu des données transmises (ou de la spécificité connexion (IPV6 ?)).
En ce qui concerne les données, utilises-tu les nouvelles API pour encoder en Base64 sur iOS 7 ?
Il faut savoir qu'il y a plusieurs variantes d'encodage en Base64 qui ne portent que sur quelques caractères (/, :, .).
Il se pourrait que ton appli Rail n'arrive pas à décoder tout le message quand les données (login, mdp) contiennent ces caractères.
En ce qui concerne des paramètres de connexions, les sessions incluent parfois l'IP pour améliorer la sécurité mais je ne sais pas si iOS 7 peut avoir changé quelque chose à ce sujet.
Après il y a la méthode systématique :
Si on écarte un problème de timing ou d'ordre de requête.
Le problème ne peut venir que du contenu de la requête HTTP ou des informations sur la connexion (adresse IP, autre?) qui passe du serveur à l'appli.
Tu n'as qu'à dumper le contenu complet de ta requête (headers+body) au départ de iOS, à l'arrivée sur le serveur et à l'arrivée dans l'appli Rail.
Si tu peux faire ça pour un même jeu de donnée au départ de l'appli (Login+mdp+ip, etc) en ios6 et 7, tu trouveras forcément la différence.
pb uniquement sur 5s car proc 64 bits ?
Les erreurs sont sur IPV4.
Non. L'appli est encore iOS 6 SDK même si elle tourne sur iOS 7...
On parlait de Base64 car Rails encode le token en Base64.
Il n'y a pas l'IP dans les sessions Rails (par défaut). Mais je pourrais les rajouter, ça ne servirait à rien.
Je confirme, car les 2 requêtes sont bien à la suite.
Oui, c'est une piste.
Ce matin, j'ai forcé la durée de la session Rails à 30 minutes.
Et ajouté d'autres logs.
J'attends les tests de mes utilisateurs.
Ok. Mais n'encodes-tu pas le login et mot de passe ? (De toutes façons si tu restes sur les même méthodes de calculs ça ne peut pas être ça).
Il y a eu des problèmes avec le réglage des cookies (par exemple : https://discussions.apple.com/message/23067423#23067423), mais tu sembles dire que le problème se produit lors du stockage de la session côté serveur...
Oui, un utilisateur vient de me dire que l'activation des cookies dans safari changerait la donne !
Pourtant, je n'utilise pas les cookies mais uniquement les sessions stockées en base...
Je continue mon enquête...
bah en général tu as quand même au moins le numéro de session stocké sous forme de cookie, sinon ça passe dans l'URL mais tu le saurais dans ce cas-là .
Je confirme.
Je n'arrive plus à synchroniser si mes cookies sont désactivés.
Quel rapport avec iOS 7, ils ont changé les réglages par défaut ?
J'espère que la seule solution n'est pas de faire passer le session_id dans l'URL ?
Sinon, je vais avoir l'impression de faire un site sous PHP 3 en l'an 2000...
Après c'est sensé être transparent...
Tout à fait.
Je suis entièrement d'accord.
Par contre, j'ai poussé les tests.
J'ai désactivé les cookies sous iOS 6.1.3... et bang, ça marche quand-même !
Le problème n'est donc QUE quand les cookies sont désactivés sous iOS 7, mais pas sous iOS 6.
Ils ont peut-être corrigé un bug sous iOS7 qui faisait qu'avant sous iOS6 certains cookies passaient même quand l'option était désactivée, alors que maintenant c'est corrigé et du coup ça passe plus ?
<troll>
Si tu travaillais en mode stateless, avec un WS qui authentifie en DIGEST ça ne serait pas arrivé tout ça ^^
REST FTW !
</troll>
Impossible de mettre la main sur le vrai changelog de iOS 7 (en tout cas, je le vois pas sur l'ADC)
C'est le moment de dumper tes headers HTTP pour confirmer le problème des cookies mais c'est surement ça.
Ton test sur iOS6 ne prouve sans doute pas grand chose car la gestion des cookies semblent assez complexe et il semble qu'il faut réinstaller l'app pour être sur des paramètres qu'elle utilise.
Voir ce post qui semble faire un tour assez complet de l'état actuel : http://www.inmite.eu/en/blog/20130928-cookies-on-ios7-can-not-login.
La solution :
The solution is pretty simple. You have to apply your own policy. Just put following piece of code to your application's starÂtup and force policy to pre-iOS7 default value:
[[NSHTTPCookiÂeStorage sharedHTTPCooÂkieStorage] setCookieAccepÂtPolicy:NSHTTPCoÂokieAcceptPoliÂcyAlways]
Et au même moment, Céroce m'a envoyé ce lien:
https://devforums.apple.com/message/898034#898034
Je teste ça et je reviens vers vous.
Oui, ça je l'avais trouvé. Je cherchais un vrai changelog plus complet et plus technique, et pas un "what's new".