SQL-Beispiele aus dem Miro-Board (08 Benutzerverwaltung)
SQL-Dateien
haendler1.sql | (CREATE-TABLE- und INSERT-Befehle: Praktikumsversion, etwas mehr Daten) |
haendler1_uuid.sql | (Eine Händler-Datenbank id vom Typ UUID) |
update-01.sql | (Beispiele zur Update-Operation) |
Postgres: Kryptographische Befehle (random, encoding)
/* Verschlüsselung */ SELECT gen_random_uuid(); SELECT gen_random_bytes(256); SELECT encode(gen_random_bytes(256), 'hex'); SELECT encode(gen_random_bytes(256), 'base64'); SELECT digest('geheim', 'md5'); SELECT encode(digest('geheim', 'md5'), 'hex'); -- http://md5decrypt.net/en/ -- https://crackstation.net/ -- e8636ea013e682faf61f56ce1cb1ab5c SELECT digest('geheim', 'sha256'); SELECT encode(digest('geheim', 'sha256'), 'hex'); -- http://md5decrypt.net/en/Sha256/ -- https://crackstation.net/ -- addb0f5e7826c857d7376d1bd9bc33c0c544790a2eac96144a8af22b1298c940 SELECT digest('geheim', 'sha512'); SELECT encode(digest('geheim', 'sha512'), 'hex'); -- http://md5decrypt.net/en/Sha512/ -- https://crackstation.net/ -- 8d847e01d22baa969f71fa362b4de21c9e13c7882bcea13ba5c6a8ae0d71fc8c9700c82e0087a65c8b37bd29f536747f28c9672bec1cae7762d2c9f36b6013f2 SELECT encode(gen_random_bytes(32), 'base64'); -- SKKO6bHd8U+IQ/7TR5IQQXV+4JYhTIhi9eXMGTIDmog= SELECT encode(digest('geheim' || 'SKKO6bHd8U+IQ/7TR5IQQXV+4JYhTIhi9eXMGTIDmog=', 'sha512'), 'hex'); -- http://md5decrypt.net/en/Sha512/ liefert das Passwort nicht mehr im Klartext -- https://crackstation.net/ -- 9750c58338678863cd90751495ef293d8824d8acbe43172c2b4d8b100156fc82f0b63970a7ad19fecbded2e61b955c12dc8bc41b91a34044d393da7c52401f7c
Postgres: Kryptographische Befehle (crypt)
-- https://www.postgresql.org/docs/current/static/pgcrypto.html SELECT gen_salt('bf'), gen_salt('bf'), gen_salt('bf'); SELECT crypt('geheim', gen_salt('bf')), crypt('geheim', gen_salt('bf')); SELECT crypt('geheim', '$2a$06$pKJmBXm9ihFbTBCl3/wBM.'), '$2a$06$pKJmBXm9ihFbTBCl3/wBM.'; SELECT crypt('geheim', '§"$%fdg43TREGD54ü3245ete§$%§$%'), crypt('password', '§"$%fdg43TREGD54ü3245ete§$%§$%'); SELECT crypt('geheim', '$2a$06$pKJmBXm9ihFbTBCl3/wBM.'), crypt('password', '$2a$06$pKJmBXm9ihFbTBCl3/wBM.'); SELECT gen_salt('bf'); SELECT gen_salt('bf',8); SELECT gen_salt('bf',14); SELECT crypt('geheim', gen_salt('bf')); SELECT crypt('geheim', gen_salt('bf',8)); SELECT crypt('geheim', gen_salt('bf',14)); -- '$2a$14$5PEKjgHVsugXNVXHWbZmt.9F4ieQw1iVHKrYngZFKIeGEB2/E6GLG' SELECT ( '$2a$14$5PEKjgHVsugXNVXHWbZmt.9F4ieQw1iVHKrYngZFKIeGEB2/E6GLG' = crypt ( 'geheim', '$2a$14$5PEKjgHVsugXNVXHWbZmt.9F4ieQw1iVHKrYngZFKIeGEB2/E6GLG' ) ) AS password_correct; SELECT ( '$2a$14$5PEKjgHVsugXNVXHWbZmt.9F4ieQw1iVHKrYngZFKIeGEB2/E6GLG' = crypt ( 'passwort', '$2a$14$5PEKjgHVsugXNVXHWbZmt.9F4ieQw1iVHKrYngZFKIeGEB2/E6GLG' ) ) AS password_correct;
Before-Trigger
- Falls der Trigger (samt zugehöriger Prozedur) schon existiert: Lösche ihn. DROP TRIGGER IF EXISTS h_name_mit_ortschaft_trigger ON haendler; DROP FUNCTION IF EXISTS h_name_mit_ortschaft_function() CASCADE; -- Erzeuge die Trigger-Prozedur. Diese berechnet den Wert von -- h_name_mit_ortschaft aus den neuen Werten von h_name und h_ortschaft. CREATE FUNCTION h_name_mit_ortschaft_function() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN NEW.h_name_mit_ortschaft = COALESCE(NEW.h_name || ' in ' || NEW.h_ortschaft, NEW.h_name); -- Gibt das modifizierte NEW-Tupel zurück. RETURN NEW; END $$ ; -- Jedes Mal BEVOR ein neues Tupel in haendler eingefügt wird bzw. -- den alten Wert ersetzt, wird es modifiziert. Der Wert des Attributs -- h_name_mit_ortschaft wird automatisch berechnet. CREATE TRIGGER h_name_mit_ortschaft_trigger BEFORE INSERT OR UPDATE ON haendler FOR EACH ROW EXECUTE PROCEDURE h_name_mit_ortschaft_function() ;
Untainted Strings
CREATE DOMAIN IF NOT EXISTS D_UNTAINTED AS VARCHAR CHECK (value !~ '[<>"'';]|--|/\*') ; CREATE TABLE haendler (id UUID NOT NULL gen_random_uuid(), name D_UNTAINTED NOT NULL, ... );
Händler: Sichere Domains
/* Alte Tabellen etc. (in der richtigen Reihenfolge) löschen. */ DROP TABLE IF EXISTS haendler CASCADE; DROP DOMAIN IF EXISTS D_EMAIL CASCADE; /* Domains definieren */ -- CREATE DOMAIN IF NOT EXISTS D_UNTAINTED -- AS -- VARCHAR CHECK (value !~ '[<>"'';]|--|/\*'); DO $$ BEGIN CREATE DOMAIN D_UNTAINTED AS VARCHAR CHECK (value !~ '[<>"'';]|--|/\*'); EXCEPTION WHEN duplicate_object THEN null; END $$; CREATE DOMAIN D_EMAIL AS VARCHAR CHECK (value ~* '\A[a-z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-z0-9-]+(?:\.[a-z0-9-]+)*\Z') ; CREATE TABLE haendler (h_id INTEGER NOT NULL, h_name D_UNTAINTED NOT NULL, h_ortschaft D_UNTAINTED, h_user_name D_UNTAINTED, h_email D_EMAIL NOT NULL, h_password VARCHAR, h_salt VARCHAR, CONSTRAINT pk_haendler PRIMARY KEY (h_id), CONSTRAINT unique_haendler_user_name UNIQUE (h_user_name), CONSTRAINT unique_haendler_email UNIQUE (h_email) );
Händler: Hash and Salt
DROP TRIGGER IF EXISTS encrpyt_password_trigger_insert ON haendler; DROP FUNCTION IF EXISTS encrpyt_password_function_insert(); DROP TRIGGER IF EXISTS encrpyt_password_trigger_update ON haendler; DROP FUNCTION IF EXISTS encrpyt_password_function_update(); CREATE FUNCTION encrpyt_password_function_insert() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN -- Erzeuge einen zufälligen Salt-String. NEW.h_salt = encode(gen_random_bytes(258), 'base64'); -- Salze das Passwort und speichere es als Hashwert. NEW.h_password = encode(digest(NEW.h_password || NEW.h_salt, 'sha512'), 'hex'); -- Gibt das modifizierte NEW-Tupel zurück. RETURN NEW; END $$ ; CREATE FUNCTION encrpyt_password_function_update() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN -- Ändere ddas Salz und das Passwort nur, wenn ein neues Passwort -- eingegeben wurde. Falls nur andere Attribute geändert werden, -- dürfen das Salz und das Passwort nicht veraändert werden. IF (NEW.h_password <> OLD.h_password) THEN NEW.h_salt = encode(gen_random_bytes(256), 'base64'); NEW.h_password = encode(digest(NEW.h_password || NEW.h_salt, 'sha512'), 'hex'); ELSE NEW.h_salt = OLD.h_salt; -- Dder Salzwert darf unter keinen Umsänden -- vom Benutzer verändert werden. END IF; RETURN NEW; END $$ ; CREATE TRIGGER encrpyt_password_trigger_insert BEFORE INSERT ON haendler FOR EACH ROW EXECUTE PROCEDURE encrpyt_password_function_insert() ; CREATE TRIGGER encrpyt_password_trigger_update BEFORE UPDATE ON haendler FOR EACH ROW EXECUTE PROCEDURE encrpyt_password_function_update() ;
Händler: Hash-Tests
INSERT INTO haendler(h_id, h_name, h_ortschaft, h_user_name, h_email, h_password) VALUES (1, 'Maier', 'Königsbrunn', 'maier1', 'hans@maier.de', 'geheim'), (2, 'Müller', 'Königsbrunn', 'müller', 'gerd@mueller.de', 'geheim'), (3, 'Maier', 'Augsburg', 'maier2', 'hans@wrzlbrmpft.de', '123456'), (4, 'Huber', NULL, 'huber', 'huber@huber-gmbh.com', '"Iw!.d%6z*"'), (5, 'Schmidt', 'Hamburg', 'schmidt', 'schmiddy@schmidt.firma', 'g3h31m') ; /* Passwort-Abfrage */ SELECT * FROM haendler; SELECT h_password = encode(digest('geheim' || h_salt, 'sha512'), 'hex') AS password_correct FROM haendler WHERE h_id = 1; /* Vereinfachung mit Hilfe einer Passwort-Abfrage-Funktion */ DROP FUNCTION IF EXISTS check_password(pw VARCHAR, h haendler); CREATE FUNCTION check_password(pw VARCHAR, h haendler) RETURNS BOOLEAN RETURNS NULL ON NULL INPUT LANGUAGE SQL IMMUTABLE AS $$ SELECT h.h_password = encode(digest(pw || h.h_salt, 'sha512'), 'hex'); $$ ; /* Ein paar weitere Tests */ -- Die obige Passwort-Überprüfung kann nun einfacher erfolgen: SELECT check_password('geheim', haendler) FROM haendler WHERE h_id = 1; SELECT check_password('geheim', h) FROM haendler h WHERE h_id = 2; SELECT check_password('geheim', h), h.* FROM haendler h;
Händler ohne ’salt”
DROP TABLE IF EXISTS haendler CASCADE; CREATE TABLE haendler (h_id INTEGER NOT NULL, h_name D_UNTAINTED NOT NULL, h_ortschaft D_UNTAINTED, h_user_name D_UNTAINTED, h_email D_EMAIL NOT NULL, h_password VARCHAR, CONSTRAINT pk_haendler PRIMARY KEY (h_id), CONSTRAINT unique_haendler_user_name UNIQUE (h_user_name), CONSTRAINT unique_haendler_email UNIQUE (h_email) );
Händler: Crypt
-- Falls der Trigger (samt zugehöriger Prozedur) schon existiert: Lösche ihn. DROP TRIGGER IF EXISTS encrpyt_password_trigger ON haendler; DROP FUNCTION IF EXISTS encrpyt_password_function(); CREATE FUNCTION encrpyt_password_function() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN -- Wenn ein neues Passwort eingegeben wird, -- speichere das Passwort gezalzen und verschlüsselt. IF (TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND NEW.h_password <> OLD.h_password) ) THEN NEW.h_password = crypt(NEW.h_password, gen_salt('bf',8)); END IF; -- Gibt das modifizierte NEW-Tupel zurück. RETURN NEW; END $$ ; -- Jedes Mal BEVOR ein neues Tupel in haendler eingefügt wird bzw. den alten Wert ersetzt, -- wird es modifiziert. Der Wert des Attributs h_password wird gesalzen und verschlüsselt. CREATE TRIGGER encrpyt_password_trigger BEFORE INSERT OR UPDATE ON haendler FOR EACH ROW EXECUTE PROCEDURE encrpyt_password_function() ; -- Erstelle Funktion zum Testen, ob das Passwort korrekt eingegeben wurde. DROP FUNCTION IF EXISTS check_password(VARCHAR, VARCHAR); -- usr: Benutzername oder E-Mail-Adresse CREATE FUNCTION check_password(usr VARCHAR, pw VARCHAR) RETURNS BOOLEAN RETURNS NULL ON NULL INPUT LANGUAGE SQL IMMUTABLE AS $$ SELECT h.h_password = crypt(pw, h.h_password) FROM haendler h WHERE usr IN (h.h_user_name, h.h_email); $$ ;
Händler: Crypt-Tests
/* Daten einfügen */ INSERT INTO haendler(h_id, h_name, h_ortschaft, h_user_name, h_email, h_password) VALUES (1, 'Maier', 'Königsbrunn', 'maier1', 'hans@maier.de', 'geheim'), (2, 'Müller', 'Königsbrunn', 'müller', 'gerd@mueller.de', 'geheim'), (3, 'Maier', 'Augsburg', 'maier2', 'hans@wrzlbrmpft.de', '123456'), (4, 'Huber', NULL, 'huber', 'huber@huber-gmbh.com', '"Iw!.d%6z*"'), (5, 'Schmidt', 'Hamburg', 'schmidt', 'schmiddy@schmidt.firma', 'g3h31m') ; /* Passwort-Abfrage */ SELECT * FROM haendler; SELECT h_password = crypt('geheim', h_password) AS password_correct FROM haendler WHERE h_id = 1; SELECT check_password('hans@maier.de', 'geheim'); SELECT check_password('müller', 'geheim'); SELECT check_password('maier2', 'geheim'); SELECT check_password('maier2', '123456'), check_password('maier2', '654321'); UPDATE haendler SET h_password = '654321' WHERE h_id = 3; SELECT check_password('maier2', '123456'), check_password('maier2', '654321'); SELECT check_password(h_user_name, 'geheim'), h_email, h_password FROM haendler WHERE h_user_name = 'maier1'; UPDATE haendler SET h_email = 'hannes@maier.de' WHERE h_id = 1; SELECT check_password(h_user_name, 'geheim'), h_email, h_password FROM haendler WHERE h_user_name = 'maier1';