Fork me on GitHub

Sujet première session 13 mai 2015

Rédigez la partie 1 (Côté client) sur une copie séparée. N’oubliez pas de mettre votre code barres sur chacune des copies.

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 (7 points)

On suppose que le client possède la feuille de style ./css/boites.css suivante :

section {
    border: thin black outset;
    padding: 0px;
    margin-bottom: 10px;
    background-color: #eee; /* gris très clair */
}
section > h2:first-child {
    padding: 0px;
    margin: 0px;
    background-color: #888; /* gris foncé */
    color: white;
    text-align: center;    
}
.table2cols {
    padding: 0px;
    width: 500px;
    border: 1px black dashed;
}
.table2cols td {
    padding: 0px;
    margin: 0px;
    border: 1px black solid;
    width: 50%;
    text-align: center;
}
input, button {
    /* Certains champs de formulaires ont le mauvais goût d'avoir 
     des bordures et un padding, ce qui complique inutilement 
     leur dimensionnement. Cette propriété CSS fait en sorte que 
     pour ces éléments, width et height incluent le padding et 
     la bordure. */
    box-sizing: border-box;
}

HTML : 2 points

Ecrivez une page HTML 5 qui inclut cette feuille de style externe, qui l’utilise, et qui reproduit le contenu de l’encadré ci-dessous, tout en respectant les consignes suivantes :

Ma TODO list
  1. Un premier truc
  2. Un second truc
  3. Un troisième truc
Actions

Les jours de la semaine
LundiMonday
MardiTuesday
MercrediWednesday
JeudiThursday

CSS : 2 points

Note importante : Toutes les questions qui suivent necessitent entre 1 et 5 lignes de code maximum.

  1. Sans toucher au code html, ajouter une règle CSS de sorte que les champs de formulaires et les boutons qui sont dans la boîte Actions occupent désormais 100% de la largeur de la boîte et soient placés les uns au dessous des autres, comme dans l’encadré ci-dessous.
