Ciao ragazzi, perdonate il titolo fantasioso.
Ho inventato un nuovo tipo di implementazione logica per lo storage delle password, invece della solita combinazione user/password.
Volevo dei consigli da voi, sia perchè lo possiate confutare e solo nel caso fosse effettivamente più sicuro, vorrei implementarlo nel kernel UNIX al posto di quello attuale (solo che non posso leggermi tutto il kernel, perciò se avete articoli utili per ciò che riguarda l'astrazione del login linkatemi pure per favore).
Cominciamo con una premessa: è ovvio che il server deve essere sicuro, l'implementazione è solo una soluzione aggiuntiva nel quale l'attaccante è riuscito a bypassare le misure precedenti: come lo può essere criptare una password, o disabilitare eventuali direttive, .
Ho creato un POC in MySQL/MariaDB, perchè secondo me rende bene il concetto ma si può ovviamente implementare anche in NoSQL, o addirittura modificando la struttura della gestione delle password in /etc/shadow utilizzando il nono campo come indice del file che contiene delle password randomiche, fra cui quella dell'utente (.
File .SQL (Ho semplificato il più possibile il codice per rendere il tutto il più comprensibile possibile: se non volete incorrere in concorrenze se effettuate un inserimento massivo di utenti deve essere applicato il LOCK)
Nella teoria se l'attaccante accede ai dati privati a volte si ritrova a dover decriptare la password dell'utente interessato (e ogni tanto lo fa come ultima spiaggia nel caso il server sia ben configurato e protetto), mentre così si ritrova con n passwords non sapendo quale è quella che deve decriptare.
Per esempio in sistemi amministrativi o login root potrebbero auto-generare nMila password hash "fake".
Grazie a chiunque dimostrerà interessamento.
Se non volete cliccare sul link:
codice:
CREATE DATABASE test CHARSET utf8 COLLATE utf8_bin;
use test;
SET GLOBAL log_bin_trust_function_creators = 1;
-----------------------------------------------------------------------------
-- Configurazione:
-- @RND_STR_LENGHT, lunghezza della stringa utilizzata per il salt della password;
-- @RND_STR_CHARS, caratteri utilizzati per la generazione della stringa casuale;
----------------------------------------------------------------------------
SET @RND_STR_LENGHT = 255;
SET @RND_STR_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$/()=?^[]+*@-_<>.';
CREATE TABLE users(
id BIGINT UNSIGNED AUTO_INCREMENT,
email VARCHAR(255) NOT NULL,
salt CHAR(255) NOT NULL,
PRIMARY KEY(id)
)ENGINE=innoDB;
CREATE TABLE hashes(
hash CHAR(128) NOT NULL,
UNIQUE KEY(hash)
)ENGINE=innoDB;
----------------------------------------------------------------------------
-- Genera la stringa casuale da 255 caratteri
----------------------------------------------------------------------------
DELIMITER $$
CREATE FUNCTION GEN_RND_STR() RETURNS VARCHAR(255) NOT DETERMINISTIC
BEGIN
SET @rndStr = '';
WHILE LENGTH(@rndStr) < @RND_STR_LENGHT DO
SET @rndStr = CONCAT(@rndStr, SUBSTRING(@RND_STR_CHARS FROM FLOOR(RAND() * LENGTH(@RND_STR_CHARS) + 1) FOR 1));
END WHILE;
RETURN @rndStr;
END$$
DELIMITER ;
----------------------------------------------------------------------------
-- Registra l'utente
----------------------------------------------------------------------------
DELIMITER $$
CREATE PROCEDURE REG_USER(
IN _email VARCHAR(255),
IN _password VARCHAR(255)
) BEGIN
# Ipotizzando che prima delle registrazione venga verificato che l email
# inserita non sia già presente.
# Inserisce una hash univoca, utilizziamo il loop per rigenerarlo nel caso
# sia già presente nella tabella.
#
# L hash è generato dalla password dell utente concatenato al salt:
# SHA2(CONCAT(salt, password), 512)
hashGen: LOOP
SET @rndSalt = GEN_RND_STR();
SET @hash = SHA2(CONCAT(@rndSalt, _password), 512);
SET @SQL = '
SELECT @t := COUNT(*)
FROM hashes
WHERE ? = hash';
PREPARE stmt FROM @SQL;
EXECUTE stmt USING @hash;
DEALLOCATE PREPARE stmt;
IF ((SELECT @t) = 0) THEN
LEAVE hashGen;
END IF;
END LOOP hashGen;
INSERT INTO users (email, salt)
VALUES (_email, @rndSalt);
INSERT INTO hashes
VALUES (@hash);
# Genera una tabella casuale nella quale vengono riordinate le hash.
# Questo è necessario perchè se fossero inserite nello stesso ordine nel
# quale sono inseriti gli utenti basterebbe un "LIMIT USER_ID" ed avere
# come ultimo elemento l'hash dell'utente.
CREATE TEMPORARY TABLE tmpHashes(
hash CHAR(128) NOT NULL,
UNIQUE KEY(hash)
)ENGINE=innoDB
AS (
SELECT *
FROM hashes
ORDER BY RAND()
);
# Cancella i dati e ripopola la tabella delle hash.
TRUNCATE TABLE hashes;
INSERT INTO hashes SELECT * FROM hashes;
END$$
DELIMITER ;
----------------------------------------------------------------------------
-- Verifica le credenziali
----------------------------------------------------------------------------
DELIMITER $$
CREATE PROCEDURE LOGIN(
IN _email VARCHAR(255),
IN _password VARCHAR(255),
OUT _userId BIGINT UNSIGNED
) BEGIN
SELECT u.id
INTO _userId
FROM users u
INNER JOIN hashes h
ON h.hash = SHA2(CONCAT(u.salt, _password), 512)
WHERE u.email = _email;
END$$
DELIMITER ;
CALL REG_USER('email@test.com', 's3cr3tp4ss');
CALL REG_USER('kitty@yep.org', 'hello_kitty');
CALL REG_USER('jhon@fbi.gov', '_2jsd82+*é称ç称XVBZ__-');
SELECT * FROM users;
CALL LOGIN('email@test.com', 's3cr3tp4ss', @userId);
SELECT @userId;
CALL LOGIN('email@test.com', 'invalid_password', @userId);
SELECT @userId;
DROP DATABASE test;