| Криптиране в Oracle Database 10g - част II |
| Неделя, 24 Септември 2006 08:15 | ||||||||||||||||||||||||||||
|
За целите на криптирането в Oracle Database 10g съществува отделен PL/SQL пакет, който се нарича DBMS_CRYPTO. Освен възможността да криптираме и декриптираме стандартни типове данни, чрез този пакет можем да използваме LOB полета (изображения, звук и т.н.). Друга изключително важна възможност е вградената поддръжка за криптиране и декриптиране на данни в бази с различен character set. В базата присъства и пакет наречен DBMS_OBFUSCATION_TOOLKIT, който също предлага ограничени възможности за криптиране, но той присъства заради целите на обратната съвместимост и по-добре да не го използваме. Нека обобщим достъпните криптографски подходи чрез DBMS_CRYPTO:
Нека сега напишем малко код, с който да илюстрираме как по програмен път можем да криптираме и декриптираме едно съобщение чрез средствата на DBMS_CRYPTO пакета. Криптиране чрез DBMS_CRYPTO За да криптираме дадена информация, трябва предварително да вземем някои решения за алгоритъма, режима на използване и разбира се – ключа. В DBMS_CRYPTO са дефинирани следните константи, от които можем да направим нужните ни комбинации за алгоритъм, режим, уплътняване, хеш и MAC алгоритми:
Съгласно горната таблица можем или да използваме някоя от готовите комбинации съдържащи шифър, режим и метод на уплътнение или да си конструираме наш собствен вариант, като подберем подходящите константи. След като сме решили каква комбинация ще използваме извикваме функцията DBMS_CRYPTO.ENCRYPT. Тя приема като параметър открития текст, комбинацията за криптиране, ключа и като незадължителен параметър – инициализиращ вектор. Също така трябва да имаме предвид, че DBMS_CRYPTO не поддържа VARCHAR2 типа. Вместо това функциите и процедурите в пакета очакват RAW данни, предварително конвертирани до AL32UTF8 character set. Следният примерен скрипт илюстрира използването на DBMS_CRYPTO за криптиране и декриптиране на просто съобщение: /* * ORACLE ENCRYPTION * * ENCRYPT.SQL * * (C) 2006 Nikolay Manchev. Some rights reserved. * Licensed under a Creative Commons Attribution License. * */ SET SERVEROUTPUT ON DECLARE input_string VARCHAR2(128) := 'A secret''s worth depends on the people from whom it must be kept.'; key_string VARCHAR2(16) := 'gx17TTA6oViv52nJ'; input_raw RAW(128) := UTL_I18N.STRING_TO_RAW(input_string, 'AL32UTF8'); key_raw RAW(16) := UTL_I18N.STRING_TO_RAW(key_string, 'AL32UTF8'); encryption_mode NUMBER; encrypted_raw RAW(2048); decrypted_raw RAW(2048); decrypted_string VARCHAR2(2048); BEGIN dbms_output.put_line('Input string: '||input_string); dbms_output.put_line('Key string : '||key_string); dbms_output.put_line(chr(13)); dbms_output.put_line('**** ENCRYPTION ****'); encryption_mode := DBMS_CRYPTO.ENCRYPT_AES128 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5; encrypted_raw := DBMS_CRYPTO.ENCRYPT(input_raw, encryption_mode, key_raw); dbms_output.put_line('Encrypted : '||encrypted_raw); dbms_output.put_line(chr(13)); dbms_output.put_line('**** DECRYPTION ****'); decrypted_raw := DBMS_CRYPTO.DECRYPT(encrypted_raw, encryption_mode, key_raw); decrypted_string := UTL_I18N.RAW_TO_CHAR(decrypted_raw, decrypted_string); dbms_output.put_line('Decrypted : '||decrypted_string); END; / Да разгледаме примерния код. Откритият текст записваме в променливата input_string. Използваме произволна комбинация от букви и цифри за ключ, която записваме в променливата key_string. В последствие конвертираме стойностите и на двете променливи до RAW тип, тъй като това са очакванията на ENCRYPT функцията (тя приема входящото съобщение като RAW, BLOB или CLOB, а ключът винаги като RAW) и ги записваме съответно в променливи input_raw и key_raw. Декларираме и няколко допълнителни променливи, които ще използваме в последствие. На променливата encryption_mode присвояваме стойност от сумата на типа криптиране, режима и метода на уплътнение. В случая използваме 128 битово AES криптиране, свързването между блоковете е верижно, а уплътняването им съгласно PKCS#5. Извикваме функцията ENCRYPT, като й предаваме текста, начина на криптиране и ключа, а резултатът от изпълнението й записваме в променливата encrypted_raw. След като отпечатваме криптираното съобщение извършваме декриптиране чрез функцията DECRYPT, очаквайки да получим идентично съобщение с открития текст. Резултатът от изпълнението изглежда така: Input string: A secret's worth depends on the people from whom it must be kept. Key string : gx17TTA6oViv52nJ **** ENCRYPTION **** Encrypted : 1972B3A95E148313EA7EA5513FAFDEA741BD0705796B58CCF6CC4A2BB2B7AD4779EAA35BCFAB8211 526DBE92E87713D3F92C85BD5A8BAB48FAD915E6F3623EF29297995689E7EAF6D468988C1163B043 **** DECRYPTION **** Decrypted : A secret's worth depends on the people from whom it must be kept. PL/SQL procedure successfully completed. SQL> Тъй като функцията ENCRYPT връща данните в RAW формат добре би било да ги конвертираме например чрез RAWTOHEX или UTL_ENCODE.BASE64_ENCODE за да ги приведем във вид удобен за съхранение във VARCHAR2. Използване на хеш функции За изчисляване на хеш стойности в DBMS_CRYPTO е включена функция HASH. Тя приема входящи данни от тип RAW, CLOB или BLOB. Вторият параметър на HASH е типът на хеширащата функция, който се задава чрез вече описаните константи дефинирани в DBMS_CRYPTO. Функцията връща RAW стойност, която представя изчислената хеш стойност. Следният скрипт илюстрира използването на HASH функцията: /* * ORACLE ENCRYPTION * * HASH.SQL * * (C) 2006 Nikolay Manchev. Some rights reserved. * Licensed under a Creative Commons Attribution License. * */ SET SERVEROUTPUT ON DECLARE input_string VARCHAR2(128) := 'Learning to ignore things is one of the great paths to inner peace.'; input_raw RAW(128) := utl_i18n.string_to_raw(input_string, 'AL32UTF8'); BEGIN dbms_output.put_line(chr(13)); dbms_output.put_line('Message: '||input_string); dbms_output.put_line('MD4 hash : '||dbms_crypto.hash(input_raw,dbms_crypto.hash_md4)); dbms_output.put_line('MD5 hash : '||dbms_crypto.hash(input_raw,dbms_crypto.hash_md5)); dbms_output.put_line('SHA1 hash : '||dbms_crypto.hash(input_raw,dbms_crypto.hash_sh1)); input_string := 'Learning to ignore things is one of the Great paths to inner peace.'; input_raw := utl_i18n.string_to_raw(input_string, 'AL32UTF8'); dbms_output.put_line(chr(13)); dbms_output.put_line('Message: '||input_string); dbms_output.put_line('MD4 hash : '||dbms_crypto.hash(input_raw,dbms_crypto.hash_md4)); dbms_output.put_line('MD5 hash : '||dbms_crypto.hash(input_raw,dbms_crypto.hash_md5)); dbms_output.put_line('SHA1 hash : '||dbms_crypto.hash(input_raw,dbms_crypto.hash_sh1)); END; / Като използваме три различни хеш алгоритъма това, което ще видим след изпълнението ще бъдат трите различни хеш стойности. В допълнение променяме само една единствена буква в съобщението ("g" в "great" става "G"), което води до корено различни стойности и при трите алгоритъма при тяхното преизчисление. Ето и самия резултат: Message: Learning to ignore things is one of the great paths to inner peace. MD4 hash : 2123E4E71E22EC2AD4F2956D1EEB0374 MD5 hash : C516D55D81018969849A815C64123BDB SHA1 hash : 4E88E0A340BBF0E02F41A5E72D107F67A8386EA3 Message: Learning to ignore things is one of the Great paths to inner peace. MD4 hash : 95932413D237986264C0486817B86D67 MD5 hash : 04AEAFDFD51999759F2F616B26E49CBC SHA1 hash : F891F94883756DEE86AC7E859150537780F36E1F PL/SQL procedure successfully completed. SQL> Като цяло препоръката на Oracle е да се използва SHA1. Също така и тук трябва да помислим за конвертиране на резултата например в Base64 преди неговото съхраняване. Използване на MAC Последният пример, който ще разгледаме е използването на функцията MAC, за изчисляване на код на автентичност на съобщението. Ще използваме произволно генериран ключ, който заедно със съобщението ще предадем на функцията. В последствие променяме първата буква на ключа в главна и изчисляваме отново MAC-а. Следният скрипт реализира описания сценарий: /* * ORACLE ENCRYPTION * * MAC.SQL * * (C) 2006 Nikolay Manchev. Some rights reserved. * Licensed under a Creative Commons Attribution License. * */ SET SERVEROUTPUT ON DECLARE input_string VARCHAR2(128) := 'The opposite of an archeologist is a dog'; key_string VARCHAR2(16) := 'b7b20afaXd11fGP'; input_raw RAW(128) := utl_i18n.string_to_raw(input_string,'AL32UTF8'); key_raw RAW(16) := utl_i18n.string_to_raw(key_string,'AL32UTF8'); BEGIN dbms_output.put_line(chr(13)); dbms_output.put_line('Message: '||input_string); dbms_output.put_line('Key : '||key_string); dbms_output.put_line('MAC : '||dbms_crypto.mac(input_raw, dbms_crypto.hmac_sh1, key_raw)); key_string := 'B7b20afaXd11fGP'; key_raw := utl_i18n.string_to_raw(key_string,'AL32UTF8'); dbms_output.put_line(chr(13)); dbms_output.put_line('Message: '||input_string); dbms_output.put_line('Key : '||key_string); dbms_output.put_line('MAC : '||dbms_crypto.mac(input_raw, dbms_crypto.hmac_sh1, key_raw)); END; / И съответно резултатът от изпълнението му: Message: The opposite of an archeologist is a dog Key : b7b20afaXd11fGP MAC : 284B62D2A1B487DB29AD86E79478C305CD30F96E Message: The opposite of an archeologist is a dog Key : B7b20afaXd11fGP MAC : 89078C3D3A05BC84E0436E4548E2A39BC8BF07C4 PL/SQL procedure successfully completed. SQL> В горния пример използваме SHA-1 при изчисляването на кода за автентичност, който като цяло е препоръчван пред MD5. Управление на ключа и защитни стратегии Генериране на сигурни ключове В примерите, които разгледахме до момента се използваше статичен ключ, за който предположихме, че е генериран напълно случайно. До колко сигурно сме шифровали дадено съобщение зависи най-вече от това колко надежден ключ използваме с избрания алгоритъм. Пакетът DBMS_CRYPTO предоставя функция наречена RANDOMBYTES, която генерира достатъчно сигурни ключове. Тя се използва съвсем лесно – просто трябва да й кажем колко дълъг ключ искаме да генерира: /* * ORACLE ENCRYPTION * * KEY_GEN.SQL * * (C) 2006 Nikolay Manchev. Some rights reserved. * Licensed under a Creative Commons Attribution License. * */ SET SERVEROUTPUT ON DECLARE key_length NUMBER := 16; key RAW(16); BEGIN key := dbms_crypto.randombytes(key_length); dbms_output.put_line('Key : '||key); END; / Въпреки, че като цяло компютърно генерираният ключ винаги е псевдо-случаен, генераторът използван от RANDOMBYTES е считан за достатъчно надежден и е сертифициран от RSA. Съхраняване на ключа и други защитни стратегии Съхраняването на ключовете е може би най-отговорната задача от процеса по шифриране на данни. Ако неоторизиран потребител получи достъп до ключа, то всички наши усилия за защита на данните стават напразни. Първото и може би най-логично, но определено не най-сигурно място за съхраняване на ключовете е операционната система, където е инсталирана базата. Тъй като PL/SQL принципно може да прави външни извиквания, съвсем лесно можем да реализираме схема с външен файл съдържащ ключ. Разбира се, имайки предвид че повечето пробиви в сигурността стават именно на ниво операционна система, съхраняването на ключовете в лесен за достъп файл направо се равнява на подарък за нарушителя. По-логично място за съхранение е самата Oracle базата. Можем да съхраняваме ключовете в отделна таблица (като цяло лоша идея), до която имат достъп само определени потребители, а можем също така и да оставим фиксиран ключ в самите PL/SQL процедури които се грижат за криптирането и декриптирането на данните. Ако изберем втория вариант, задължително трябва да "скрием" кода на процедурата, така че ключът да не може да се чете в свободен вид. Това можем да направим, като използваме wrap инструмента, който е част от Oracle Database 10g. Това, което той прави е от чистият текст да генерира не четим байт код. По този начин никой разработчик, администратор или потребител имащ достъп до базата не може да види изходния код на процедурата, съдържаща ключа. За да "разбъркаме" дадена процедура по този начин е достатъчно да изпълним на командния ред: $ wrap iname=име_на_файла.sql Въпреки, че по този начин съдържанието на файла няма да е четимо, то ще продължава коректно да се изпълнява. Разбира се, ще загубим възможността да го променяме, така че е добре да си запазим копие извън машината (например на външен носител с надлежно контролиран достъп). Разбира се, не трябва да забравяме, че никой не гарантира нивото на сигурност, което wrap дава. Колкото по-далеч съхраняваме ключа от самите данни, толкова по-добре. Възможно е да приложим стратегия, при която самите потребители управляват ключовете на данните си. Имайки предвид обаче, че много от потребителите използват меко казано неадекватни практики що се касае до сигурността, към тази стратегия трябва да се подхожда изключително внимателно. Още повече, повечето експерти по сигурността посочват периодичната промяна на криптиращите ключове като добра практика (с други думи да декриптираме и отново да криптираме данните но с нов ключ, прилагайки процедурата през определен период от време). Дистрибуцията и съхранението на новите ключове от потребителите също е рискова операция, имайки предвид навиците за записване на пароли на хвърчащи листчета. Може би най-лесният и сигурен начин е използването на Transparent Data Encryption (или TDE), това е възможност, която присъства в Oracle Database 10g Release 2 и автоматично управлява не само процесите по криптиране и декриптиране, но и съхранението на ключовете. Тъй като тази система сама по себе си е достатъчно сложна, ще я разгледаме подробно по-нататък. След като разгледахме няколко възможни варианта, може би все пак трябва да помислим за някаква обща препоръка за стратегия спрямо ключовете. Може би един от най-добрите варианти, които максимално ще затруднят всеки нарушител включва по малко от всички разгледани варианти, а именно: 1.не използваме един единствен ключ – вместо това за всеки запис от таблицата генерираме отделен ключ; 2.не криптираме данните с ключовете в таблицата, а вместо това за всеки ред правим комбинация от един главен (и таен) ключ и специално генерираният за реда ключ (например чрез "изключващо или" - XOR) 3.главният ключ съхраняваме извън системата и го предаваме като параметър на криптиращите и декриптиращи процедури. в този случай трябва да сме сигурни че никой няма да "подслуша" предаването на параметъра. Използвайки подобна стратегия можем да сме сигурни, че за декриптирането на данните един нарушител трябва да се добере и до двата ключа едновременно, което ще го затрудни допълнително. Ако желаем, като допълнителна мярка можем да си "поиграем" с Oracle Label Security спрямо колоната в таблицата съдържащ уникалните ключове за записите. Изобщо – вариантите са много. Въпросът е да открием правилния баланс между високата надеждност и лекотата за работа с данните и обслужването на системата. Заключение Oracle Database 10g предлага изключителни добри възможности за защита на данните. Това, за което трябва да се погрижим е да ги използваме правилно. Също така, заслужава си да погледнем и малко по-далече от стандартните възможности за криптиране. Oracle предлага допълнителна опция към базата, наречена Oracle Advanced Security, която включва възможности за криптиране на мрежово ниво (при преноса на данни между различни сървъри и между клиент и сървър), спомената вече Transparent Data Encryption, както и Strong Authentication системата, чрез която автентикацията на потребителите се извършва не само на базата на пароли, а и чрез смарт карти, сертификати и други. |


Коментари