Actions

  1. Ajouter une règle CSS pour la balise body de sorte que la largeur de son contenu soit 800px, entouré par une bordure bleue de 2pixels de large, un espace vide de 50pixels dans toutes les directions entre la bordure et le contenu, un espace de 10px en haut de la bordure, et un espace symmétrique à gauche et à droite de la bordure.

  2. Ajouter des règles CSS qui changent la couleur de fond des lignes de la table des jours de la semaine : les lignes paires en blanc et les lignes impaires en gris clair (couleur #ccc)

  3. Ajouter une règle CSS qui met en gras et colore en rouge le texte des lignes de la table des jours de la semaine uniquement quand la souris passe dessus.

JS : 3 points

Note importante : Toutes les questions qui suivent necessitent entre 1 et 5 lignes de code JavaScript maximum. Vous avez le droit d’utiliser des frameworks (JQuery ou autres).

  1. Ecrivez un gestionnaire d’évènements qui supprime tous les items de la TODO list lorsque l’on clique sur le bouton « Tout Effacer ».

  2. Ecrivez un gestionnaire d’évènements qui ajoute ce que l’on a entré dans le champ de texte à TODO List quand on clique sur « Ajouter ». Efforcez-vous d’éviter les attaques XSS, injection html ou injection de script.

  3. On souhaite écrire un gestionnaire d’événements qui supprime la ligne de la table des jours sur laquelle on clique. On suppose que chaque élément du DOM possède une methode remove() qui le supprime. On va choisir un gestionnaire d’évènement parmi les trois candidats suivants :

function removeRow_v1(event) {
  event.target.remove();
}
function removeRow_v2(event) {
  event.currentTarget.remove();
}
function removeRow_v3(event) {
  var elem1 = event.target;
  var elem2 = event.currentTarget;
  if (elem1 === elem2) return;
  while (elem1.parentNode !== elem2)
     elem1 = elem1.parentNode;
  elem1.remove();
}

précisez pour chacun s’il peut fonctionner, et si oui, à quel(s) élément(s) et à quel événement il faut l’attacher (donner la/les lignes JavaScript qui fait cela). Si un gestionnaire ne peut pas fonctionner correctement, préciser pourquoi.

Côté serveur – Express/Silex (7 points)

On considère une application web composée des deux gestionnaires suivants (donnés d’abord en Node.js, puis en Silex) :

app.get('/consulter', function(req, res) {
	res.render('consulter.twig');
});

app.get('/inventaire', function(req, res) {
	db.query('SELECT * FROM items WHERE name LIKE ? AND color=?',
		     [req.body.name, req.body.color],
			 function (err, rows) {
				 res.render('liste.twig', { 'items' : rows });
			 });
});
$app->get('/consulter', function(Application $app) {
	return $app['twig']->render('consulter.twig');
});

$app->get('/inventaire', function(Application $app, Request $req) {
	$rows = $app['db']->fetchAll(
		'SELECT * FROM items WHERE name LIKE ? AND color=?',
		[$req->request->get('name'), $req->request->get('color')]);
	return $app['twig']->render('liste.twig', array('items' => $rows));
});

On donne aussi un extrait du code du template consulter.twig :

<form method="get" action="/inventaire">
	<input type="text" name="name">
	<select name="color">
		<option value="jaune">jaune</option>
		<option value="rouge">rouge</option>
	</select>
	<input type="submit" value="Chercher">
</form>

et de liste.twig :

<table>
	{% for it in items %}
	<tr>
		<td>{{ it.name }}</td>
		<td>{{ it.color }}</td>
		<td>{{ it.size }}</td>
		<td>{{ it.shape }}</td>
	</tr>
	{% endfor %}
</table>

Web 1.0 : 4 points

  1. Il y a une erreur bête dans les gestionnaires. Trouvez-la et proposez une correction.

  2. Que peut-on dire sur la structure de la table SQL items ?

  3. L’utilisateur visite l’URL /consulter, remplit le champ name avec le texte « toto », choisit la couleur « rouge », et soumet le formulaire.

    Décrire la requête HTTP (URL et contenu) envoyée par le browser et le contenu de la réponse donnée par le serveur.

  4. On définit une liste de couleurs permises dans un tableau global défini dans l’application. Par exemple, en JavaScript

    var colors = ['jaune', 'rouge', 'vert', 'bleu'];
    

    ou en PHP

    $colors = array('jaune', 'rouge', 'vert', 'bleu');
    

    Modifier le gestionnaire de /consulter et le template consulter.twig pour que la liste des couleurs sélectionnables soit générée dynamiquement à partir du tableau colors.

AJAX : 3 points

  1. Modifier le gestionnaire de /inventaire pour qu’il retourne les données au format JSON plutôt que dans une page HTML.

  2. Modifier le formulaire de consulter.twig pour que, à la pression du bouton, les données soient téléchargées de /inventaire via une requête AJAX, et affichées dans la même page.

    Vous pouvez utiliser un framework (par ex. JQuery) pour construire la requête AJAX. Vous pouvez utiliser la fonction de bibliothèque suivante pour générer le tableau HTML.

    function createTable(data) {
        var tab = docuement.createElement('table');
        for (var i = 0; i < data.length; i++) {
            var ligne = document.createElement('tr');
            ligne.innerHTML = "<td>" + data[i].name +
                              "</td><td>" + data[i].color +
                              "</td><td>" + data[i].size +
                              "</td><td>" + data[i].shape +
                              "</td>";
            tab.appendChild(ligne);
        }
        return tab;
    }
    

Théorie et sécurité (7 points)

HTTP : 2 points

On Considère l’échange HTTP suivant (les requêtes sont alignées à gauche, les réponses à droite)

GET / HTTP/1.1
Host: www.example.com
Cookie: sessionid=123aef345ff90
HTTP/1.1 302 Found
Location: http://www.example.com/home
Set-Cookie: toto=titi
  1. Écrire la requête HTTP envoyée par le browser à la suite de cet échange. Préciser, en particulier, le contenu des entêtes Host, Cookie et Referer.

Le serveur répond avec une nouvelle redirection :

HTTP/1.1 302 Found
Location: http://www.other.com/
  1. Même question qu’auparavant : Écrire la requête HTTP envoyée par le browser. Préciser, en particulier, le contenu des entêtes Host, Cookie et Referer.

XSS : 2 points

On considère le template suivant

<html>
<head>
	<script>
		var name = "{{ name }}";
	</script>
	<script src="{{ user_script }}"></script>	
</head>
<body>
	<a href="{{ action | raw }}">Click me</a>
</body>
</html>

servi par la ligne de code (en PHP)

return $app['twig']->render('template.twig', array(
	'name' => $req->request->name,
	'user_script' => $req->request->user_script,
	'action' => $req->request->action
));

ou bien (en Node.js)

res.render('template.twig', {
	'name'        : req.body.name,
	'user_script' : req.body.user_script,
	'action'      : req.body.action
});

Pour chacune des chaînes suivantes, dire si elle permet de réaliser une injection XSS, et dans ce cas à quelle(s) variable(s) (parmi name, user_script et action) elle peut être assignée pour mener l’attaque :

  1. "><script>alert('xss');</script>,
  2. (function() { alert('xss'); })(),
  3. https://www.evil.com/script.js,
  4. ";alert('xss')//,
  5. " alert('xss')//,
  6. javascript:alert('xss').

Cross-domain policy : 3 points

L’URL http://www.example.com/ renvoie la page HTML suivante :

<html>
<head>
	<link rel="stylesheet" href="style.css">
	<link rel="alternate" type="application/atom+xml" href="feed.atom">
	<script src="//some.api.com/maps.js"></script>
	<script src="https://other.api.com/calendar.js"></script>
	<script>
		var xhr = new XMLHttpRequest();
		xhr.open("POST", "http://api.example.com/data.json");
		xhr.responseType = "json";
		xhr.send("hello");

		function webbug() {
			var img = new Image(0,0);
			img.src = "img/chat.jpg";
			document.body.appendChild(img);
		}
	
		sessionStorage.secret = 'toto';
	</script>
</head>
<body onload="webbug">
	<p><a href="/lorem/ipsum">Lorem ipsum</a>
	   <img src="img/chien.jpg"></p>

	<iframe src="http://wiki.example.com/"></iframe>
	
	<form method="get" action="https://identity.com/login">
		<input type="submit">
	</form>
</body>
</html>
  1. Au moment du chargement de la page, quelles URLs sont automatiquement demandées par le browser ?

  2. Quelles requêtes sont bloquées par des politiques de sécurité ? Quelles sont les politiques en question ?

  3. Le script maps.js a-t-il accès à la valeur de sessionStorage.secret ? Et le script calendar.js ? Que change si la page est servie via HTTPS ?

  4. L’URL https://wiki.example.com/ répond avec le code HTML suivant

    <html>
    <head>
        <script>
        console.log(sessionStorage.secret);
        </script>
    </head>
    <body>
        ...
    </body>
    </html>
    

    Quelle valeur s’affiche dans la console JavaScript ?