Bonjour,

Dans le cadre d'un premier développement sous NodeJS, je suis actuellement bloqué sur une connection sécurisée au contenus de mon site.

Actuellement, je dispose d'une page index.html sur laquelle se trouvent tous mes contenus...
Je voudrais que le visiteur tombe d'abord sur une page "épurée" (appelée index_default.html) qui présente juste les pages d'accueil, à propos, ... et une dialogbox JQueryUI.
Si les identifiants de connection sont valides, la dialogbox ouvre un websocket pour que mon serveur vérifie dans la BDD MongoDB et envoie -si ces identifiants sont effectivement corrects- la fameuse page index.html avec tous ses contenus.

Mes codes ci-dessous bloquent aux niveaux :
- des formulaires de connection/inscription et de la page index_default.html: sont-ils réellement sécurisés ? que dois-je modifier ?
- de la réponse serveur: la redirection vers index.html ne fonctionne pas ...

index_default.html (côté client) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title>Site de test NodeJS</title>
        <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
	<link type="text/css" rel="stylesheet" href="styles/styles.css">
	<script src="http://code.jquery.com/jquery-latest.js"></script>
	<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
	<script src="/socket.io/socket.io.js"></script>
	<script>
	$(document).ready(function(){
		//Initialisation auto de la langue = celle du navigateur
		var language = window.navigator.userLanguage || window.navigator.language;
		if (/fr|de|es/.test(language)) { SelectedLang = language.substring(0,2); } //english by default
		$("#lang_selector").val(SelectedLang);
 
		// Navigation
		$("#menu a, footer a").click(function() {
			$("section").hide();
			$("#"+this.name).show();
		});
 
		var email = $("#email"),
			password = $("#password"),
			allFields = $( [] ).add( email ).add( password ),
			NewPseudo = $("#NewPseudo"),
			NewEmail = $("#NewEmail"),
			NewPassword = $("#NewPassword"),
			SecretQuestion = $("#secret_question"),
			SecretAnswer = $("#secret_answer"),
			allNewFields = $( [] ).add( NewEmail ).add( NewPassword ).add( SecretQuestion ).add( SecretAnswer ),
			tips = $(".validateTips");
		function updateTips( t ) {
			tips.text( t ).addClass("ui-state-highlight");
			setTimeout(function() {
				tips.removeClass("ui-state-highlight", 1500 );
			}, 500 );
		}
		function checkLength( o, n, min, max ) {
			if ( o.val().length > max || o.val().length < min ) {
				o.addClass("ui-state-error");
				updateTips("Length of " + n + " must be between " + min + " and " + max + ".");
				return false;
			}
			else {return true;}
		}
		function checkRegexp( o, regexp, n ) {
			if ( !( regexp.test( o.val() ) ) ) {
				o.addClass("ui-state-error");
				updateTips( n );
				return false;
			}
			else {return true;}
		}
		function checkIsSame( a, b, n ) {
			if ( a.val() != b.val() ) {
				a.addClass("ui-state-error");
				updateTips( n );
				return false;
			}
			else {return true;}
		}
		$("#login-form").dialog({
			autoOpen: false,
			height: 300,
			width: 340,
			modal: true,
			buttons: {
				"Connecter": function() {
					var bValid = true;
					allFields.removeClass("ui-state-error");
					bValid = bValid && checkLength( email, "email", 6, 80 );
					bValid = bValid && checkLength( password, "password", 5, 16 );
					bValid = bValid && checkRegexp( email, /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i, "eg. ui@jquery.com");
					bValid = bValid && checkRegexp( password, /^([0-9a-zA-Z])+$/, "Password field only allows : a-z 0-9");
					if ( bValid ) {
						var socket = io.connect('https://localhost:8080'); //à remplacer par l'adresse du site
						socket.emit('login',{email:email.val(),password:password.val()});
						$(this).dialog("close");
					}
				},
				Cancel: function() {$(this).dialog("close");}
			},
			close: function() {allFields.val("").removeClass("ui-state-error");}
		});
		$("#new_account-form").dialog({
			autoOpen: false,
			height: 400,
			width: 340,
			modal: true,
			buttons: {
				"Inscription": function() {
					var bValid = true;
					allNewFields.removeClass("ui-state-error");
					bValid = bValid && checkLength( NewPseudo, "pseudo", 1, 255 );
					bValid = bValid && checkLength( NewEmail, "email", 6, 80 );
					bValid = bValid && checkLength( NewPassword, "password", 5, 16 );
					bValid = bValid && checkLength( SecretQuestion, "your secret question", 1, 255 );
					bValid = bValid && checkLength( SecretAnswer, "your secret answer", 1, 255 );
					bValid = bValid && checkRegexp( NewPseudo, /^[a-zA-Z]([0-9a-zA-Z _\-])+$/, "Nickname must start with a letter, and accept only letters, numbers, spaces, or _- characters");
					bValid = bValid && checkRegexp( NewEmail, /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i, "Wrong email format, @ and/or domain.com are missing");
					bValid = bValid && checkRegexp( NewPassword, /^([0-9a-zA-Z])+$/, "Password field only allows : a-z 0-9");
					bValid = bValid && checkIsSame( NewPassword, $("#confirm_password"), "Both password fields must be identical");
					if ( bValid ) {
						var NewData = {
							'pseudo' : NewPseudo.val(),
							'email' : NewEmail.val(),
							'password' : NewPassword.val(),
							'SecretQuestion' : SecretQuestion.val(),
							'SecretAnswer' : SecretAnswer.val()
						};
						var socket = io.connect('https://localhost:8080'); //à remplacer par l'adresse du site
						socket.emit('new_account',NewData);
						$(this).dialog("close");
					}
				},
				Cancel: function() {$(this).dialog("close");}
			},
			close: function() {allNewFields.val("").removeClass("ui-state-error");}
		});
 
		$("#login_link, #new_account_link").click(function() {
			var formID = $(this).attr('id').replace('_link','');
			$('#'+formID+'-form').dialog('open');
		});
 
	});
	</script>
