Truncation of strings and it's impact on column level encryption (original version here: http://informix-technology.blogspot.com/2016/12/string-truncate-column-level-encryption.html)
English version
A recent costumer engagement re-activated a dormant issue on my mind... I've already mentioned this is posts, answers in IIUG mailing list, internal and external chats and discussions... Personally I think this is one of the top annoying things in Informix. I'm referring to the fact that on a non-ANSI informix database we truncate a string on insert if it's length exceeds the length of the field. And we do this silently... no error.
There are two RFEs opened for this (appropriately defined as duplicates). The original one is 33830 and the other (duplicate) is 53804. I've seen several reasons for not implementing this, which I'd like to oppose (again):
- It's stated in the ANSI standard that it should work like this
Although the ANSI standard is hard to read, there are some paragraphs that seem to suggest this. But even if it's clearly stated there, it isn't what people want. This can corrupt data. Anybody would prefer an error.
Additionally we don't truncate on ANSI mode databases.
- We would be changing previous behavior
True. But that could be an option and by default we could keep the old behavior. The author of the RFE suggests a new parameter in $ONCONFIG. That is an option, but I'd prefer also an option on CREATE DATABASE (we already introduced an option for NLSCASE SENSITIVE). Ideally we would have a new ALTER DATABASE to change it. The $ONCONFIG parameter could and should be used to set the default (if the option was not specified on the CREATE DATABASE statement).
I would not create a new $ONCONFIG parameter. I'd prefer having more options on the EILSEQ_COMPAT_MODE parameter which already controls functionality around the same topic.
- It would be hard to implement
Having basic programming knowledge and considering we don't do this for ANSI mode databases (see test below), I doubt this would be too hard to implement. Somehow I can imagine a piece of current code like
if ( ANSI_MODE_FLAG && length(input) > col_length)
raise_exception(1279);
which would become:
if ((ANSI_MODE_FLAG || AVOID_TRUNCATE_FLAG) && length(input) > col_length)
raise_exception(1279);
ANSI_MODE_FLAG and AVOID_TRUNCATE_FLAG are assumed to be flags set from the logging mode and from the database (or session) options
- That's not a database problem. The application must check it's inputs
Although I can agree with the idea that applications should check the inputs, I know many of them don't check the length. And as we don't silently truncate a big number (100000 for example) when inserted into a SMALLINT column, I can't understand why we do it with strings. The database must help keeping data integrity. And it fails doing that for strings
As mentioned before we don't truncate strings on ANSI mode database. Here's what happens:
castelo@primary:informix-> dbaccess -e stores_ansi test_ansi_truncate.sql
Database selected.
DROP TABLE IF EXISTS test;
Table dropped.
CREATE TABLE test
(
col1 CHAR(5)
);
Table created.
INSERT INTO test VALUES('123456');
1279: Value exceeds string column length.
Error in line 7
Near character position 32
377: Must terminate transaction before closing database.
853: Current transaction has been rolled back due to error
or missing COMMIT WORK.
castelo@primary:informix->
Why don't we simply use ANSI mode databases? Well... They have other limitations (can't use datablades) and conversion of existing ones would require application changes.
Ok... so string truncation is a problem by itself. But this article is about a specially complex and nasty effect of string truncation.
Users looking into column level encryption will notice that the encrypted form of the data will be stored in a character column. And the encryption process will "enlarge" the length of the data. Even more complex, the length of encrypted data depends on various factors. The initial number of "characters" (or digits) is one. Others are if it uses hint or not and the encryption algorithm. Below is a table from the manual that maps the "N" original digits/characters to the final result depending on the algorithm and usage of the hint:
N
|
ENCRYPT_TDES
No Hint
|
ENCRYPT_AES
No Hint
|
ENCRYPT_TDES
With Hint
|
ENCRYPT_AES
With Hint
|
1 to 7
|
35
|
43
|
87
|
99
|
8 to 15
|
43
|
43
|
99
|
99
|
16 to 23
|
55
|
67
|
107
|
119
|
24 to 31
|
67
|
67
|
119
|
119
|
32 to 39
|
75
|
87
|
131
|
139
|
40 to 47
|
87
|
87
|
139
|
139
|
100
|
163
|
171
|
215
|
227
|
200
|
299
|
299
|
355
|
355
|
500
|
695
|
707
|
747
|
759
|
The previous values can be calculated using the following formulae (again, accordingly to the manual):
- Encryption by ENCRYPT_TDES( ) with no hint:
Encrypted size = (4 x ((8 x((N + 8)/8) + 10)/3) + 11)
- Encryption by ENCRYPT_AES( ) with no hint:
Encrypted size = (4 x ((16 x((N + 16)/16) + 10)/3) + 11)
- Encryption by ENCRYPT_TDES( ) with a hint:
Encrypted size = (4 x ((8 x((N + 8)/8) + 50)/3) + 11)
- Encryption by ENCRYPT_AES( ) with a hint:
Encrypted size = (4 x ((16 x((N + 16)/16) + 50)/3) + 11)
The integer division ( / ) returns an integer quotient and discards any remainder.
So, effectively, using the above calculations it should be possible to validate if the value inserted by the user would fit a specific column length. But that would have to be implemented for each column. Could introduce some errors, and would make it impossible to implement column level encryption without changing the application, which is otherwise almost possible (the only change is to provide a password at the session level, if that fits the use case).
And after all this discussion, what is the problem in truncating the strings in this context? Pretty simply it will make it impossible to decrypt the data, so effectively it corrupts data in an unrecoverable way. There's no way to overstate this. An application, application developer or a DBA cannot afford to have that risk. As I usually say when I'm discussing this, if we loose a few characters of a name, email address, or street address, it's possible that a human can recover the missing data by looking at what is left. But with column level encryption even the loss of a single character means the data is lost. Not something we want to deal with, or have to explain to business managers.
What can we do? We could start by opening a bunch of PMRs and link them to the RFEs above... but apart from pressing IBM there's actually something very easy we can do to solve this for the very strict context of column level encryption.
As mentioned, recently I had a customer who is concerned with the upcoming EU data protection regulations and is considering their options to address the requirements in that new compliance challenge. One of the options is to use column level encryption, but they were highly concerned with the above scenario. So I gave this matter a considerable thought and I think I came up with a relatively reasonable workaround (although I hate to be forced to deal with this in the first place).
The solution is elegant, relatively lightweight, doesn't require any extra application changes and hopefully should be able to avoid the nasty hypotheses of data loss or corruption.
My first approach was to implement a trigger, and try to verify if the input data would fit the column. I quickly realized that the data as seen by the trigger is already truncated, so the approach would not work. But at that same moment it became clear that another approach would work: If the data is already truncated (or not, depending on the sizes of course), all I had to do was try to decrypt it. If the data was actually truncated, it would raise an error. And if I try that within a trigger, the triggering operation will naturally fail, which if the intended outcome. You can check the code at the end of the article.
Trying to decrypt the data would have the following problems:
- It would be expensive in terms of CPU resources
- If the data has been encrypted with a specific password passed to the encrypt functions (ENCRYPT_AES() or ENCRYPT_TDES() ), I would not be able to DECRYPT_CHAR(), without knowing the password
A solution to both of this problems is to use the GETHINT() function. It apparently is less expensive than DECRYPT_CHAR and at the same time doesn't require the password. So it will work in either cases (password at session level or password at function level). It will also "work" even if no hint was provided. Most importantly it will fail if data truncation had happened.
So basically I need to create a procedure that receives the encrypted data, apply the GETHINT() and let the magic happen. This procedure should be called from triggers set on the column/table (INSERT and UPDATE) that pass the "new" values as parameter. I choose to use LVARCHAR(32000) as it should cover most use cases. Smart blob encryption was not considered. In those cases we don't have a size limit...
The code is shown at the end of the article. Let's see what happens when we run it:
bica@primary:fnunes-> dbaccess -e stores test_truncate.sql
Database selected.
DROP TABLE IF EXISTS test_encrypt;
Table dropped.
CREATE TABLE test_encrypt
(
col1 SERIAL,
col2 CHAR(43)
);
Table created.
SET ENCRYPTION PASSWORD 'blog_password';
Encryption password set.
DROP PROCEDURE IF EXISTS check_encryption;
Routine dropped.
CREATE PROCEDURE check_encryption(str LVARCHAR(32000))
DEFINE dummy CHAR(32);;
SELECT GETHINT(str) INTO dummy FROM sysmaster:sysdual;;
END PROCEDURE;
Routine created.
;
CREATE TRIGGER i_test_encrypt INSERT ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.
CREATE TRIGGER u_test_encrypt UPDATE OF col2 ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 1'));
1 row(s) inserted.
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 2'));
1 row(s) inserted.
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('This longer row will probably not fit!'));
26005: The encrypted data is wrong or corrupted.
Error in line 32
Near character position 1
UPDATE test_encrypt SET col2 = ENCRYPT_AES('This longer row will probably not fit!') WHERE col1 = 1;
26005: The encrypted data is wrong or corrupted.
Error in line 34
Near character position 1
UPDATE test_encrypt SET col2 = ENCRYPT_AES('This fits!') WHERE col1 = 2;
1 row(s) updated.
Database closed.
bica@primary:fnunes->
So, as can be seen, I created a table with a column (col2) defined as CHAR(43) which won't be enough for some data I'll try to INSERT/UPDATE.
Then I setup the session encryption password. I create the procedure and the triggers on the table.
And I move on to the DML. The first two INSERTs use short values and they work. The third INSERT uses a longer string which encrypted version won't fit col2. It raises an error 26005. Then I try a similar string but within an UPDATE and I face the same error. And an update with short length works.
I believe this is a simple, clean and robust solution for this problem. But as usual, use it at your own risk. And don't forget this is a workaround for a specific scenario (column level encryption) to overcome a problem I believe should not exist in the first place.
Versão Portuguesa
Uma
visita recente a um cliente reavivou um assunto que andava adormecido
na minha mente... Já mencionei isto em artigos, respostas na lista de
discussão do IIUG, conversas e discussões internas e externas...
Pessoalmente penso que é uma das coisas mais irritantes no Informix.
Refiro-me ao facto de que numa base de dados não-ANSI, o Informix corta
strings na inserção e alteração se o comprimento das mesmas exceder o tamanho da coluna. E tal acontece de forma silenciosa. Sem erro.
Existem dois RFEs abertos para isto (apropriadamente detetados como duplicados). O original é o
33830 e o outro (duplicado) é o
53804. Já me foram sugeridas várias razões para a não implementação disto, as quais uma vez mais gostaria de refutar:
- É referido que o standard ANSI sugere que este é o comportamento correto
Embora o standard
ANSI seja difícil de ler, existem alguns parágrafos que parecem sugerir
isto. Mas mesmo que fosse claro, existem contra-argumentos para isto.
Ainda que seja o standard, não é o que as pessoas querem. Isto
pode corromper dados. Qualquer pessoa preferirá um erro. Adicionalmente e
paradoxalmente nós não fazemos o corte em bases de dados criadas em
modo ANSI
- Estaria a mudar-se o comportamento anterior
Verdade. Mas isto poderia ser uma opção e por omissão manter-se o
comportamento antigo. O autor do RFE sugere um parâmetro no $ONCONFIG.
Seria uma opção, mas eu preferiria também uma opção no CREATE DATABASE
(já introduzimos uma opção para NLSCASE SENDITIVE). Idealmente
deveríamos também ter um novo ALTER DATABASE para mudar a opção. O
parâmetro do $ONCONFIG poderia e deveria ser usado como a definição por
omissão (se a opção não fosse especificada no CREATE DATABASE).
Pessoalmente não criaria um novo parâmetro, mas antes daria novos
valores possíveis ao parâmetro EILSEQ_COMPAT_MODE, que já controla
funcionalidades em torno deste tópico
- Seria difícil de implementar
Tendo conhecimentos básicos de programação, e considerando que já hoje nós não cortamos as strings
em bases de dados criadas em modo ANSI (ver o teste abaixo), duvido que
isto fosse muito complexo de implementar. De alguma forma imagino um
pedaço de código atual semelhante a:
if ( ANSI_MODE_FLAG && length(input) > col_length)
raise_exception(1279);
que deveria tornar-se:
if ((ANSI_MODE_FLAG || AVOID_TRUNCATE_FLAG) && length(input) > col_length)
raise_exception(1279);
ANSI_MODE_FLAG é assumido que será uma flag dependente do mode de logging da base de dados e AVOID_TRUNCATE_FLAG seria dependente da opção de criação da base de dados ou do parâmetro no $ONCONFIG
- Isto não é um problema de base de dados. A aplicação tem de validar os seus inputs
Embora
tenha de concordar com a ideia que as aplicações devem validar os seus
inputs, sei que muitas delas não verificam o comprimento das strings
ou fazem-no pelas definições de estruturas de dados. E se não cortamos
de forma silenciosa um número grande (como 100000) ao tentar inserir num
SMALLINT, não consigo entender porque o fazemos com strings. A
base de dados tem como função essencial assegurar a integridade dos
dados, de acordo com as definições dos mesmos. E falha quando se trata
de o fazer em strings.
Como mencionado acima, nós não cortamos
strings em bases de dados criadas em modo ANSI. Vejamos o que acontece:
castelo@primary:informix-> dbaccess -e stores_ansi test_ansi_truncate.sql
Database selected.
DROP TABLE IF EXISTS teste;
Table dropped.
CREATE TABLE teste
(
col1 CHAR(5)
);
Table created.
INSERT INTO teste VALUES('123456');
1279: Value exceeds string column length.
Error in line 7
Near character position 32
377: Must terminate transaction before closing database.
853: Current transaction has been rolled back due to error
or missing COMMIT WORK.
castelo@primary:informix->
Porque não passamos simplesmente a
usar bases de dados em modo ANSI? Bem.... têm outras limitações (não
podem usar datablades) e isso requereria mudanças no código das
aplicações.
Ok... Então o corte de
strings é um problema em si mesmo. Mas este artigo é sobre um efeito complexo e devastador desse corte.
Utilizadores
que estejam a considerar o uso de encriptação de colunas, verificarão
que a forma encriptada dos dados deverá ser guardada numa coluna com um
tipo de dados alfa-numéricos (CHAR ou uma variante). E saberão também
que o processo de encriptação fará "crescer" o tamanho dos dados. Ainda
mais complicado, o tamanho dos dados encriptados dependerá de vários
fatores: O número de caracteres (alfa-numéricos) originais é um desses
fatores. Outros serão se se usa ou não uma
hint, e o algoritmo de
encriptação. Abaixo está uma tabela retirada do manual que mapeia o
número original de dígitos / caracteres com o tamanho final, dependendo
do algoritmo usado e da utilização ou não de
hint:
N
|
ENCRYPT_TDES
No Hint
|
ENCRYPT_AES
No Hint
|
ENCRYPT_TDES
With Hint
|
ENCRYPT_AES
With Hint
|
1 to 7
|
35
|
43
|
87
|
99
|
8 to 15
|
43
|
43
|
99
|
99
|
16 to 23
|
55
|
67
|
107
|
119
|
24 to 31
|
67
|
67
|
119
|
119
|
32 to 39
|
75
|
87
|
131
|
139
|
40 to 47
|
87
|
87
|
139
|
139
|
100
|
163
|
171
|
215
|
227
|
200
|
299
|
299
|
355
|
355
|
500
|
695
|
707
|
747
|
759
|
Os valores anteriores podem ser calculados com as seguintes fórmulas (de acordo com o manual):
- Encriptação com ENCRYPT_TDES( ) sem hint:
Tamanho dados encriptados = (4 x ((8 x((N + 8)/8) + 10)/3) + 11)
- Encriptação com ENCRYPT_AES( ) sem hint:
Tamanho dados encriptados = (4 x ((16 x((N + 16)/16) + 10)/3) + 11)
- Encriptação com ENCRYPT_TDES( ) com hint:
Tamanho dados encriptados = (4 x ((8 x((N + 8)/8) + 50)/3) + 11)
- Encriptação com ENCRYPT_AES( ) com hint:
Tamanho dados encriptados= (4 x ((16 x((N + 16)/16) + 50)/3) + 11)
A divisão inteira ( / ) retorna um quociente inteiro descartando qualquer resto.
Portanto,
efetivamente usando os cálculos acima deverá ser possível validar se um
valor inserido por um utilizador caberá numa determinada coluna depois
de encriptado. Mas isto teria de ser implementado para cada coluna.
Poderia introduzir alguns erros, e tornaria impossível implementar a
encriptação de colunas sem mudar a aplicação, o que de outra forma seria
praticamente possível (a única alteração necessária seria o
providenciar uma
password de encriptação ao nível de sessão, se tal for permitido pelo caso de uso).
E depois de toda esta discussão, qual é o problema em cortar
strings
neste contexto? Muito simplesmente, tornará impossível efetuar a
desencriptação, pelo que efetivamente tal situação corrompe dados de
forma irrecuperável. Não há forma de exagerar esta conclusão. Uma
aplicação, um programador ou um DBA não podem dar-se ao luxo de correr
esse risco. Como habitualmente digo quando estou a discutir este
assunto, se perdermos uns poucos caracteres de um nome, um endereço de
correio ou endereço postal, é possível que um ser humano possa recuperar
os dados cortados através da consulta do que sobrou. Mas com
encriptação de colunas, mesmo a perda de um só carater implica que os
dados estejam perdidos. Não é coisa com que queiramos lidar ou que
tenhamos de explicar a pessoas responsáveis pelo negócio.
Então o
que podemos fazer? Podíamos começar por abrir um rol de PMRs e ligá-los a
este RFE acima., mas para além de pressionarmos a IBM, existe algo que
efetivamente podemos fazer para resolver o contexto estrito de
encriptação de colunas.
Como mencionado, recentemente tive
contacto com um cliente que está preocupado com a nova regulação de
proteção de dados da EU e está a considerar as opções que têm neste novo
desafio de
compliance. Uma das opções que têm é usar encriptação
do nível da coluna, mas estavam altamente preocupados com o cenário
acima. Por causa disso dediquei um considerável esforço ao tema
e penso que cheguei a uma forma de contornar o problema (embora odeie
ter de lidar com tal situação).
A solução é elegante,
relativamente leve, não requer nenhuma modificação na aplicação e
espero que esteja à altura de evitar a terrível hipótese de perda de
dados ou corrupção.
A minha primeira abordagem foi implementar um
trigger e tentar verificar se os dados de input (encriptados) caberiam na coluna. Rapidamente me apercebi que o
trigger já vê os dados
"cortados", pelo que a abordagem não resultaria. Mas no mesmo momento
ficou claro que outra abordagem semelhante resultaria: se os dados já
estão potencialmente "cortados", tudo o que deveria fazer seria
desencriptá-los. Se tivessem sido efetivamente cortados isto
despoletaria um erro. E se tal acontecer dentro de um
trigger então a operação que despoletou o
trigger
iria por arrasto falhar. Pode verificar o código no final do artigo.
Tentar desencriptar os dados levantaria os seguintes problemas::
- Seria dispendioso em termos de recursos de CPU
- Se os dados tivessem sido encriptados com uma password
especifica passada às funções de encriptação (ENCRYPT_AES() or
ENCRYPT_TDES() ), não seria capaz de efetuar a desencriptação sem
saber a password
A
solução para ambos estes problemas é usar a função GETHINT(). Deverá
ser menos exigente em termos de CPU que a DECRYPT_CHAR() e tem a
vantagem de não necessitar de
password. Deverá portanto funcionar em ambos os casos (
passwords ao nível da sessão e ao nível das funções). Irá também funcionar mesmo que não tenha sido utilizada nenhuma
hint. Mas o verdadeiramente importante é que falhará se os dados tiverem sido corrompidos.
Assim,
basicamente o que necessito é criar um procedimento que recebe os dados
encriptados e aplica o GETHINT(), e deixar a magia acontecer. O
procedimento deve ser chamado por
triggers definidos na coluna / tabela (INSERT e UPDATE), que passam os "novos" valores como parâmetro
Escolhi
o LVARCHAR(32000) como tipo do parâmetros dado que deverá cobrir a
maioria dos casos. A encriptação de Smart Blobs não foi considerada. Até
porque nesses casos não temos um limite de tamanho...
O código está visível no final do artigo. Vejamos o que acontece quando o corremos:
bica@primary:fnunes-> dbaccess -e stores test_truncate.sql
Database selected.
DROP TABLE IF EXISTS test_encrypt;
Table dropped.
CREATE TABLE test_encrypt
(
col1 SERIAL,
col2 CHAR(43)
);
Table created.
SET ENCRYPTION PASSWORD 'blog_password';
Encryption password set.
DROP PROCEDURE IF EXISTS check_encryption;
Routine dropped.
CREATE PROCEDURE check_encryption(str LVARCHAR(32000))
DEFINE dummy CHAR(32);;
SELECT GETHINT(str) INTO dummy FROM sysmaster:sysdual;;
END PROCEDURE;
Routine created.
;
CREATE TRIGGER i_test_encrypt INSERT ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.
CREATE TRIGGER u_test_encrypt UPDATE OF col2 ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
Trigger created.
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 1'));
1 row(s) inserted.
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 2'));
1 row(s) inserted.
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('This longer row will probably not fit!'));
26005: The encrypted data is wrong or corrupted.
Error in line 32
Near character position 1
UPDATE test_encrypt SET col2 = ENCRYPT_AES('This longer row will probably not fit!') WHERE col1 = 1;
26005: The encrypted data is wrong or corrupted.
Error in line 34
Near character position 1
UPDATE test_encrypt SET col2 = ENCRYPT_AES('This fits!') WHERE col1 = 2;
1 row(s) updated.
Database closed.
bica@primary:fnunes->
Como se pode ver, crio uma tabela com uma
coluna (col2) definida como CHAR(43) e que não será suficiente para
alguns dados que irei tentar inserir/alterar.
Depois estabeleço a
password de sessão. Crio o procedimento e os
triggers na tabela. E passo ao DML. Os primeiros dois INSERTs usam valores curtos e vão funcionar. O terceiro INSERT usa uma
string mais longa, cuja versão encriptada não cabe na coluna col2. Despoleta um erro 26005. Depois tento uma
string semelhante mas via um UPDATE e encontramos o mesmo erro. Depois um UPDATE "curto" e funciona
Acredito
que isto é simples, "limpo" e robusto para resolver o problema. Mas
como é habitual, utilize por sua conta, peso e risco. E relembro que
isto é uma forma de contornar um problema que penso que nunca deveria
ter acontecido, num contexto específico que é a encriptação de colunas.
Code/Código
DROP TABLE IF EXISTS test_encrypt;
CREATE TABLE test_encrypt
(
col1 SERIAL,
col2 CHAR(43)
);
SET ENCRYPTION PASSWORD 'blog_password';
DROP PROCEDURE IF EXISTS check_encryption;
CREATE PROCEDURE check_encryption(str LVARCHAR(32000))
DEFINE dummy CHAR(32);
SELECT GETHINT(str) INTO dummy FROM sysmaster:sysdual;
END PROCEDURE;
CREATE TRIGGER i_test_encrypt INSERT ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
CREATE TRIGGER u_test_encrypt UPDATE OF col2 ON test_encrypt REFERENCING NEW AS new_data
FOR EACH ROW
(
EXECUTE PROCEDURE check_encryption(new_data.col2)
);
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 1'));
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('Row 2'));
INSERT INTO test_encrypt VALUES(0, ENCRYPT_AES('This longer row will probably not fit!'));
UPDATE test_encrypt SET col2 = ENCRYPT_AES('This longer row will probably not fit!') WHERE col1 = 1;
UPDATE test_encrypt SET col2 = ENCRYPT_AES('This fits!') WHERE col1 = 2;