Сообщение от laimas
|
Где сама защищенная страница до поры до времени, на сервере? Вот на сервер и передавайте "abc123", которое он сравнит с хешем этого пароля. В этом случае подобрать будет очень сложно.
|
Давайте внимательно посмотрим на вашу схему. Ваша схема должна использовать две страницы. Один для входа в систему, второй для секретной страницы.
Но это большая уязвимость безопасности, если не существует единственного четкого способа доступа к данным вашего приложения. Я не говорю о коде, который я написал таким образом, я думаю, что он должен работать, но кажется, что существует второй способ доступа. Вам нужно два, это нормально, но не более того числа, которое вы предполагаете. Допустим, вы просто перенаправляете своего пользователя на секретную страницу, если предоставленные учетные данные верны. Да, ваши пользователи могут даже не знать, что можно получить секретную страницу, просто вставив правильный адрес в адресную строку, но ничто не мешает поисковому боту Google проиндексировать эту страницу.
На самом деле я нашел вашу идею в одном приложении для камер системы безопасности, которое было разработано так, как вы описали ... люди верили, что они защищены, но на самом деле эта "защищенная" страница была доступна всем, кто делает правильный поисковый запрос.
Я думаю, было бы излишне делать две страницы для этого приложения викторины.
В любом случае, вы можете написать свое решение полностью на JavaScript. В современных браузерах есть API веб-криптографии. Почему бы не использовать это? Таким образом, вы можете создать свое приложение, в котором хранятся вопросы и ответы на вопросы викторины, а также данные, которые будут показаны кому-то, кто прошел тест.
<script>
var data = {
quiz: {cipherText:"kqiyEf7WQl9774pyF3PI2w4gmpt0yZDtESUfgNyKaItOvJWzzKBGdLu5q2d9fO2CDlMPldGXJCesA9w7CE73S53hE25HWQSTYuqTZ69tVeQBix4N8QS+8lec8l1ZHawBele5l8A/4Smie77p9sJCakFcF/WYYy1iJJ93UlNzmc00TS7kTOA71D8zYthR/xnfBrnmuwrACgJN+kyUtnpHXKRpuCoicn+NqD2xm63HpbTJnME9Ns/BStttyyWtoYCKvuaB9ZpQYyM5BAR+w0liTiicrawkFslnpTVwY9MqhkNeEq7GZgYkKSXiJmP3H50BVBeozZWSW3lGxJhAk9TVnoQRIvhzP7DqX7NPbL5GUqY8HciSDfwBwPAtPBGBfdP62MZRnFpnW9X7Fmt7tdW4CaaMzjCL2+tg5y5T7GKK0ymgxmohuqRiQnuriOVTxcDAQwNkW2ntZYo+RxU9X2ONiEOienc5SSqGAivNOfReU5UcUJj+8FslyCXhL6AeinxjG9Kis0uVvHKKUMyawM3cT9t3BH1shoVB1e37mJ6g1QKlyUd0VwY8sBqW5TZdCWFJjB65YcicibBEbciXIWcUbUDRqKyS0w==",iv:"CGE/wWm0eEJzWzKu"},
code: {cipherText:"pNavP6FL6bCxtB9D6fVXhFqRJXWOw/XkCHLohPcWclrYwstm9Rd+NOrtTCAFU7bpDN5VD8wjc9V1gNZpLRAzhfi0kw==",iv:"F6tJKgxCXPveHGYq"}
};
class App {
constructor() {}
async start(encrypted) {
class State {
constructor(points) {
this.points = points;
Object.freeze(this);
}
}
try {
const popQuiz = await this.decrypt(encrypted.quiz, () => prompt("Password:", ""));
var rightAnswers = 0;
var answers = [];
alert("Добро пожаловать в викторину!");
questions: for(const { question, answer } of popQuiz) {
do {
var guess = prompt(question);
if(guess == null) break questions;
if((await this.hash(guess)) === answer) {
rightAnswers++;
answers.push(answer);
continue questions;
}
} while(confirm("Ответ неверен! Попробовать ещё?"));
break;
}
if(rightAnswers === popQuiz.length) {
var message;
try {
message = await this.decrypt(encrypted.code, () => JSON.stringify(answers));
} catch(error) {}
}
alert(`Пройдено ${rightAnswers} из ${popQuiz.length}\n${message ? message : "Чтобы получить расширенный доступ, вы должный пройти тест!"}`);
} catch(error) {
console.log(error);
if(confirm("Авторизация не удалась! Попробовать ещё раз?")) {
this.start(encrypted);
}
}
}
static hash(algo, str) {
return crypto.subtle.digest(algo, new TextEncoder().encode(str));
}
static async genEncryptionKey(password, mode, length) {
var algo = {
name: 'PBKDF2',
hash: 'SHA-256',
salt: new TextEncoder().encode('a-unique-salt'),
iterations: 1000
};
var derived = { name: mode, length: length };
var encoded = new TextEncoder().encode(password);
var key = await crypto.subtle.importKey('raw', encoded, { name: 'PBKDF2' }, false, ['deriveKey']);
return crypto.subtle.deriveKey(algo, key, derived, false, ['encrypt', 'decrypt']);
}
static async encrypt(text, password, mode, length, ivLength) {
var algo = {
name: mode,
length: length,
iv: crypto.getRandomValues(new Uint8Array(ivLength))
};
var key = await this.genEncryptionKey(password, mode, length);
var encoded = new TextEncoder().encode(text);
return {
cipherText: await crypto.subtle.encrypt(algo, key, encoded),
iv: algo.iv,
toJSON() {
return {
cipherText: App.encode64(this.cipherText),
iv: App.encode64(this.iv)
};
}
};
}
static async decrypt(encrypted, password, mode, length) {
var algo = {
name: mode,
length: length,
iv: encrypted.iv
};
var key = await this.genEncryptionKey(password, mode, length);
var decrypted = await crypto.subtle.decrypt(algo, key, encrypted.cipherText);
return new TextDecoder().decode(decrypted);
}
static encode64(buff) {
return btoa(new Uint8Array(buff).reduce((s, b) => s + String.fromCharCode(b), ""));
}
async encrypt(object, passwordCallback) {
var mode = 'AES-GCM', length = 256, ivLength = 12;
var encrypted = await App.encrypt(JSON.stringify(object), passwordCallback(), mode, length, ivLength);
return encrypted.toJSON();
}
async decrypt(object, passwordCallback) {
var mode = 'AES-GCM', length = 256, ivLength = 12;
function toBuffer(encrypted, property) {
return fetch(`data:text/plain;base64,${encrypted[property]}`).then(r => r.arrayBuffer());
}
var encrypted = {
cipherText: await toBuffer(object, "cipherText"),
iv: new Uint8Array(await toBuffer(object, "iv"))
};
var decrypted = await App.decrypt(encrypted, passwordCallback(), mode, length);
return JSON.parse(decrypted);
}
async hash(string) {
var hashed = await App.hash('SHA-256', `#DATA<${string}>`);
return App.encode64(hashed);
}
}
var app = new App();
</script>
<button onclick="app.start(data);">Начать викторину для избранных!</button>