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 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
|
set @old_size = @@global.optimizer_trace_max_mem_size;
set global optimizer_trace_max_mem_size=1048576;
select user();
user()
root@localhost
create database somedb;
use somedb;
create table t1(a varchar(100));
insert into t1 values("first");
create table t2(a varchar(100));
insert into t2 values("first");
create table t3(a varchar(100));
insert into t3 values("first");
SET sql_mode = 'ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION';
create procedure p1() sql security definer
begin
declare b int;
if (select count(*) from t1)
then
select 22 into b from dual;
end if;
select a into b from t1 limit 1;
insert into t1 values(current_user());
end|
create function f1() returns int sql security definer
begin
declare b int;
select 48 into b from dual;
select a into b from t1 limit 1;
insert into t1 values(current_user());
return 36;
end|
create trigger trg2 before insert on t2 for each row
begin
insert into t3 select * from t3;
end|
SET sql_mode = default;
create sql security definer view v1 as select * from t1;
create user user1@localhost identified by '';
grant all on *.* to user1@localhost with grant option;
select user();
user()
user1@localhost
set optimizer_trace="enabled=on";
show grants;
Grants for user1@localhost
GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ABORT_EXEMPT,AUDIT_ADMIN,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FIREWALL_EXEMPT,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,GROUP_REPLICATION_STREAM,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PASSWORDLESS_USER_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SENSITIVE_VARIABLES_OBSERVER,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,TELEMETRY_LOG_ADMIN,XA_RECOVER_ADMIN ON *.* TO `user1`@`localhost` WITH GRANT OPTION
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `user1`@`localhost` WITH GRANT OPTION
# ==========================================================
# Part A.
# Test that security context changes are allowed when, and only
# when, invoker has all global privileges.
# ==========================================================
# Because invoker has all global privileges, all traces are visible:
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
call p1() 20 0
call p1() 20 0
set b@0 NULL 20 0
jump_if_not 3(3) (select count(0) from `somedb`.`t1`) 3065 0
select 22 into b from dual 407 0
select a into b from t1 limit 1 2253 0
insert into t1 values(current_user()) 20 0
# this SET always purges all remembered traces
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select f1() 214 0
select f1() 218 0
set b@0 NULL 20 0
select 48 into b from dual 407 0
select a into b from t1 limit 1 2253 0
insert into t1 values(current_user()) 20 0
freturn 3 36 20 0
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
root@localhost
root@localhost
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select * from v1 898 0
select * from v1 2041 0
set optimizer_trace_offset=0,optimizer_trace_limit=100;
insert into t2 values(current_user());
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
insert into t2 values(current_user()) 20 0
insert into t2 values(current_user()) 20 0
insert into t3 select * from t3 3120 0
# Show that really all global privileges are needed: let root
# revoke just one from user1. Because user1 does not have all global
# privileges anymore, security context changes are forbidden,
# thus there is no trace.
select user();
user()
root@localhost
revoke shutdown on *.* from user1@localhost;
select user();
user()
user1@localhost
set optimizer_trace="enabled=on";
show grants;
Grants for user1@localhost
GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ABORT_EXEMPT,AUDIT_ADMIN,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,FIREWALL_EXEMPT,FLUSH_OPTIMIZER_COSTS,FLUSH_STATUS,FLUSH_TABLES,FLUSH_USER_RESOURCES,GROUP_REPLICATION_ADMIN,GROUP_REPLICATION_STREAM,INNODB_REDO_LOG_ARCHIVE,INNODB_REDO_LOG_ENABLE,PASSWORDLESS_USER_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SENSITIVE_VARIABLES_OBSERVER,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SHOW_ROUTINE,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,TELEMETRY_LOG_ADMIN,XA_RECOVER_ADMIN ON *.* TO `user1`@`localhost` WITH GRANT OPTION
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `user1`@`localhost` WITH GRANT OPTION
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
# In CALL we execute stored procedure and notice a security
# context change. The context change is probably only relevant
# for substatements, but we still hide CALL. This is to be
# consistent with what we do when routine body should not be
# exposed. And it also feels safer to disable I_S output as
# soon as possible.
# Ps-protocol-specific note: mysqltest uses normal protocol for CALL
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
call p1() 20 0
0 1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
select f1() {
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
}
]
}
}
]
} 0
1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
root@localhost
root@localhost
root@localhost
root@localhost
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
insert into t2 values(current_user());
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
insert into t2 values(current_user()) {
"steps": [
]
} 0
1
# Verify that user1 cannot circumvent security checks by
# setting @@optimizer_trace_offset so that I_S output is disabled
# before the object (routine) is checked, and enabled in the
# middle of object usage, when 'offset' is passed.
set optimizer_trace_offset=2,optimizer_trace_limit=1;
call p1();
# Even though the routine's execution started before
# 'offset', it detected the security context changes. So the
# trace of CALL gets the "missing privilege" mark but we don't
# see it as CALL was before 'offset'.
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
# Finally, verify that if the routine's definer does modify
# @@optimizer_trace from "enabled=off" to "enabled=on", in the
# body of the routine, then tracing works. This is no security
# issue, as it was done by the routine's definer.
select user();
user()
root@localhost
create procedure p2() sql security definer
begin
declare b int;
set optimizer_trace="enabled=on";
select 22 into b from dual;
end|
select user();
user()
user1@localhost
set optimizer_trace="enabled=off";
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p2();
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
select 22 into b from dual {
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select 22 AS `22`"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
]
}
},
{
"join_execution": {
"select#": 1,
"steps": [
]
}
}
]
} 0
# Variable is as set by the routine
select @@optimizer_trace;
@@optimizer_trace
enabled=on,one_line=off
# ==========================================================
# Part B.
# Do same tests but with SQL SECURITY INVOKER objects, to verify that
# the restriction on security context changes is not present.
# ==========================================================
select user();
user()
root@localhost
alter procedure p1 sql security invoker;
alter function f1 sql security invoker;
alter sql security invoker view v1 as select * from t1;
# Triggers cannot be SQL SECURITY INVOKER so we don't test
# them here.
alter procedure p2 sql security invoker;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
call p1() 20 0
call p1() 20 0
set b@0 NULL 20 0
jump_if_not 3(3) (select count(0) from `somedb`.`t1`) 3065 0
select 22 into b from dual 407 0
select a into b from t1 limit 1 2253 0
insert into t1 values(current_user()) 20 0
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select f1() 214 0
select f1() 218 0
set b@0 NULL 20 0
select 48 into b from dual 407 0
select a into b from t1 limit 1 2253 0
insert into t1 values(current_user()) 20 0
freturn 3 36 20 0
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select * from v1 898 0
select * from v1 2041 0
set optimizer_trace_offset=2,optimizer_trace_limit=1;
call p1();
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
set b@0 NULL 20 0
set optimizer_trace="enabled=off";
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p2();
# SELECT substatement is traced (no security context change)
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
select 22 into b from dual {
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select 22 AS `22`"
}
]
}
},
{
"join_optimization": {
"select#": 1,
"steps": [
]
}
},
{
"join_execution": {
"select#": 1,
"steps": [
]
}
}
]
} 0
select @@optimizer_trace;
@@optimizer_trace
enabled=on,one_line=off
# ==========================================================
# Part C.
# User1 got traces. Determine the minimum set of privileges he
# needed for that.
# ==========================================================
drop procedure p2;
select user();
user()
root@localhost
revoke all privileges, grant option from user1@localhost;
# Grant minimum privileges to use the routines and views,
# without considering optimizer trace:
grant execute on procedure p1 to user1@localhost;
grant execute on function f1 to user1@localhost;
grant select (a) on v1 to user1@localhost;
# Objects above are SQL SECURITY INVOKER, so invoker needs
# privileges on objects used internally:
grant select (a) on t1 to user1@localhost;
grant insert (a) on t1 to user1@localhost;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace="enabled=on";
show grants;
Grants for user1@localhost
GRANT EXECUTE ON FUNCTION `somedb`.`f1` TO `user1`@`localhost`
GRANT EXECUTE ON PROCEDURE `somedb`.`p1` TO `user1`@`localhost`
GRANT SELECT (`a`) ON `somedb`.`v1` TO `user1`@`localhost`
GRANT SELECT (`a`), INSERT (`a`) ON `somedb`.`t1` TO `user1`@`localhost`
GRANT USAGE ON *.* TO `user1`@`localhost`
# Those privileges are not enough to see traces:
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
# In CALL we execute stored procedure and notice that body should
# not be exposed. The trace of this CALL would not expose the
# body. Trace of substatements would. But, due to
# implementation, CALL is hidden.
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
call p1() 20 0
0 1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
# SELECT is hidden (same reason as for CALL).
# Ps-protocol-specific note: preparation of SELECT above does not
# execute f1, so does not risk exposing body, so its trace is
# visible.
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
select f1() {
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
}
]
}
}
]
} 0
1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
# Cannot see anything as it would expose body of view
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# C.0) Add more privileges:
select user();
user()
root@localhost
# - for use of t1 in routines and view:
grant select on t1 to user1@localhost;
# - for use of view:
grant select, show view on v1 to user1@localhost;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
call p1() 20 0
0 1
# Trace exposed body of routine, and content of t1, which we
# could see anyway:
show create procedure p1;
Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
p1 ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION NULL utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci
select * from t1 limit 1;
a
first
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select f1() 214 0
0 1
# Trace exposed body of routine, and content of t1, which we
# could see anyway:
show create function f1;
Function sql_mode Create Function character_set_client collation_connection Database Collation
f1 ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION NULL utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select * from v1 898 0
select * from v1 2041 0
# Trace exposed body of view, and content of t1, which we
# could see anyway:
show create view v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY INVOKER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8mb4 utf8mb4_0900_ai_ci
# Now remove each privilege to verify that it was needed:
# C.1) remove table-level SELECT privilege on t1
select user();
user()
root@localhost
revoke select on t1 from user1@localhost;
grant select (a) on t1 to user1@localhost;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
# Cannot see those substatements which use t1
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
call p1() {
"steps": [
]
} 0
1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
# Cannot see those substatements which use t1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select f1() 214 0
0 1
# Trace exposed body of routine, which we could see anyway:
set optimizer_trace="enabled=off";
show create function f1;
Function sql_mode Create Function character_set_client collation_connection Database Collation
f1 ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION NULL utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci
set optimizer_trace="enabled=on";
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
# Cannot see anything as it might expose some data from columns
# of t1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# C.2) remove table-level SELECT privilege on view
select user();
user()
root@localhost
# Put back privilege removed in C.1
grant select on t1 to user1@localhost;
# And remove a next one:
revoke select on v1 from user1@localhost;
grant select (a) on v1 to user1@localhost;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
# Cannot see anything as it might expose some data from columns
# of v1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# C.3) remove SHOW VIEW privilege on view
select user();
user()
root@localhost
# Put back privilege removed in C.3
grant select on v1 to user1@localhost;
# And remove a next one:
revoke show view on v1 from user1@localhost;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace="enabled=off";
# We have no right to see view's body:
show create view v1;
ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
set optimizer_trace="enabled=on";
# Verify that optimizer trace does not influence the privilege
# checking in SHOW CREATE:
show create view v1;
ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
# Cannot see anything as it would expose body of view
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
1
1
# ==========================================================
# Part D.
# Like Part C, but instead of SQL SECURITY INVOKER objects
# created by root and used by User1, let's have SQL SECURITY
# DEFINER objects created and used by User1. Determine the
# minimum set of privileges he needs for that.
# ==========================================================
select user();
user()
root@localhost
drop procedure p1;
drop function f1;
drop view v1;
drop trigger trg2;
revoke all privileges, grant option from user1@localhost;
# Grant minimum privileges to create and use objects,
# without considering optimizer trace:
grant create routine on somedb.* to user1@localhost;
grant trigger on t2 to user1@localhost;
grant create view on somedb.* to user1@localhost;
grant select (a) on t1 to user1@localhost;
grant insert (a) on t1 to user1@localhost;
grant insert (a) on t2 to user1@localhost;
grant select (a) on t3 to user1@localhost;
grant insert (a) on t3 to user1@localhost;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace="enabled=on";
SET sql_mode = 'ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION';
create procedure p1() sql security definer
begin
declare b int;
if (select count(*) from t1)
then
select 22 into b from dual;
end if;
select a into b from t1 limit 1;
insert into t1 values(current_user());
end|
create function f1() returns int sql security definer
begin
declare b int;
select 48 into b from dual;
select a into b from t1 limit 1;
insert into t1 values(current_user());
return 36;
end|
create trigger trg2 before insert on t2 for each row
begin
insert into t3 select * from t3;
end|
create sql security definer view v1 as select * from t1;
SET sql_mode = default;
# Creating a view is not enough to be able to SELECT it...
select user();
user()
root@localhost
grant select (a) on v1 to user1@localhost;
select user();
user()
user1@localhost
# Those privileges are not enough to see traces:
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
# Can see body of routine (as definer), but not statements using t1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
call p1() 20 0
call p1() 20 0
set b@0 NULL 20 0
0 1
select 22 into b from dual 407 0
0 1
0 1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
# Can see body of routine (as definer), but not statements using t1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select f1() 214 0
select f1() 218 0
set b@0 NULL 20 0
select 48 into b from dual 407 0
0 1
0 1
freturn 3 36 20 0
show create function f1;
Function sql_mode Create Function character_set_client collation_connection Database Collation
f1 ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int
begin
declare b int;
select 48 into b from dual;
select a into b from t1 limit 1;
insert into t1 values(current_user());
return 36;
end utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
# Cannot see anything as it might expose some data from columns
# of t1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
set optimizer_trace_offset=0,optimizer_trace_limit=100;
insert into t2 values(current_user());
# Cannot see anything as it might expose some data from
# columns of t2
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# Also test a query accessing t1 in FROM clause:
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select a from (select a from t1 where a like "f%") as tt where a like "fi%";
a
first
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# D.0) Add more privileges:
select user();
user()
root@localhost
# - for use of t1 in routines and view:
grant select on t1 to user1@localhost;
# - for use of view:
grant select, show view on v1 to user1@localhost;
# - for use of trigger
grant select on t2 to user1@localhost;
grant select on t3 to user1@localhost;
delete from t1 where a<>"first";
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
call p1();
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
call p1() 20 0
call p1() 20 0
set b@0 NULL 20 0
jump_if_not 3(3) (select count(0) from `somedb`.`t1`) 2868 0
select 22 into b from dual 218 0
select a into b from t1 limit 1 2041 0
insert into t1 values(current_user()) 20 0
# Trace exposed body of routine, and content of t1, which we
# could see anyway:
show create procedure p1;
Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
p1 ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`localhost` PROCEDURE `p1`()
begin
declare b int;
if (select count(*) from t1)
then
select 22 into b from dual;
end if;
select a into b from t1 limit 1;
insert into t1 values(current_user());
end utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci
select * from t1 limit 1;
a
first
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select f1();
f1()
36
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select f1() 214 0
select f1() 218 0
set b@0 NULL 20 0
select 48 into b from dual 218 0
select a into b from t1 limit 1 2041 0
insert into t1 values(current_user()) 20 0
freturn 3 36 20 0
# Trace exposed body of routine, and content of t1, which we
# could see anyway:
show create function f1;
Function sql_mode Create Function character_set_client collation_connection Database Collation
f1 ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int
begin
declare b int;
select 48 into b from dual;
select a into b from t1 limit 1;
insert into t1 values(current_user());
return 36;
end utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select * from v1 898 0
select * from v1 2041 0
# Trace exposed body of view, and content of t1, which we
# could see anyway:
show create view v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`user1`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1` utf8mb4 utf8mb4_0900_ai_ci
set optimizer_trace_offset=0,optimizer_trace_limit=100;
insert into t2 values(current_user());
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
insert into t2 values(current_user()) 20 0
insert into t2 values(current_user()) 20 0
insert into t3 select * from t3 2881 0
# Trace exposed body of trigger, and content of t2/t3, which we
# could see anyway:
show create trigger trg2;
Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation Created
trg2 ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION CREATE DEFINER=`user1`@`localhost` TRIGGER `trg2` BEFORE INSERT ON `t2` FOR EACH ROW begin
insert into t3 select * from t3;
end utf8mb4 utf8mb4_0900_ai_ci utf8mb4_0900_ai_ci #
select * from t2, t3 order by t2.a, t3.a limit 1;
a a
first first
# Trace exposed content of t1 which we could see anyway:
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select a from (select a from t1 where a like "f%") as tt where a like "fi%";
a
first
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
select a from (select a from t1 where a like "f%") as tt where a like "fi%" 1104 0
select a from (select a from t1 where a like "f%") as tt where a like "fi%" 3366 0
# For routines, as they only use t1 and we added only one
# privilege on t1, we have nothing to remove.
# Now remove each privilege to verify that it was needed for
# the view.
# D.1) remove table-level SELECT privilege on v1
select user();
user()
root@localhost
revoke select on v1 from user1@localhost;
grant select (a) on v1 to user1@localhost;
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
# Cannot see anything as it might expose some data from columns
# of v1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# D.2) remove table-level SHOW VIEW privilege on v1
select user();
user()
root@localhost
# Put back privilege removed in D.1
grant select on v1 to user1@localhost;
# And remove a next one:
revoke show view on v1 from user1@localhost;
select user();
user()
user1@localhost
# We have no right to see view's body:
show create view v1;
ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
# Cannot see anything as it would expose body of view
select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY TRACE INSUFFICIENT_PRIVILEGES
1
1
# D.3) remove table-level SELECT privilege on t1
select user();
user()
root@localhost
# Put back privilege removed in D.2
grant show view on v1 to user1@localhost;
# And remove a next one:
revoke select on t1 from user1@localhost;
grant select (a) on t1 to user1@localhost;
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1;
a
first
user1@localhost
user1@localhost
# Cannot see anything as it might expose some data from columns
# of t1
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# Now remove each privilege to verify that it was needed for
# the trigger:
# D.4) remove table-level SELECT privilege on t2
select user();
user()
root@localhost
revoke select on t2 from user1@localhost;
grant select (a) on t2 to user1@localhost;
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
insert into t2 values(current_user());
# Cannot see anything as it might expose some data from
# columns of t2
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
0 1
0 1
# D.5) remove table-level SELECT privilege on t3
select user();
user()
root@localhost
# Put back privilege removed in D.4
grant select on t2 to user1@localhost;
# And remove a next one:
revoke select on t3 from user1@localhost;
grant select (a) on t3 to user1@localhost;
select user();
user()
user1@localhost
set optimizer_trace_offset=0,optimizer_trace_limit=100;
insert into t2 values(current_user());
# Cannot see substatement as it might expose some data from
# columns of t3
select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
QUERY length(TRACE) INSUFFICIENT_PRIVILEGES
insert into t2 values(current_user()) 20 0
insert into t2 values(current_user()) 20 0
0 1
# Cleanup
select user();
user()
root@localhost
drop user user1@localhost;
Warnings:
Warning 4005 User 'user1'@'localhost' is referenced as a definer account in a view.
Warning 4005 User 'user1'@'localhost' is referenced as a definer account in a stored routine.
Warning 4005 User 'user1'@'localhost' is referenced as a definer account in a trigger.
# ==========================================================
# Part E.
# Misc tests.
# ==========================================================
select user();
user()
root@localhost
drop view v1;
create sql security definer view v1 as select * from t1 where 'secret';
create user user1@localhost identified by '';
Warnings:
Warning 4005 User 'user1'@'localhost' is referenced as a definer account in a stored routine.
Warning 4005 User 'user1'@'localhost' is referenced as a definer account in a trigger.
grant create, insert, select on somedb.* to user1@localhost;
grant create routine on somedb.* to user1@localhost;
select user();
user()
user1@localhost
user1 cannot see view's body:
show create view v1;
ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
SET sql_mode = 'ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION';
user1 creates a procedure
create procedure proc() sql security definer
begin
set optimizer_trace="enabled=on";
set optimizer_trace_offset=0,optimizer_trace_limit=100;
select * from v1 limit 0;
create table leak select * from information_schema.optimizer_trace;
set optimizer_trace="enabled=off";
end|
SET sql_mode = default;
select user();
user()
root@localhost
root runs procedure, without fear of risk as it is SQL SECURITY DEFINER
call proc();
a
select user();
user()
user1@localhost
user1 cannot see view's body:
select * from leak;
QUERY TRACE MISSING_BYTES_BEYOND_MAX_MEM_SIZE INSUFFICIENT_PRIVILEGES
# Cleanup
select user();
user()
root@localhost
drop database somedb;
drop user user1@localhost;
set @@global.optimizer_trace_max_mem_size = @old_size;
|