Databassäkerhet är viktigt för alla MySQL-inställningar. Användare är grunden för alla system. När det gäller databassystem tänker jag i allmänhet på dem i två distinkta grupper:
- Applikations-, tjänst- eller programanvändare - i princip kunder eller klienter som använder en tjänst.
- Databasutvecklare, administratörer, analytiker, etc... - De som underhåller, arbetar med eller övervakar databasinfrastrukturen.
Även om varje användare behöver komma åt databasen på någon nivå, skapas inte alla dessa behörigheter lika.
Till exempel behöver kunder och kunder tillgång till sina "relaterade användarkontodata", men även det bör övervakas med viss kontroll. Vissa tabeller och data bör dock vara strikt förbjudna (t.ex. systemtabeller).
Ändå:
- Analytiker behöver läsbehörighet ', för att få information och insikt via frågetabeller...
- Utvecklare kräver en mängd behörigheter och privilegier för att utföra sitt arbete...
- DBA behöver "root" eller liknande typprivilegier för att köra programmet...
- Köpare av en tjänst måste se sin beställnings- och betalningshistorik...
Du kan föreställa dig (jag vet att jag gör det) hur svårt det är att hantera flera användare eller grupper av användare inom ett databasekosystem.
I äldre versioner av MySQL etableras en miljö för flera användare på ett något monotont och repetitivt sätt.
Ändå implementerar version 8 en exceptionell och kraftfull SQL-standardfunktion - Roler - som lindrar ett av de mer överflödiga områdena i hela processen:att tilldela privilegier till en användare.
Så, vad är en roll i MySQL?
Du kan säkert besöka, MySQL 2018:What's in 8.0 and Other Observations, skrev jag för Severalnines-bloggen här där jag nämner roller för en översikt på hög nivå. Men där jag bara sammanfattade dem där, ser det här aktuella inlägget ut att gå djupare och enbart fokusera på roller.
Så här definierar MySQL-dokumentationen online en roll:"En MySQL-roll är en namngiven samling privilegier".
Verkar inte bara den definitionen hjälpsam?
Men hur?
Vi kommer att se i exemplen som följer.
För att notera exemplen som tillhandahålls
Exemplen som ingår i det här inlägget är i en personlig utvecklings- och inlärningsarbetsstation/miljö för "enanvändare", så var säker och implementera de bästa praxis som gynnar dig för dina speciella behov eller krav. Användarnamnen och lösenorden som visas är rent godtyckliga och svaga.
Användare och privilegier i tidigare versioner
I MySQL 5.7 existerar inte roller. Tilldelning av behörigheter till användare görs individuellt. För att bättre förstå vad roller ger, låt oss inte använda dem. Det är ingen mening alls, jag vet. Men när vi går igenom inlägget kommer det att göra det.
Nedan skapar vi några användare:
CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password';
CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password';
CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';
Sedan ges dessa användare vissa privilegier:
GRANT SELECT ON some_db.specific_table TO 'reader_1'@'localhost';
GRANT SELECT, INSERT ON some_db.specific_table TO 'reader_writer'@'localhost';
GRANT UPDATE, DELETE ON some_db.specific_table TO 'changer_1'@'localhost';
Usch, skönt att det är över. Nu tillbaka till...
Och precis som det har du en begäran om att implementera ytterligare två "skrivskyddade" användare...
Tillbaka till ritbordet:
CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2';
CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';
Tilldela dem privilegier också:
GRANT SELECT ON some_db.specific_table TO 'reader_2'@'localhost';
GRANT ALL ON some_db.specific_table TO 'reader_3'@'localhost';
Kan du se hur detta är mindre än produktivt, fullt av upprepningar och felbenäget? Men ännu viktigare, fattade du felet?
Bra för dig!
Samtidigt som jag beviljade privilegier för dessa två ytterligare användare, av misstag beviljade ALLA privilegier till ny användare reader_3.
Hoppsan.
Ett misstag som vem som helst kan göra.
Ange MySQL-roller
Med roller, mycket av ovanstående systematisk privilegietilldelning och delegering kan strömlinjeformas något .
Användarskapandet förblir i princip detsamma, men det är att tilldela privilegier genom roller som skiljer sig:
mysql> CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password';
Query OK, 0 rows affected (0.19 sec)
mysql> CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password';
Query OK, 0 rows affected (0.22 sec)
mysql> CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';
Query OK, 0 rows affected (0.08 sec)
mysql> CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2';
Query OK, 0 rows affected (0.28 sec)
mysql> CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';
Query OK, 0 rows affected (0.12 sec)
Genom att fråga i systemtabellen mysql.user kan du se att de nyskapade användarna finns:
(Obs! Jag har flera användarkonton i den här inlärnings-/utvecklingsmiljön och har undertryckt mycket av utdata för bättre skärpa på skärmen.)
mysql> SELECT User FROM mysql.user;
+------------------+
| User |
+------------------+
| changer_1 |
| mysql.infoschema |
| mysql.session |
| mysql.sys |
| reader_1 |
| reader_2 |
| reader_3 |
| reader_writer |
| root |
| | --multiple rows remaining here...
+------------------+
23 rows in set (0.00 sec)
Jag har den här godtyckliga tabellen och exempeldata:
mysql> SELECT * FROM name;
+--------+------------+
| f_name | l_name |
+--------+------------+
| Jim | Dandy |
| Johhny | Applesauce |
| Ashley | Zerro |
| Ashton | Zerra |
| Ashmon | Zerro |
+--------+------------+
5 rows in set (0.00 sec)
Låt oss nu använda roller för att upprätta och tilldela privilegier för de nya användarna att använda namntabellen.
Skapa först rollerna:
mysql> CREATE ROLE main_read_only;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_read_write;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_changer;
Query OK, 0 rows affected (0.14 sec)
Lägg märke till tabellen mysql.user igen:
mysql> SELECT User FROM mysql.user;
+------------------+
| User |
+------------------+
| main_changer |
| main_read_only |
| main_read_write |
| changer_1 |
| mysql.infoschema |
| mysql.session |
| mysql.sys |
| reader_1 |
| reader_2 |
| reader_3 |
| reader_writer |
| root |
| |
+------------------+
26 rows in set (0.00 sec)
Baserat på denna utgång kan vi ana; att roller i allt väsentligt är användarna själva.
Nästa, privilegietilldelning:
mysql> GRANT SELECT ON practice.name TO 'main_read_only';
Query OK, 0 rows affected (0.14 sec)
mysql> GRANT SELECT, INSERT ON practice.name TO 'main_read_write';
Query OK, 0 rows affected (0.07 sec)
mysql> GRANT UPDATE, DELETE ON practice.name TO 'main_changer';
Query OK, 0 rows affected (0.16 sec)
Ett kort mellanspel
Vänta en minut. Kan jag bara logga in och utföra några uppgifter med själva rollkontona? När allt kommer omkring är de användare och de har de privilegier som krävs.
Låt oss försöka logga in på övningsdatabasen med rollen main_changer:
:~$ mysql -u main_changer -p practice
Enter password:
ERROR 1045 (28000): Access denied for user 'main_changer'@'localhost' (using password: YES
Det enkla faktum att vi får en lösenordsuppmaning är en bra indikation på att vi inte kan (åtminstone vid denna tidpunkt). Som ni minns satte jag inte ett lösenord för någon av rollerna när de skapades.
Vad har mysql.user-systemtabellernas authentication_string-kolumn att säga?
mysql> SELECT User, authentication_string, password_expired
-> FROM mysql.user
-> WHERE User IN ('main_read_only', 'root', 'main_read_write', 'main_changer')\G
*************************** 1. row ***************************
User: main_changer
authentication_string:
password_expired: Y
*************************** 2. row ***************************
User: main_read_only
authentication_string:
password_expired: Y
*************************** 3. row ***************************
User: main_read_write
authentication_string:
password_expired: Y
*************************** 4. row ***************************
User: root
authentication_string: ***various_jumbled_mess_here*&&*&*&*##
password_expired: N
4 rows in set (0.00 sec)
Jag inkluderade rotanvändaren bland rollnamnen för IN()-predikatkontrollen för att helt enkelt visa att den har en authentication_string, där rollerna inte har det.
Det här avsnittet i CREATE ROLE-dokumentationen förtydligar det bra:"En roll när den skapas är låst, har inget lösenord och tilldelas standardinsticksprogrammet för autentisering. (Dessa rollattribut kan ändras senare med ALTER USER-satsen, av användare som har globalt CREATE USER-behörighet.)"
Tillbaka till den aktuella uppgiften, vi kan nu tilldela rollerna till användare baserat på deras nödvändiga behörighetsnivå.
Observera att ingen ON-sats finns i kommandot:
mysql> GRANT 'main_read_only' TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.13 sec)
mysql> GRANT 'main_read_write' TO 'reader_writer'@'localhost';
Query OK, 0 rows affected (0.16 sec)
mysql> GRANT 'main_changer', 'main_read_only' TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.13 sec)
Det kan vara mindre förvirrande om du använder någon form av 'namnkonvention ' vid upprättande av rollnamn, (jag vet inte om MySQL tillhandahåller ett just nu... Community?) om det inte är av någon annan anledning än att skilja mellan dem och vanliga "icke-roll"-användare visuellt.
Det finns fortfarande en del arbete kvar att göra
Det var väl superlätt?
Mindre redundant än det gamla sättet av privilegietilldelning.
Låt oss sätta dessa användare i arbete nu.
Vi kan se de beviljade privilegierna för en användare med SHOW GRANTS-syntax. Här är vad som för närvarande är tilldelat reader_1-användarkontot:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+------------------------------------------------------+
| Grants for [email protected] |
+------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost` |
+------------------------------------------------------+
2 rows in set (0.02 sec)
Även om det ger en informativ utgång, kan du 'justera ' uttalandet för ännu mer detaljerad information om alla exakta privilegier som en tilldelad roll tillhandahåller genom att inkludera en USING-sats i SHOW GRANTS-satsen och namnge de tilldelade rollernas namn:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost' USING 'main_read_only';
+-------------------------------------------------------------+
| Grants for [email protected] |
+-------------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
| GRANT SELECT ON `practice`.`name` TO `reader_1`@`localhost` |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost` |
+-------------------------------------------------------------+
3 rows in set (0.00 sec)
Efter att ha loggat in med reader_1:
mysql> SELECT * FROM practice.name;
ERROR 1142 (42000): SELECT command denied to user 'reader_1'@'localhost' for table 'name'
Vad i hela friden? Den användaren beviljades SELECT-privilegier genom rollen main_read_only.
För att undersöka det, låt oss besöka två nya tabeller i version 8, specifikt för roller.
Mysql.role_edges-tabellen visar vilka roller som har beviljats till alla användare:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| % | main_changer | localhost | changer_1 | N |
| % | main_read_only | localhost | changer_1 | N |
| % | main_read_only | localhost | reader_1 | N |
| % | main_read_only | localhost | reader_2 | N |
| % | main_read_only | localhost | reader_3 | N |
| % | main_read_write | localhost | reader_writer | N |
+-----------+-----------------+-----------+---------------+-------------------+
6 rows in set (0.00 sec)
Men jag tror att den andra extra tabellen, mysql.default_roles, bättre kommer att hjälpa oss att lösa SELECT-problemen för user reader_1:
mysql> DESC mysql.default_roles;
+-------------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------+------+-----+---------+-------+
| HOST | char(60) | NO | PRI | | |
| USER | char(32) | NO | PRI | | |
| DEFAULT_ROLE_HOST | char(60) | NO | PRI | % | |
| DEFAULT_ROLE_USER | char(32) | NO | PRI | | |
+-------------------+----------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM mysql.default_roles;
Empty set (0.00 sec)
Tom resultatuppsättning.
Det visar sig att för att en användare ska kunna använda en roll - och i slutändan privilegierna - måste användaren tilldelas en standardroll.
mysql> SET DEFAULT ROLE main_read_only TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.11 sec)
(En standardroll kan tilldelas flera användare i ett kommando enligt ovan...)
mysql> SET DEFAULT ROLE main_read_only, main_changer TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.10 sec)
(En användare kan ha flera standardroller specificerade som i fallet för user changer_1...)
Användare reader_1 är nu inloggad...
mysql> SELECT CURRENT_USER();
+--------------------+
| CURRENT_USER() |
+--------------------+
| [email protected] |
+--------------------+
1 row in set (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE() |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.03 sec)
Vi kan se den för närvarande aktiva rollen och även att reader_1 kan utfärda SELECT-kommandon nu:
mysql> SELECT * FROM practice.name;
+--------+------------+
| f_name | l_name |
+--------+------------+
| Jim | Dandy |
| Johhny | Applesauce |
| Ashley | Zerro |
| Ashton | Zerra |
| Ashmon | Zerro |
+--------+------------+
5 rows in set (0.00 sec)
Andra dolda nyanser
Det finns en annan viktig del av pusslet vi måste förstå.
Det finns potentiellt tre olika "nivåer" eller "varianter" av rolltilldelning:
SET ROLE …;
SET DEFAULT ROLE …;
SET ROLE DEFAULT …;
Jag ger användaren reader_1 en ytterligare roll och loggar sedan in med den användaren (visas inte):
mysql> GRANT 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)
Eftersom rollen main_read_write har INSERT-behörigheten, kan användaren reader_1 nu köra det kommandot eller hur?
mysql> INSERT INTO name(f_name, l_name)
-> VALUES('Josh', 'Otwell');
ERROR 1142 (42000): INSERT command denied to user 'reader_1'@'localhost' for table 'name'
Vad händer här?
Detta kan hjälpa...
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE() |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.00 sec)
Kom ihåg att vi initialt satte user reader_1 som standardroll main_read_only. Det är här vi måste använda en av dessa distinkta "nivåer" av vad jag löst uttrycker 'rollinställning':
mysql> SET ROLE main_read_write;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE() |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)
Försök nu att INSERT igen:
mysql> INSERT INTO name(f_name, l_name)
-> VALUES('Josh', 'Otwell');
Query OK, 1 row affected (0.12 sec)
Men när användaren reader_1 loggar ut igen kommer rollen main_read_write inte längre att vara aktiv när reader_1 loggar in igen. Även om användaren reader_1 har rollen main_read_write tilldelad, är det inte standard.
Låt oss nu lära känna den tredje "nivån" av "rollinställning", SET ROLE DEFAULT.
Anta att användaren reader_1 inte har några roller tilldelade ännu:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+----------------------------------------------+
| Grants for [email protected] |
+----------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
+----------------------------------------------+
1 row in set (0.00 sec)
Låt oss GE den här användaren två roller:
mysql> GRANT 'main_changer', 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.07 sec)
Tilldela en standardroll:
mysql> SET DEFAULT ROLE ‘main_changer’ TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)
Sedan med användaren reader_1 inloggad är den standardrollen aktiv:
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE() |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)
Byt nu till rollen main_read_write:
mysql> SET ROLE 'main_read_write';
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE() |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)
Men för att återgå till den tilldelade standardrollen, använd SET ROLE DEFAULT som visas nedan:
mysql> SET ROLE DEFAULT;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE() |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)
Roller ej beviljade
Även om user changer_1 har 2 roller tillgängliga under en session:
mysql> SELECT CURRENT_ROLE();
+-----------------------------------------+
| CURRENT_ROLE() |
+-----------------------------------------+
| `main_changer`@`%`,`main_read_only`@`%` |
+-----------------------------------------+
1 row in set (0.00 sec)
Vad händer om du försöker ställa in en användare till en roll som de inte har fått?
mysql> SET ROLE main_read_write;
ERROR 3530 (HY000): `main_read_write`@`%` is not granted to `changer_1`@`localhost`
Nekad.
Take Away
Inget användarhanteringssystem skulle vara komplett utan möjligheten att begränsa eller till och med ta bort åtkomst till vissa operationer om det skulle behövas.
Vi har kommandot SQL REVOKE till vårt förfogande för att ta bort privilegier från användare och roller.
Kom ihåg att rollen main_changer har denna uppsättning privilegier, i princip gör alla de användare som beviljats den här rollen också:
mysql> SHOW GRANTS FOR main_changer;
+-----------------------------------------------------------------+
| Grants for [email protected]% |
+-----------------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%` |
| GRANT UPDATE, DELETE ON `practice`.`name` TO `main_changer`@`%` |
+-----------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> REVOKE DELETE ON practice.name FROM 'main_changer';
Query OK, 0 rows affected (0.11 sec)
mysql> SHOW GRANTS FOR main_changer;
+---------------------------------------------------------+
| Grants for [email protected]% |
+---------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%` |
| GRANT UPDATE ON `practice`.`name` TO `main_changer`@`%` |
+---------------------------------------------------------+
2 rows in set (0.00 sec)
För att veta vilka användare denna förändring påverkade kan vi besöka tabellen mysql.role_edges igen:
mysql> SELECT * FROM mysql.role_edges WHERE FROM_USER = 'main_changer';
+-----------+--------------+-----------+-----------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+--------------+-----------+-----------+-------------------+
| % | main_changer | localhost | changer_1 | N |
+-----------+--------------+-----------+-----------+-------------------+
1 row in set (0.00 sec)
Och vi kan se att user changer_1 inte längre har DELETE-behörigheten:
mysql> SHOW GRANTS FOR 'changer_1'@'localhost' USING 'main_changer';
+--------------------------------------------------------------------------+
| Grants for [email protected] |
+--------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `changer_1`@`localhost` |
| GRANT UPDATE ON `practice`.`name` TO `changer_1`@`localhost` |
| GRANT `main_changer`@`%`,`main_read_only`@`%` TO `changer_1`@`localhost` |
+--------------------------------------------------------------------------+
3 rows in set (0.00 sec)
Slutligen, om vi behöver bli av med en roll helt, har vi kommandot DROP ROLE för det:
mysql> DROP ROLE main_read_only;
Query OK, 0 rows affected (0.17 sec)
Och genom att fråga i tabellen mysql.role_edges har rollen main_read_only tagits bort:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| % | main_changer | localhost | changer_1 | N |
| % | main_read_write | localhost | reader_1 | N |
| % | main_read_write | localhost | reader_writer | N |
+-----------+-----------------+-----------+---------------+-------------------+
3 rows in set (0.00 sec)
(Bonus:Den här fantastiska YouTube-videon var en bra inlärningsresurs för mig om roller.)
Det här exemplet på användarskapande, rolltilldelning och installation är i bästa fall rudimentärt. Ändå har roller sina egna regler som gör dem långt ifrån triviala. Min förhoppning är att jag genom detta blogginlägg har belyst de områden som är mindre intuitiva än andra, vilket gör det möjligt för läsare att bättre förstå potentiella rollanvändningar inom sina system.
Tack för att du läser.