</head>
 
<body>
 
	<img id="logo"><a href="/">Logo</a></img>
	<h1>Site de test NodeJS</h1>
 
	<div id="login-form" title="Formulaire de connection">
		<p class="validateTips">Tous les champs sont requis.</p>
		<form>
			<fieldset>
				<input type="email" id="email" placeholder="Email"><br/>
				<input type="password" id="password" placeholder="Password"/>
			</fieldset>
		</form>
	</div>
	<div id="new_account-form" title="Formulaire d'inscription">
		<p class="validateTips">Tous les champs sont requis.</p>
		<form>
			<fieldset>
				<input type="text" id="NewPseudo" title="Ce pseudo sera affiché publiquement sur les applications du site" placeholder="Réponse secrète"><br/>
				<input type="email" id="NewEmail" title="Votre email servira d'identifiant de connexion" placeholder="Email"><br/>
				<input type="password" id="NewPassword" placeholder="mot de passe"/><br/>
				<input type="password" id="confirm_password" placeholder="Confirmez votre mot de passe"/><br/>
				<input type="text" id="secret_question" title="Cette question vous sera posée pour retrouver vos identifiants de connection" placeholder="Question secrète"><br/>
				<input type="text" id="secret_answer" title="Réponse à votre question secrète" placeholder="Réponse secrète">
			</fieldset>
		</form>
	</div>
	<div id="log_links">
		<a id="login_link">Connection</a><span> | </span>
		<a id="new_account_link">Inscription</a>
	</div>
	<select id="lang_selector">
		<option value="fr">Français</option>
		<option value="en">English</option>
		<option value="de">Deutch</option>
		<option value="es">Español</option>
	</select>
 
	<nav id="menu">
		<a name="home">Accueil</a>
		<a name="about">A propos</a>
	</nav>
	<script>
		$("#menu a").click(function() {
			$("section").hide();
			$("#"+this.name).show();
		})
	</script>
 
	<div id="content">
		<section id="home"><h1>Accueil</h1>
			<p>Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. </p>
			<p>Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.</p>
			<p>Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.</p>
			<p>Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est.</p>
			<p>Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.</p>
			<p>Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate. </p>
		</section>
		<section id="about"><h1>A propos</h1></section>
	</div>
 
	<footer></footer>
</body>
</html>
app.js (côté serveur Node local) :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/////////////////////////////////////////  Définitions  /////////////////////////////////////////
var express		= require('express'),
	app			= express(),
    server		= require('http').createServer(app),
    io			= require('socket.io').listen(server),
//	validator = require('validator'),
    fs			= require('fs'),
	EasyMongo	= require('easymongo');
 
server.listen(8080);
 
var options = { // configuration du serveur de base de données
    dbname: 'Database',
    host: 'localhost',
    port: 27017
};
var mongo = new EasyMongo(options);
 
// Structure du serveur principal
app.use("/styles", express.static(__dirname + '/public/styles'))
	.use("/scripts", express.static(__dirname + '/public/scripts'))
	.use("/images", express.static(__dirname + '/public/images'))
	.get('/', function(req, res) { // Chargement du fichier index.html affiché au client
		res.sendfile(__dirname + '/public/index_default.html');
	});
 
 
/*	function requireHTTPS(req, res, next) {
		if (!req.secure) {
			//FYI this should work for local development as well
			return res.redirect('https://' + req.get('host') + req.url);
		}
		next();
	}
	app.use(requireHTTPS);
	app.get('/', routeHandlerHome);
 
	The middleware approach will work because express will run the middleware in the order added,
	before it runs the router, and in general this kind of site-wide policy is cleaner as middleware vs. a wildcard route.
 
	Regarding question about sniffing session cookies, that must be addressed by marking the cookies as secure
	when you set them. If they haven't been marked secure, the browser will transmit them with HTTP requests as well,
	thus exposing them to sniffing
*/
 
