Provide minimum level of security to connection strings/passwords

人走茶凉 提交于 2019-12-12 04:32:34

问题


Some info:

-Small application using a SQLite encrypted database

-Using Dotfuscator Community Edition I still can take a look inside of the code (even with methods's names obfuscated)

sQliteConnection.SetPassword("Password=ThisIsMySuperSecretPassword");

-I don´t need a high level of protection about the code itself (the logic), but I need to protect the database password

If I use some concatenation like

PartA = "ThisIs"
PartB= "MySuper"
PartC= "SecretPassword"
Password = PartA + PartB + PartC

With the 4 lines not together on the code, maybe I can have a minimun of protection

Now I can to go a step further with something like this:

PartA = "Tehtibs5Iasu"
PartB= "M4ytS6uhpae3r5"
PartC= "S5efc2rhe8taP3ags5sgw4ogr5d6"

So I have one valid character and one invalid character only to confuse the eventual lurker. I will use a For loop to "decrypt" each part

How much secure can be a solution like this?

Is there other tricks to protect a password inside the code?


回答1:


Is there other tricks to protect a password inside the code?
Yes
(If by 'tricks' you mean alternate means to supply passwords and use connection strings).

Note that you cannot keep something secret on someone's PC. But with a SQLite DB in particular, you can make it fairly difficult to snatch the connection string...by not storing a connection string. The demo DB for this will use the password: ''. That is, it will use a binary password.

A Class with all Shared methods is used to manage the connection:

Imports System.Data.SQLite
Imports System.IO
Imports System.Drawing.Imaging

Friend Class SQLiteConn

    ' ToDo: obscure the name to hide the purpose ?
    Friend Shared PasswordImg As Image
    ' full path to the dbfile
    Friend Shared DBFile As String

    ' cant create one
    Private Sub New()

    End Sub

    ' this is to avoid exposing any method which returns the PW Byte()
    Friend Shared Sub SetPassword(dbcon As SQLiteConnection)
        If PasswordImg Is Nothing Then
            Throw New ArgumentNullException("PasswordImg")
        End If
        dbcon.SetPassword(ImageToByteArray(PasswordImg))
    End Sub

    Friend Shared Sub ChangePassword(newPW As Image)
        ' not tested, I think this is correct 
        Using dbCon As New SQLiteConnection(GetLiteConnStr())
            dbCon.SetPassword(ImageToByteArray(newPW))
            ' to do: add error handling,
            ' only set pwImg on success
            PasswordImg = newPW
        End Using
    End Sub

    ' the connection string is not persisted
    ' to make it harder to copy the byte[] ... it only
    ' exists momentarliy
    Friend Shared Function GetLiteConnStr() As String
        Dim cb As New SQLiteConnectionStringBuilder

        If String.IsNullOrEmpty(DBFile) Then
            Throw New ArgumentNullException("DbFile")
        End If

        cb.DataSource = DBFile
        cb.HexPassword = ImageToByteArray(PasswordImg)
        cb.Pooling = True
        ' add other needed options

        Return cb.ConnectionString
    End Function

    Private Shared Function ImageToByteArray(img As Image) As Byte()
        Dim tmpData As Byte()
        Using ms As New MemoryStream()
            img.Save(ms, ImageFormat.Png)
            tmpData = ms.ToArray
        End Using
        Return tmpData
    End Function
End Class

SQLite allows binary passwords which this implements.

  • The source for the PW in this case is a small orange ball image. This results in a password of 694 bytes
  • Naturally, it cant be any orange ball, but that orange ball
  • The image is embedded into the app, so it would have to be extracted before it could be used - if they know how and know thats what needs to be done. Any error extracting it will invalidate it as the PW.
  • You can change the password by changing a few pixels in that image or using a different image.
  • The resulting byte array is not stored anywhere so it exists only momentarily as part of the connection string. Same for the actual connection string.
  • It doesn't have to be an image. You could use Guid.ToByteArray() or the result of a Crypto Hash of something. Both of those would be a bit easier to reproduce outside your code.
  • Rather than a ConnectionString, it could be modified to return a New SQLiteConnection, but I dont see any advantage in that.
  • A (useless, unused) connection string in app settings might still serve as a red herring for someone looking for the normal approach.

Usage

First, set the password image somewhere (remember, everything is static/Shared):

' only place the PW source is revealed in code
SQLiteConn.PasswordImg = My.Resources.ballorange
' change path for AppData or whatever as needed
SQLiteConn.DBFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                                      "SQLite dbs", "CryptoTest.db")

Note that each time you reference something like My.Resources.ballorange a brand new image object is created. You only need to set the PasswordImg once ever. If you were to change the code to accept the image as an argument, you run the risk of leaking resources as a side effect of creating connections.

Create DB

Dim tmpConnStr = String.Format("Data Source='{0}';Version=3;{1}", SQLiteConn.DBFile, "")

Dim sql = <sql>CREATE TABLE CryptoTest (
                    Id             INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
                    Name           TEXT,
                    Value          INTEGER);</sql>.Value

Using dbCon As New SQLiteConnection(tmpConnStr)
    ' tell helper to set PW, encrypt file
    SQLiteConn.SetPassword(dbCon)
    dbCon.Open()
    Using cmd As New SQLiteCommand(sql, dbCon)
        cmd.ExecuteNonQuery()
    End Using
End Using

I would avoid creating the DB on the client machine. If they are supplied with an empty but encrypted DB file as part of installation, it reduces the ease of patching your code to never encrypt it in the first place. Just distribute a blank like any other data file, installing it to the desired path.

Not many SQLite browser tools support encryption though, so you may need the above to apply that encryption.

Write To DB

Using dbcon As New SQLiteConnection(SQLiteConn.GetLiteConnStr())
    dbcon.Open()

    Using cmd As New SQLiteCommand("INSERT INTO CryptoTest (name, value) VALUES ('foo', 7)", dbcon)
        cmd.ExecuteNonQuery()
    End Using
End Using

Read DB

Using dbCon As New SQLiteConnection(SQLiteConn.GetLiteConnStr())
    Using cmd As New SQLiteCommand("SELECT * FROM CryptoTest", dbCon)
        dbCon.Open()

        dtSample = New DataTable()
        dtSample.Load(cmd.ExecuteReader())
    End Using
End Using

dgv1.DataSource = dtSample

It works fine:

It is not fool proof, they can use ILSpy or other decompiler to see what you are doing, but that will always be the case with a NET app. However, thats just the first hurdle - they also need to work out how to get that runtime data.

When you issue updates/bug fixes you could also change the password by using a different image, changing a few pixels, or altering the methods slightly (convert the byte array to a Base64 string for example).

It is certainly better then security by obscurity (concatenating strings) and may suffice for what you are trying to do.



来源:https://stackoverflow.com/questions/41192205/provide-minimum-level-of-security-to-connection-strings-passwords

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!