diff --git a/media/features/ledger/ContosoCupBetting.png b/media/features/ledger/ContosoCupBetting.png new file mode 100644 index 00000000..1a4a89e7 Binary files /dev/null and b/media/features/ledger/ContosoCupBetting.png differ diff --git a/media/features/ledger/Download-Receipt.png b/media/features/ledger/Download-Receipt.png new file mode 100644 index 00000000..aa7cfd91 Binary files /dev/null and b/media/features/ledger/Download-Receipt.png differ diff --git a/media/features/ledger/Ledger-Tables.png b/media/features/ledger/Ledger-Tables.png new file mode 100644 index 00000000..f6c02d93 Binary files /dev/null and b/media/features/ledger/Ledger-Tables.png differ diff --git a/media/features/ledger/Michael.jpg b/media/features/ledger/Michael.jpg new file mode 100644 index 00000000..0e9a9755 Binary files /dev/null and b/media/features/ledger/Michael.jpg differ diff --git a/media/features/ledger/Pieter.jpg b/media/features/ledger/Pieter.jpg new file mode 100644 index 00000000..678eb169 Binary files /dev/null and b/media/features/ledger/Pieter.jpg differ diff --git a/media/features/ledger/Place-Bet.png b/media/features/ledger/Place-Bet.png new file mode 100644 index 00000000..e5fca616 Binary files /dev/null and b/media/features/ledger/Place-Bet.png differ diff --git a/samples/features/security/ledger/README.md b/samples/features/security/ledger/README.md index 85f25ada..790d3d36 100644 --- a/samples/features/security/ledger/README.md +++ b/samples/features/security/ledger/README.md @@ -5,3 +5,4 @@ This set of samples/demos showcases [Ledger](https://docs.microsoft.com/en-us/azure/azure-sql/database/ledger-overview). - [Demo of Ledger in Azure SQL Database](./azure-sql-database/README.md) +- [Demo of Ledger in SQL Server](./sql-server/README.md) diff --git a/samples/features/security/ledger/sql-server/README.md b/samples/features/security/ledger/sql-server/README.md new file mode 100644 index 00000000..ea253c4b --- /dev/null +++ b/samples/features/security/ledger/sql-server/README.md @@ -0,0 +1,118 @@ +![](../../../../manage/sql-server-extended-security-updates/media/solutions-microsoft-logo-small.png) + +# Ledger SQL Server - Demo + +The demo in this folder showcases the [ledger](https://docs.microsoft.com/en-us/azure/azure-sql/database/ledger-overview) feature in SQL Server. The demo uses the Contoso World Cup application. + +## Content + +[About this sample](#about-this-sample) +[Before you begin](#before-you-begin) +[Setup](#setup) +[Demo - show the main benefits of the ledger feature](#Demo---show-the-main-benefits-of-the-ledger-feature) + +## About this sample + +- **Applies to:** SQL Server 2022 (16.x) +- **Key features:** Ledger +- **Workload:** World Cup Betting application +- **Programming Language:** C#, Transact-SQL +- **Authors:** Pieter Vanhove +- **Update history:** + +## Before you begin + +Before you begin, you need an Azure subscription. If you don't already have an Azure subscription, you can get one for free [here](https://azure.microsoft.com/free/). + +You also need to make sure the following software is installed on your machine: + +1. [SQL Server 2022](https://www.microsoft.com/en-us/sql-server/sql-server-downloads). Even SQL Server 2022 Express edition should be sufficient for this demo. +1. [Visual Studio Code](https://code.visualstudio.com/download). +1. [SQL Server Management Studio (SSMS)](https://learn.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms?view=sql-server-ver16) - version 18.12.1 or later is required. +1. [Azure Data Studio](https://learn.microsoft.com/en-us/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver16) - version 1.41 or later is required. + +## Setup + +### Azure Storage Account +1. Go to the [Azure Portal](https://portal.azure.com/) and create a new resource group that will be used for the demo environment. +1. Create a new Azure Storage Account in the resource group. Make sure you [enable version-level immutability support on the storage account](https://learn.microsoft.com/en-us/azure/storage/blobs/immutable-policy-configure-version-scope?tabs=azure-portal#enable-version-level-immutability-support-on-a-storage-accountconfigure) to ensure that database digests are protected from tampering. Make sure the immutability policy allows protected append writes to append blobs and that the policy is locked. +1. Follow the steps on [Azure Storage account permission](https://learn.microsoft.com/en-us/sql/relational-databases/security/ledger/ledger-digest-management?view=sql-server-ver16#azure-storage-account-permission) to create a credential on the SQL Server Instance. + +### Database + +1. Clone/download and unpack the repository. +1. Open SQL Server Management Studio and connect to your SQL Server 2022 instance. +1. Open the file [CreateDatabaseSchemaAndPopulateDatabase.sql](../sql-server/setup/CreateDatabaseSchemaAndPopulateDatabase.sql) +1. Go to line 12 and replace **YourStorageAccountName** with the name of the Azure storage account you just created. +1. Run the script. This will + 1. Create the database + 1. Create the database schema + 1. Create a function to calculate the payout + 1. Create a stored procedure to place bets + 1. Configure the automatic digest storage + 1. Populate the database. + +### Web Application + +1. Open Visual Studio +1. Open the folder [WorldCup](../WorldCup) +1. Click on the file Program.cs in the Explorer and go to line 25 +1. Change the *Server* parameter in the connection string to the name of your SQL Server Instance. +1. Save your modifications. + +## Demo - show the main benefits of the ledger feature + +### Scenario + +Contoso Cup Betting is using a simple web application where people can place bets, in this case bets on soccer games. + +In this demo you will use 2 different types of users: + +- Pieter, the DBA of the company. +- Michael, who is an internal auditor. + +All the games and bets that are placed needs to be immutable. Using ledger tables is a good solution for this since the data of all the bets and the games needs to be fully trusted. It prevents the company from removing or updating any placed bets. Pieter, who has placed a few bets on some games, wants to maliciously increase his payout. Unfortunately for Pieter, the bets table is an append-only ledger table, which means he can't change his bets with regular T-SQL updates. Pieter knows how to tamper with the data directly in the data file and thinks his change will never be noticed. However, Michael reviews the Worldcup database after the tournament is over and noticed that the verification procedure fails, which means that the data cannot be trusted. + +### Prepare for the demo + +Perform the below steps before you show the demo. + +1. Connect to the database + 1. Start the Azure Data Studio. + 1. Open the Notebook [Ledger - World Cup Demo - SQL Server 2022](../tsql-scripts/Ledger%20-%20World%20Cup%20Demo%20-%20SQL%20Server%202022.ipynb) + 1. Attach the Notebook to the SQL Server Instance and WorldCup database you have created during the demo Setup. + +1. Prepare a browser window . + 1. Open Visual Studio + 1. Open the folder [WorldCup](../WorldCup) + 1. Go to the top menu and click *Terminal - New Terminal* + 1. In the terminal window type *dotnet run*. This will launch the web application. + 1. Open your browser and enter the following URL *http://localhost:3000* + +![Contoso Cup Betting](../../../../../media/features/ledger/ContosoCupBetting.png) + +### Demo steps + +1. Show the app and the database. + 1. Show the Contoso World Cup web app in the browser. Explain that this is a list of all the soccer games. Show that you are logged in as Pieter (the DBA of the company) and place a bet. + ![Place Bet](../../../../../media/features/ledger/Place-Bet.png) + 1. Once you have placed the bet, show that the user can download a cryptographic receipt that proves the bet. This receipt could also be used to verify the database afterwards but is not part of this demo. + ![Download Cryptographic Receipt](../../../../../media/features/ledger/Download-Receipt.png) + 1. Go to the Notebook and show the 2 tables, Moneyline and bets in the Explorer. Explain that Moneyline table is an updatable ledger table and bets an append-only ledger table. + ![Ledger Tables](../../../../../media/features/ledger/Ledger-Tables.png) + 1. Run the `code cell 1` in the Notebook to show the games in the Moneyline table. + +1. Show how ledger captures history information in the updatable ledger table + 1. Pieter has been asked to change the Home Country Odds for the game with ID 1. Run `code cell 2` in the Notebook to update the record. + 1. Run `code cell 3` in the Notebook to show the history of the updated record. Explain that updates are stored as 2 records. A delete of the old version and an insert of the new version of the row. + +1. Show how append-only ledger table works and how ledger helps investigate tampering by DBAs. + 1. Michael, who is an internal auditor, performs a routine review of changes in the Worldcup database. As his first step, Michael runs the ledger verification to be sure he can trust the data he’s going to examine. Run `code cell 4` to execute the verification stored procedure. The result should be "Success"! + 1. Pieter wants to maliciously increase his payout for the game he betted on. Run `code cell 5 and 6` to try to update the record. Explain that update on an append-only ledger table is blocked. + 1. Pieter thinks he's smart and tampers with the data directly into the data file by using a stored procedure. Do not run `code cell 7` because the stored procedure does not exists. + 1. Michael, reviews the Worldcup database again after the tournament is over. He noticed that the verification procedure failed. Observe the results of code cell 8 to see evidence of tampering in a database ledger. **DO NOT RUN** the cell just review the pre-run results to see tampering in action. + 1. Review the history of the transaction and show to the audience that you can verify which transactions cannot be trusted. **DO NOT RUN** the cell just review the pre-run results. + +## Key Takeaways + +Ledger makes your data tamper-evident and cryptographically verifiable, which helps ensure non-repudiation, eliminating a need to run laborious manual audits and time-consuming investigations. diff --git a/samples/features/security/ledger/sql-server/setup/CreateDatabaseSchemaAndPopulateDatabase.sql b/samples/features/security/ledger/sql-server/setup/CreateDatabaseSchemaAndPopulateDatabase.sql new file mode 100644 index 00000000..ad01653a --- /dev/null +++ b/samples/features/security/ledger/sql-server/setup/CreateDatabaseSchemaAndPopulateDatabase.sql @@ -0,0 +1,136 @@ +--Create the database +CREATE DATABASE WorldCup +GO +ALTER DATABASE [WorldCup] SET ALLOW_SNAPSHOT_ISOLATION ON +GO + +USE WorldCup +GO +--Enable Automatic Digest Storage +--REPLACE with the name of your Azure storage account. +ALTER DATABASE SCOPED CONFIGURATION + SET LEDGER_DIGEST_STORAGE_ENDPOINT = 'https://.blob.core.windows.net'; +GO +--Create the table MoneyLine. This table will contain all the games and the odds +CREATE TABLE [dbo].[MoneyLine]( + [MoneyLineID] [int] IDENTITY(1,1) NOT NULL, + [HomeCountry] [nvarchar](50) NOT NULL, + [HomeCountryOdds] [INT] NOT NULL, + [DrawOdds] [INT] NOT NULL, + [VisitCountry] [nvarchar](50) NOT NULL, + [VisitCountryOdds] [INT] NOT NULL, + [GameDateTime] [datetime2] NOT NULL + ) +WITH +( + SYSTEM_VERSIONING = ON, + LEDGER = ON +); +GO + +--Populate the MoneyLine table + +INSERT INTO [dbo].[MoneyLine] ([HomeCountry], [HomeCountryOdds], [DrawOdds], [VisitCountry],[VisitCountryOdds],[GameDateTime]) +VALUES ('Qatar', 250, 245, 'Ecuador',105,'2022-11-20 17:00:00'), +('England', -340, 390, 'Iran', 1000, '2022-11-21 14:00:00' ), +('Senegal', 475, 270, 'Netherlands',-165, '2022-11-21 17:00:00'), +('USA', 145, 205, 'Wales',205, '2022-11-21 20:00:00'), + +('Argentina', -575, 600, 'Saudi Arabia',1500, '2022-11-22 11:00:00'), +('Denmark', -230, 340, 'Tunisia',600, '2022-11-22 14:00:00'), +('Mexico', 170, 215, 'Poland',165, '2022-11-22 17:00:00'), +('France', -550, 600, 'Australia',1300, '2022-11-22 20:00:00'), + +('Morocco', 370, 230, 'Croatia',-125, '2022-11-23 11:00:00'), +('Germany', -280, 400, 'Japan',700, '2022-11-23 14:00:00'), +('Spain', -380, 475, 'Costa Rica',1000, '2022-11-23 17:00:00'), +('Belgium', -350, 450, 'Canada',900, '2022-11-23 20:00:00'), + +('Switzerland', -120, 240, 'Cameroon',350, '2022-11-24 11:00:00'), +('Uruguay', -120, 235, 'South Korea',360, '2022-11-24 14:00:00'), +('Portugal', -210, 310, 'Ghana',600, '2022-11-24 17:00:00'), +('Brazil', -235, 350, 'Serbia',600, '2022-11-24 20:00:00'), + +('Wales', 120, 215, 'Iran',250, '2022-11-25 11:00:00'), +('Qatar', 275, 225, 'Senegal',105, '2022-11-25 14:00:00'), +('Netherlands', 155, 300, 'Ecuador',390, '2022-11-25 17:00:00'), +('England', 140, 255, 'USA',400, '2022-11-25 20:00:00'), + +('Tunisia', 190, 195, 'Australia',165, '2022-11-26 11:00:00'), +('Poland', -140, 255, 'Saudi Arabia',390, '2022-11-26 14:00:00'), +('France', -110, 240, 'Denmark',310, '2022-11-26 17:00:00'), +('Argentina', -170, 285, 'Mexico',475, '2022-11-26 20:00:00'), + +('Japan', -105, 230, 'Costa Rica',310, '2022-11-27 11:00:00'), +('Belgium', -195, 310, 'Morocco',500, '2022-11-27 14:00:00'), +('Croatia', -130, 275, 'Canada',340, '2022-11-27 17:00:00'), +('Spain', 155, 230, 'Germany',170, '2022-11-27 20:00:00'), + +('Cameroon', 300, 235, 'Serbia',-105, '2022-11-28 11:00:00'), +('South Korea', 145, 195, 'Ghana',210, '2022-11-28 14:00:00'), +('Brazil', -230, 340, 'Switzerland',600, '2022-11-28 17:00:00'), +('Portugal', 110, 225, 'Uruguay',260, '2022-11-28 20:00:00'); + +--Create the table Bets that will contain all the bets +CREATE TABLE [dbo].[Bets]( + [BetID] [int] IDENTITY(1,1) NOT NULL, + [MoneylineID] [int] NOT NULL, + [FirstName] [nvarchar](50) NOT NULL, + [LastName] [nvarchar](50) NOT NULL, + [Country] [nvarchar](50), + [Bet] [money] NOT NULL, + [Payout] [money] NOT NULL, + [BetDateTime] [datetime2] NOT NULL + ) +WITH (LEDGER = ON (APPEND_ONLY = ON)); +GO + +-- Create a function to calculate the Payout +-- Calculating Payouts From Positive Moneyline Odds ---- Potential Profit = Stake x (Odds/100) + Stake +-- Calculating Payouts From Negative Moneyline Odds ---- Potential Profit = Stake / (Odds/100) + Stake +CREATE FUNCTION fn_CalculatePayout +( + -- Add the parameters for the function here + @Stake decimal(8,2), @Odds decimal(8,2) +) +RETURNS decimal(8,2) +AS +BEGIN + -- Declare the return variable here + DECLARE @Payout decimal(8,2) + + -- Add the T-SQL statements to compute the return value here + IF @Odds > 0 + SET @Payout = @Stake * (@Odds/100) + @Stake + ELSE + SET @Payout = @Stake / (ABS(@Odds)/100) + @Stake + + -- Return the result of the function + RETURN @Payout + +END +GO + + +-- Create a stored procedure to place a bet +CREATE PROCEDURE usp_PlaceBet + @MoneylineID INT, + @FirstName NVARCHAR(50), + @LastName NVARCHAR(50), + @Country NVARCHAR(50), + @Bet MONEY, + @Odds INT +AS +BEGIN + SET NOCOUNT ON; + INSERT INTO [dbo].[Bets] ([MoneylineID], [FirstName], [LastName], [Country], [Bet], [Payout],[BetDateTime]) VALUES (@MoneylineID, @FirstName, @LastName, @Country, @Bet, dbo.fn_CalculatePayout(@Odds,@Bet),GETDATE()) +END +GO + +-- Populate the bets table by executing the usp_PlaceBet stored procedure + EXEC usp_PlaceBet @MoneylineID=1, @Firstname='Catherine', @LastName='Abel', @Country='Qatar', @Bet=150, @Odds=250 + EXEC usp_PlaceBet @MoneylineID=2, @Firstname='Brandon', @LastName='Flowers', @Country='England', @Bet=350, @Odds=-340 + EXEC usp_PlaceBet @MoneylineID=3, @Firstname='Lenny', @LastName='Kravitz', @Country='Netherlands', @Bet=250, @Odds=-165 + EXEC usp_PlaceBet @MoneylineID=8, @Firstname='Eric', @LastName='Clapton', @Country='France', @Bet=400, @Odds=-550 + EXEC usp_PlaceBet @MoneylineID=12, @Firstname='Johnny', @LastName='Cash', @Country='Belgium', @Bet=300, @Odds=-350 +GO \ No newline at end of file diff --git a/samples/features/security/ledger/sql-server/tsql-scripts/Ledger - World Cup Demo - SQL Server 2022.ipynb b/samples/features/security/ledger/sql-server/tsql-scripts/Ledger - World Cup Demo - SQL Server 2022.ipynb new file mode 100644 index 00000000..ab6a3bae --- /dev/null +++ b/samples/features/security/ledger/sql-server/tsql-scripts/Ledger - World Cup Demo - SQL Server 2022.ipynb @@ -0,0 +1,500 @@ +{ + "metadata": { + "kernelspec": { + "name": "SQL", + "display_name": "SQL", + "language": "sql" + }, + "language_info": { + "name": "sql", + "version": "" + } + }, + "nbformat_minor": 2, + "nbformat": 4, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Ledger - World Cup Betting Demo - SQL Server 2022" + ], + "metadata": { + "azdata_cell_guid": "37b80cef-1217-4d8b-a71e-56b950e42727" + }, + "attachments": {} + }, + { + "cell_type": "markdown", + "source": [ + "## ![Database Administrator](../../../../../media/features/ledger/Pieter.jpg \"Database Administrator\") Updatable Ledger Tables\r\n", + "" + ], + "metadata": { + "azdata_cell_guid": "a8c136c6-b1b1-4586-a34f-1574e2bcc704" + }, + "attachments": {} + }, + { + "cell_type": "markdown", + "source": [ + "All the soccer games are stored in an updatable ledger table. Every update on the games or the odds needs to verifiable." + ], + "metadata": { + "azdata_cell_guid": "8fb98a62-b4eb-466a-b3d6-7aed7e8c745a" + }, + "attachments": {} + }, + { + "cell_type": "code", + "source": [ + "SELECT\tt.[commit_time] AS [CommitTime] \r\n", + "\t, t.[principal_name] AS [UserName]\r\n", + "\t, l.[MoneyLineID]\r\n", + " , l.[HomeCountry]\r\n", + "\t, l.[HomeCountryOdds]\r\n", + "\t, l.[DrawOdds]\r\n", + "\t, l.[VisitCountry]\r\n", + "\t, l.[VisitCountryOdds]\r\n", + " , l.[GameDateTime]\r\n", + "\t, l.[ledger_operation_type_desc] AS Operation\r\n", + "\tFROM [dbo].[MoneyLine_Ledger] l\r\n", + "\tJOIN sys.database_ledger_transactions t\r\n", + "\tON t.transaction_id = l.ledger_transaction_id\r\n", + "\tORDER BY t.commit_time DESC;" + ], + "metadata": { + "language": "sql", + "azdata_cell_guid": "fe975698-6c3c-4824-8b9f-0a421190027d" + }, + "outputs": [], + "execution_count": null + }, + { + "cell_type": "code", + "source": [ + "UPDATE MoneyLine\r\n", + "SET [HomeCountryOdds]=500\r\n", + "WHERE MoneyLineID=1" + ], + "metadata": { + "language": "sql", + "azdata_cell_guid": "f75156e5-6996-4c40-9176-069f1b6ae1c0" + }, + "outputs": [], + "execution_count": 39 + }, + { + "cell_type": "code", + "source": [ + "SELECT\r\n", + "\tt.[commit_time] AS [CommitTime] \r\n", + "\t, t.[principal_name] AS [UserName]\r\n", + "\t, l.[MoneyLineID]\r\n", + " , l.[HomeCountry]\r\n", + "\t, l.[HomeCountryOdds]\r\n", + "\t, l.[DrawOdds]\r\n", + "\t, l.[VisitCountry]\r\n", + "\t, l.[VisitCountryOdds]\r\n", + " , l.[GameDateTime]\r\n", + "\t, l.[ledger_operation_type_desc] AS Operation\r\n", + "\tFROM [dbo].[MoneyLine_Ledger] l\r\n", + "\tJOIN sys.database_ledger_transactions t\r\n", + "\tON t.transaction_id = l.ledger_transaction_id\r\n", + "\tWHERE t.[commit_time] > DATEADD(MINUTE, -10, GETDATE())\r\n", + "\tORDER BY t.commit_time DESC;" + ], + "metadata": { + "language": "sql", + "azdata_cell_guid": "4ae7db74-7c44-4c83-b667-adfa1b77a8f3" + }, + "outputs": [], + "execution_count": 40 + }, + { + "cell_type": "markdown", + "source": [ + "## Append-Only Ledger Table Bets" + ], + "metadata": { + "azdata_cell_guid": "d7582e04-61cc-4a9e-bc80-fdca578d4042" + }, + "attachments": {} + }, + { + "cell_type": "markdown", + "source": [ + "## ![Auditor](../../../../../media/features/ledger/Michael.jpg \"Auditor\") The auditor can verify the database to be sure that the data can be trusted" + ], + "metadata": { + "azdata_cell_guid": "5a3e79f0-edb3-4669-9402-a38d94871d78" + }, + "attachments": {} + }, + { + "cell_type": "markdown", + "source": [ + "Michael, who is an auditor, performs a routine review of changes in the Worldcup database. As his first step, Michael runs the ledger verification to make sure he can trust the data he’s going to examine. The result should be \"Success\"!" + ], + "metadata": { + "azdata_cell_guid": "6c9e9515-7a77-4ed1-b00a-1b67e2b4be38" + }, + "attachments": {} + }, + { + "cell_type": "code", + "source": [ + "DECLARE @digest_locations NVARCHAR(MAX) = (SELECT * FROM sys.database_ledger_digest_locations FOR JSON AUTO, INCLUDE_NULL_VALUES);\r\n", + " SELECT @digest_locations as digest_locations;\r\n", + " BEGIN TRY\r\n", + " EXEC sys.sp_verify_database_ledger_from_digest_storage @digest_locations;\r\n", + " SELECT 'Ledger verification succeeded.' AS Result;\r\n", + " END TRY\r\n", + " BEGIN CATCH\r\n", + " THROW;\r\n", + " END CATCH" + ], + "metadata": { + "azdata_cell_guid": "563e751f-df78-4f23-8618-9e7896316b81", + "language": "sql" + }, + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "source": [ + "## ![Database Administrator](../../../../../media/features/ledger/Pieter.jpg \"Database Administrator\") Let's try to modify the bet" + ], + "metadata": { + "azdata_cell_guid": "d268acd2-44be-4297-801f-37c00205da49" + }, + "attachments": {} + }, + { + "cell_type": "markdown", + "source": [ + "Pieter, the malicious DBA tries to manipulate his bet but noticed that it’s an append-only ledger table and that data cannot be modified. " + ], + "metadata": { + "azdata_cell_guid": "54f5b25f-bb4a-4da0-9cc7-52d12d7994d8" + }, + "attachments": {} + }, + { + "cell_type": "code", + "source": [ + "USE WorldCup\r\n", + "GO\r\n", + "SELECT * from Bets\r\n", + "WHERE FirstName='Pieter' and Lastname='Vanhove'" + ], + "metadata": { + "azdata_cell_guid": "faba49e8-5231-4f89-b152-abc391ce2a56", + "tags": [], + "language": "sql" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": "Commands completed successfully." + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": "(1 row affected)" + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": "Total execution time: 00:00:00.144" + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "metadata": {}, + "execution_count": 1, + "data": { + "application/vnd.dataresource+json": { + "schema": { + "fields": [ + { + "name": "BetID" + }, + { + "name": "MoneylineID" + }, + { + "name": "FirstName" + }, + { + "name": "LastName" + }, + { + "name": "Country" + }, + { + "name": "Bet" + }, + { + "name": "Payout" + }, + { + "name": "BetDateTime" + } + ] + }, + "data": [ + { + "0": "6", + "1": "12", + "2": "Pieter", + "3": "Vanhove", + "4": "Belgium", + "5": "300.00", + "6": "-1400.00", + "7": "2022-12-01 13:21:28.5000000" + } + ] + }, + "text/html": [ + "", + "", + "", + "
BetIDMoneylineIDFirstNameLastNameCountryBetPayoutBetDateTime
612PieterVanhoveBelgium300.00-1400.002022-12-01 13:21:28.5000000
" + ] + } + } + ], + "execution_count": 1 + }, + { + "cell_type": "code", + "source": [ + "UPDATE Bets\r\n", + "SET Payout=-10000\r\n", + "WHERE BetID=6" + ], + "metadata": { + "azdata_cell_guid": "e082ee2f-ec59-4525-9aae-39b91034686f", + "language": "sql" + }, + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "source": [ + "## ![Database Administrator](../../../../../media/features/ledger/Pieter.jpg \"Database Administrator\") Let's tamper the data" + ], + "metadata": { + "azdata_cell_guid": "239cf8e5-7d04-4427-a8bd-6ce35ef257de" + }, + "attachments": {} + }, + { + "cell_type": "markdown", + "source": [ + "Pieter thinks he's smart and tampers with the data directly into the data file by using a stored procedure." + ], + "metadata": { + "azdata_cell_guid": "703c9df6-e5c0-49e1-b841-522ebd5a088d" + }, + "attachments": {} + }, + { + "cell_type": "code", + "source": [ + "EXECUTE sp_TamperWithBet\r\n", + "\t@ID=6,\r\n", + "\t@PayOut=-10000" + ], + "metadata": { + "azdata_cell_guid": "4963b815-b14e-42b7-a0ee-59bbb40e98d8", + "language": "sql", + "tags": [] + }, + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "source": [ + "## ![Auditor](../../../../../media/features/ledger/Michael.jpg \"Auditor\") Verify the database again" + ], + "metadata": { + "azdata_cell_guid": "605d7ea8-0db7-41e4-9fc5-1a5b418b150e" + }, + "attachments": {} + }, + { + "cell_type": "markdown", + "source": [ + "Michael, reviews the Worldcup database again after the tournament is over. " + ], + "metadata": { + "azdata_cell_guid": "bb181bd0-d2bb-4d17-813b-b2d2912175df" + }, + "attachments": {} + }, + { + "cell_type": "code", + "source": [ + "DECLARE @digest_locations NVARCHAR(MAX) = (SELECT * FROM sys.database_ledger_digest_locations FOR JSON AUTO, INCLUDE_NULL_VALUES);\r\n", + " \r\n", + " BEGIN TRY\r\n", + " EXEC sys.sp_verify_database_ledger_from_digest_storage @digest_locations;\r\n", + " SELECT 'Ledger verification succeeded.' AS Result;\r\n", + " END TRY\r\n", + " BEGIN CATCH\r\n", + " THROW;\r\n", + " END CATCH" + ], + "metadata": { + "azdata_cell_guid": "3020ef07-2e35-4a9f-abe4-afb3a55e5539", + "language": "sql" + }, + "outputs": [ + { + "output_type": "error", + "evalue": "Msg 37371, Level 16, State 1, Procedure sys.sp_verify_database_ledger_from_digest_storage, Line 1\r\nThe computed hash from 'Bets' and the associated history table does not match the hash persisted in sys.database_ledger_transactions for transaction 1466.", + "ename": "", + "traceback": [] + }, + { + "output_type": "error", + "evalue": "Msg 37392, Level 16, State 1, Procedure sys.sp_verify_database_ledger_from_digest_storage, Line 1\r\nLedger verification failed.", + "ename": "", + "traceback": [] + }, + { + "output_type": "display_data", + "data": { + "text/html": "Total execution time: 00:00:00.474" + }, + "metadata": {} + } + ], + "execution_count": 4 + }, + { + "cell_type": "markdown", + "source": [ + "## Review the history of the transaction" + ], + "metadata": { + "azdata_cell_guid": "8a5a03e1-2306-41ec-be89-bc3065d65d30" + }, + "attachments": {} + }, + { + "cell_type": "code", + "source": [ + "SELECT\r\n", + " t.[commit_time] AS [CommitTime] \r\n", + "\t, t.[principal_name] AS [UserName]\r\n", + " ,l.[MoneylineID]\r\n", + " ,l.[FirstName]\r\n", + " ,l.[LastName]\r\n", + " ,l.[Country]\r\n", + " ,l.[Bet]\r\n", + " ,l.[Payout]\r\n", + " ,l.[BetDateTime]\r\n", + "\t, l.[ledger_operation_type_desc] AS Operation\r\n", + "\tFROM [dbo].[Bets_Ledger] l\r\n", + "\tJOIN [sys].[database_ledger_transactions] t\r\n", + "\tON t.[transaction_id] = l.[ledger_transaction_id]\r\n", + "\tWHERE t.transaction_id=1466;" + ], + "metadata": { + "azdata_cell_guid": "d3bf1ca4-c66f-4ba8-a863-0811e120de51", + "language": "sql" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/html": "(1 row affected)" + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/html": "Total execution time: 00:00:00.033" + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "metadata": {}, + "execution_count": 5, + "data": { + "application/vnd.dataresource+json": { + "schema": { + "fields": [ + { + "name": "CommitTime" + }, + { + "name": "UserName" + }, + { + "name": "MoneylineID" + }, + { + "name": "FirstName" + }, + { + "name": "LastName" + }, + { + "name": "Country" + }, + { + "name": "Bet" + }, + { + "name": "Payout" + }, + { + "name": "BetDateTime" + }, + { + "name": "Operation" + } + ] + }, + "data": [ + { + "0": "2022-12-01 13:21:28.5033333", + "1": "EUROPE\\pivanho", + "2": "12", + "3": "Pieter", + "4": "Vanhove", + "5": "Belgium", + "6": "300.00", + "7": "-10000.00", + "8": "2022-12-01 13:21:28.5000000", + "9": "INSERT" + } + ] + }, + "text/html": [ + "", + "", + "", + "
CommitTimeUserNameMoneylineIDFirstNameLastNameCountryBetPayoutBetDateTimeOperation
2022-12-01 13:21:28.5033333EUROPE\\pivanho12PieterVanhoveBelgium300.00-10000.002022-12-01 13:21:28.5000000INSERT
" + ] + } + } + ], + "execution_count": 5 + } + ] +} \ No newline at end of file