// sets the log level of socket.io, with log level 2 we wont see all the heartbits
// of each socket but only the handshakes and disconnections
io.set('log level', 2);
 
// setting the transports by order, if some client is not supporting 'websockets'
// then the server will revert to 'xhr-polling' (like Comet/Long polling).
// for more configurations go to: https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO
io.set('transports', [ 'websocket', 'xhr-polling' ]);
 
/////////////////////////////////////////  Schéma de Base de données  /////////////////////////////////////////
var Users = mongo.collection('users');
console.log(Users);
//Users.remove('',function(error, user) { console.log("Database cleared !"); });
function AllUsers() { //finds all docs in collection Users
	Users.find('',function(error, user) {console.log(user);});
};
AllUsers();
 
 
/////////////////////////////////////////  Transactions Clients - Serveur  /////////////////////////////////////////
 
// Quand un client se connecte sur le site :
io.sockets.on('connection', function(socket) {
 
	// Requête de connection utilisateur
	socket.on('login', function(data) {
		//Rajouter un CheckRegexp sur les champs data
		var password = data.password,
			email = data.email;
		Users.findOne({'email': email, 'password': password}, function(error, user) {
			if (user) {
console.log("1 : "+user);
				var UserData = {
					'pseudo' : 			user.pseudo,
					'email' : 			user.email,
					'secret_question' :	user.secret_question,
					'secret_answer' : 	user.secret_answer,
					'gender' : 			user.gender,
					'firstname' : 		user.firstname,
					'lastname' : 		user.lastname,
					'birthyear' : 		user.birthyear,
					'country' : 		user.country
				};
				app.get('/', function(req, res) { // Chargement du fichier index.html affiché au client
					res.sendfile(__dirname + '/public/index.html');
console.log("1a : envoi de la page index.html");
				});
				socket.emit('login_ok',UserData);
				socket.mongoid = user._id;
				socket.pseudo = user.pseudo; }
			else { socket.emit('login_error');
console.log("2 : login error"); }
		});
	});
 
	// Requête de création de compte
	socket.on('new_account', function(data) {
		//Rajouter un CheckRegexp sur les champs data
		var password = data.password,
			pseudo = data.pseudo,
			email = data.email,
			secret_question = data.SecretQuestion,
			secret_answer = data.SecretAnswer;
 
		// Vérification de l'existence de l'email dans la base: il doit être unique
		// (l'email servira a renvoyer un mot de passe oublié)
		Users.findOne({'email':email},function(error,result) {
console.log("3 : "+result);
			if (result) { socket.emit('new_account_error','email'); }
			else {
				// Vérification de l'existence du pseudo dans la base: il doit être unique
				// (ce pseudo sera affiché sur les différentes applications du site)
				Users.findOne({'pseudo':pseudo},function(error,result) {
console.log("4 : "+result);
					if (result) { socket.emit('new_account_error','pseudo'); }
					else {
 
						// Instanciation du nouvel enregistrement, selon le schéma défini au début
						var NewUser = {
							'pseudo' : 			pseudo,
							'email' : 			email,
							'password' : 		password,
							'secret_question' : secret_question,
							'secret_answer' : 	secret_answer,
							'creation_date' : 	new Date()
						};
 
						// Sauvegarde dans MongoDB
						Users.save(NewUser, function(error,result) {
console.log("5 : "+error);
console.log("6 : "+result);
							if (error || !result) { throw error; }
							else {
								delete NewUser.password;
								socket.emit('login_ok',NewUser);
								socket.pseudo = pseudo;
								socket.mongoid = result._id;
console.log('Utilisateur '+NewUser.pseudo+' ('+NewUser.email+') a été enregistré avec succès !');
AllUsers();
							};
						});
					};
				});
			};
		});
	});
 
	//Mot de passe oublié? check email+question secrète
 
	// Update de la page profil
	socket.on('UpdateProfile', function(profile) {
console.log("7 : "+profile);
AllUsers();
		Users.update({'_id':socket.mongoid}, {$set:profile}, function(error,result) {
			if (error || !result) { throw error; }
			else {
console.log('Nouvelles données du profil de '+profile.pseudo+' ('+profile.email+') ont été enregistrées avec succès !');
AllUsers();
			}
		});
	});
 
 
	socket.on('disconnect', function() {
AllUsers();
		Users.update({'_id' : this.mongoid}, {$set : {'last_connection_date' : new Date()} }, function(error,result) {
			if (error || !result) { throw error; }
			else {
console.log("La date de dernière déconnection a été enregistrée avec succès !"); }
AllUsers();
	});
});
 
// Fonction de sécurité
function CheckRegExp (data) {
	// retourne un objet data dont les champs sont :
	// vérifiés par reg exp
	// et hachés SHA1 (module 'crypto')
};
J'avoue que je suis novice en matière de sécurité (en dehors des regexp), que manque-t-il à mes scripts ?
Merci.