0

We have been using the AlwaysEncrypted feature of Microsoft SQL for over a year now. Until now, it has worked flawlessly. Previously, we had only a very few columns encrypted (4). A week ago, we implemented encryption on another 11 columns, so we still make very light use of it.

Unfortunately, some of our "INSERT" commands on tables with encrypted columns, are failing, but only intermittently. The commands are being run by a web server (IIS server). I can run the command, have it fail, and immediately after the exception is thrown, run the same command, in the same process (the web server has simply handled the exception) and it succeeds.

The server binary is written in c#, and the commands are absolutely trivial. For example, execution of this command fails:

INSERT INTO [dbo].[someTable] ( [COL1], [COL2], [COL3] ) VALUES ( @COL1_0, @COL2_0, @COL3_0 )

where all three columns (COL1, COL2 and COL3) are encrypted, and [someTable] has only these three columns, all varchar(64), plus an integer identity column.

c# code (paraphrased) is:

        SqlCommand cmd = new SqlCommand();
        cmd.Connection = GetOpenConnection("Data Source=REDACTED;Integrated Security=SSPI;Column Encryption Setting=enabled;Initial Catalog=PRODUCTION");
        cmd.CommandText = "INSERT INTO [dbo].[someTable] ( [COL1], [COL2], [COL3] ) VALUES ( @COL1_0, @COL2_0, @COL3_0 )";
        cmd.CommandType = CommandType.Text;
        cmd.Transaction = m_Transaction;            

        foreach(string[] paramData in inputParamsData) //3 of these
        {
                                                      //@COLx_0
            SqlParameter param = new SqlParameter(paramData[0], SqlDbType.VarChar, 64);
            param.Value = paramData[1];
            cmd.Parameters.Add(param);
        }
        string msg = "Preparing";
        try
        {
            cmd.Prepare();
            msg = "Executing";
            return cmd.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
            if (ex.Message.Contains("Operand type clash"))
            {
                log.Error($"{msg} non-query", ex);
                log.Error("Parameters in command");
                foreach (var obj in command.Parameters)
                {
                    SqlParameter param = obj as SqlParameter;
                    if (param != null)
                    {
                        log.ErrorFormat("Param name: {0}, length: {1}", param.ParameterName, param.Size);
                    }
                }
            }
            throw;
        }

Execution fails on ExecuteNonQuery() (although the exception message text says that it is unable to "Prepare" the query, examination of the logging messages makes it clear that the Prepare succeeds and the ExecuteNonQuery fails.) The logging we get is:

2019-02-21 23:15:43,046 ERROR [15:xxx.DbConnectionWrapper`6] - Executing non-query
System.Data.SqlClient.SqlException (0x80131904): Operand type clash: varchar is incompatible with varchar(64) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'ColumnKey_20180605_123456', column_encryption_key_database_name = 'Production') collation_name = 'Latin1_General_BIN2'
Statement(s) could not be prepared.
...
Error Number:206,State:2,Class:16
2019-02-21 23:15:43,067 ERROR [15:xxx.DbConnectionWrapper`6] - Parameters in command
2019-02-21 23:15:43,069 ERROR [15:xxx.DbConnectionWrapper`6] - Param name: @FIGI_0, length: 64

One can see that the parameter size has indeed been set correctly. The only column that is incrypted it the "FIGI" column, which is a varchar(64) column, and the parameter appears to be correctly set.

We have a Microsoft SQL Server Enterprise (64-bit), version 13.0.5026.0. IIS server has .NET 4.6.1.

Has anyone experienced intermittent failures like this? Does anyone have any pointers where to look to find the problem?

I note that the class SqlParameter has a property ForceColumnEncryption, but there is very little documentation to indicate when, where or how this parameter should be used. Can anyone explain its use or point me to decent documentation?

Edit: it turns out the intermittent nature is worse that this. It fails only about once a week. Given comments below regarding parameter size, I added in some debug logging when it fails, with results as above.

David I. McIntosh
  • 2,038
  • 4
  • 23
  • 45
  • 2
    A common theme in these issues is that the SQL Parameter type length not matching the same as the underlying field. Now I understand that you have **paraphrased** the code that is executing, but I think you really need to include the **actual** code that is executing here. – Brendan Green Feb 13 '19 at 02:22
  • Re: parameter type length. Yes, I am aware of that issue. (I cannot put in the "actual" code for multiple reasons). I have put breakpoints in the code and examined the SQL Parameter Type length, and it does match. The odd thing is that the problem is intermittent - it happens rarely in fact, and always succeeds on second attempts. I will log the parameter lengths in the event of failure, just to be sure. Thanks. – David I. McIntosh Feb 13 '19 at 06:42
  • You don't need to provide the actual code; but you *do* need to provide a [mcve]. As it explains in that page, you may find the cause on your way to creating that MCVE. If so, you can post it as an answer. If not, post the MCVE so that other people can help you. – Richardissimo Feb 23 '19 at 21:26
  • Have you ever tried to debug an _intermittent_ problem that is in someone else's binary? If I could reproduce it, I could likely find the issue. The code works in testing, and works in production 90% of the time. As I have said, when it fails, running the IDENTICAL code immediately after succeeds. – David I. McIntosh Feb 24 '19 at 22:24
  • We stopped having these issues almost as quickly as we started. Then, a year later, we encrypted a couple of new columns. The issue appeared again, a couple of times... It turns out, when you first encrypt a column, there is some bug that appears the first time you attempt an insert into that column. In other words, we can encrypt a column, and so long as we are just reading from that column, say for the first week after we encrypt it, all is well. The first insert attempt, a week later, fails. All subsequent operations are fine. This has happened each time we encrypt a new column. – David I. McIntosh Apr 09 '20 at 03:42
  • Definitely a bug somewhere in the SQL server or the drivers, though Microsoft will not admit it... – David I. McIntosh Apr 09 '20 at 03:44

0 Answers0