Sujet deuxième session 17 juin 2015
Lorsqu’ils vous est demandé d’écrire du code, vous pouvez donner votre réponse en vous basant sur le framework Node.js+Express ou sur PHP+Silex, au choix.
Durée 2h. Ordinateurs et téléphones interdits. Seuls documents admis : 2 feuilles A4 manuscrites recto-verso.
Côté client – HMTL, CSS, JS
Le but de cette partie est d’écrire un formulaire html pour récupérer une réservation d’avion, afin de procéder à l’enregistrement en ligne du passager. Il y a deux critères de recherche :
- le premier (par PNR) est d’entrer le numéro de réservation à 6 caractères parmi A-Z et 0-9, et le nom de famille du passager,
- le second (par Eticket) est d’entrer le numéro de billet electronique à 13 ou 14 chiffres et le code à 3 lettres majuscules de l’aéroport de départ (par ex CDG).
La compagnie aérienne a défini un gestionnaire POST à l’url /checkin/pnr
et
un gestionnaire GET à l’url /checkin/ebillet
.
Le premier accepte comme paramètres uniquement deux chaînes nommées pnr
et passengername
.
Le second prend comme paramètres deux chaînes nommées eticket
et
airport
. (Les gestionnaires envoient un message d’erreur si on leur passe
n’importe quel autre paramètre).
HTML et CSS
SANS UTILISER de JavaScript, écrivez le morceau de html/css produisant le rendu suivant. Ne mettez pas les en-têtes, allez directement à l’essentiel.
Les points suivants compteront dans la note :
- Tout votre code est situé dans une div d’identifiant
checkinoptions
, ayant comme style CSS :
div#checkinoptions {
width: 100%;
border: thin black dotted;
text-align: center;
}
- Les deux cadres PNR et Eticket doivent être de type
div
, doivent être deux frères dans le DOM, avoir comme largeur 45% bordure comprise, avoir une marge de 1% de la largeur de leur père dans le DOM. Leur bordure fait 2px de large et est bleue. - Le fait de cliquer sur les boutons check-in dirige l’utilisateur vers le gestionnaire prévu à cet effet.
- Les champs de formulaires sont des champs de texte, qui n’acceptent que les patterns spécifiés dans l’énoncé.
JavaScript
Les utilisateurs ne connaissant pas les codes de tous les aéroports par
coeur, la compagnie propose de mettre en ligne un fichier javascript
comme celui-ci à l’url /data/airports.js
:
var airports = {
'CDG' : 'Paris Charles de Gaulle, France',
'ORY' : 'Paris Orly, France',
'LAX' : 'Los Angeles, USA',
//[ ... etc (10000 lignes)...]
'YYZ' : 'Toronto, Canada'
};
window.onload = function() {
//TODO: A compléter
}
-
Donner la balise html qui permet de charger et executer ce code dans le document de la question précédente.
-
Complétez le code de la fonction (à l’endroit de
TODO: A compléter
), de sorte que lors du chargement de la page, le champ de texte où l’utilisateur devait entrer le code 3 lettres de l’aeroport soit supprimé et remplacé par un avec autant d’options que d’aéroports, mais que le bouton fonctionne encore.
Côté serveur – Express/Silex
On considère une application web composée des deux gestionnaires suivants (donnés d’abord en Node.js, puis en Silex) :
app.get('/button', function(req, res) {
db.query('SELECT COUNT(pushes) FROM button',
function(err, rows) {
res.render('button.twig', { count: rows[0].pushes });
});
});
app.get('/push', function(req, res) {
db.query('INSERT INTO button VALUES (NOW())');
res.redirect('/button');
});
$app->get('/button', function(Application $app) {
$row = $app['db']->fetch('SELECT COUNT(pushes) FROM button');
return $app['twig']->render('button.twig', array('count' => rows['pushes']);
});
$app->get('/push', function(Application $app) {
$app['db']->query('INSERT INTO button VALUES (NOW())');
return $app->redirect('/button');
});
On donne aussi un extrait du code du template button.twig
:
<form method="post" action="/push">
<input type="submit" value="Push me">
</form>
<!-- TODO à compléter -->
Web 1.0 : 4 points
-
Il y a une erreur bête (en dehors du fait que les erreurs de la base de données ne sont pas gérées). Trouvez-la et proposez une correction.
-
Que peut-on dire sur la structure de la table SQL
button
? -
Compléter le template
button.twig
pour qu’il affiche le nombre de fois que le bouton a été cliqué. -
L’utilisateur visite l’URL
/button
et clique le bouton.Décrire les requêtes HTTP (URL et contenu) envoyées par le browser et le contenu des réponses données par le serveur.
AJAX : 3 points
-
Modifier le gestionnaire de
/push
pour qu’il retourne le nombre de clics au format JSON, plutôt qu’une redirection. -
Modifier
button.twig
pour que, à la pression du bouton, le clic soit envoyé via une requête AJAX, et le nombre de clics mis à jour sans recharger la page.Vous pouvez utiliser un framework (par ex. JQuery) pour construire la requête AJAX et/ou modifier le DOM.
Note: La référence à https://www.reddit.com/r/thebutton n’est pas casuelle.
Théorie et sécurité
HTTP
-
Repérer les erreurs dans la requête et la réponse HTTP suivantes.
POST /toto/ HTTP/1.1 Host: www.toto.com/toto Set-Cookie: toto=123
200 OK HTTP/1.1 Cookie: toto=123 Host: www.toto.com Hello, toto!
-
On considère l’échange HTTP suivant (la réponse est laissée intentionnellement incomplète).
GET /home/ HTTP/1.1 Host: www.example.com Cookie: sessid=abc Referer: http://www.example.com/
HTTP/1.1 Server: Location:
GET /home/fr/ HTTP/1.1 Host: www.example.com Cookie: sessid=123456 Referer: http://www.example.com/home/
Complétez la réponse du serveur (deuxième encadré) en sorte que la totalité de l’échange soit cohérente.
Injections
On considère les gestionnaires suivants (en Node.js et en Silex)
app.post('/:usr/insert', function(req, res) {
db.query('INSERT INTO ' + req.params.usr + ' VALUES (?)',
[req.body.potatoes], ...);
});
app.get('/:usr/list', function(req, res) {
db.query('SELECT * FROM ' + req.params.usr,
function(err, rows) {
res.render('garden.twig', {
garden: rows,
user: req.params.usr });
});
});
$app->post('/{usr}/insert', function(Application $app, Request $req, $usr) {
$app['db']->query('INSERT INTO ' . $usr . ' VALUES (?)',
array($req->request->get('potatoes')));
...
});
$app->get('/{usr}/list', function(Application $app, $usr) {
$rows = $app['db']->query('SELECT * FROM ' + $usr);
return $app['twig']->render('garden.twig',
array('garden' => $rows, 'user': $usr ));
});
et l’extrait suivant du template garden.twig
.
<h1>Jardin de {{ user }}</h1>
<ul>
{% for veg in {{ garden }} %}
<li>{{ veg.potatoes | raw }}</li>
{% endfor %}
</ul>
Indiquer lesquelles des chaînes dans le tableau suivant donnent lieu à une injection, en précisant s’il s’agit d’une injection SQL ou XSS.
/{usr}/insert |
req.query.potatoes |
/{usr}/list |
|
''); DROP TABLE toto |
|||
'); DROP TABLE toto |
|||
toto VALUES (''); DROP TABLE toto |
|||
toto; DROP TABLE toto |
|||
<script>alert('xss');</script> |
|||
"; alert('xss'); |
|||
</h1> alert('xss'); |
|||
</li><script>alert('xss')</script> |