【问题标题】:Encrypted cookies in ChromeChrome 中的加密 cookie
【发布时间】:2014-03-20 12:21:14
【问题描述】:

我目前正在开发一个 C# 表单应用程序,该应用程序需要访问我计算机上的特定 cookie,我完全可以做到这一点。问题来了:

Google 将 cookie 存储在 SQLite 中,我下载了 Sqlite 数据库浏览器来帮助我查看这些值。令我惊讶的是,大约一半的 cookie 值显示为空(包括我需要的那个),尽管它们显然不是。

db 文件位于:

C:\Users\%username%\AppData\Local\Google\Chrome\User Data\Default\Cookies

在 Chrome 上,我有一个名为“编辑此 Cookie”的插件,它允许我直接修改我所在网站上的 cookie。这个插件可以读取这些 cookie,并且 Web 浏览器可以在不同请求需要时通过 HTTP 解析值,所以它们肯定存在 - 尽管如此,SQLite 浏览器和我的自定义代码都得出特定值字段为空的结论.

这是为什么呢? 是什么阻止了某些应用程序读取该字段?

【问题讨论】:

    标签: sqlite google-chrome cookies


    【解决方案1】:

    我也遇到过同样的问题,下面的代码为感兴趣的人提供了一个工作示例。所有功劳归功于 Scherling,因为 DPAPI 很到位。

    public class ChromeCookieReader
    {
        public IEnumerable<Tuple<string,string>> ReadCookies(string hostName)
        {
            if (hostName == null) throw new ArgumentNullException("hostName");
    
            var dbPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Google\Chrome\User Data\Default\Cookies";
            if (!System.IO.File.Exists(dbPath)) throw new System.IO.FileNotFoundException("Cant find cookie store",dbPath); // race condition, but i'll risk it
    
            var connectionString = "Data Source=" + dbPath + ";pooling=false";
    
            using (var conn = new System.Data.SQLite.SQLiteConnection(connectionString))
            using (var cmd = conn.CreateCommand())
            {
                var prm = cmd.CreateParameter();
                prm.ParameterName = "hostName";
                prm.Value = hostName;
                cmd.Parameters.Add(prm);
    
                cmd.CommandText = "SELECT name,encrypted_value FROM cookies WHERE host_key = @hostName";
    
                conn.Open();
                using (var reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        var encryptedData = (byte[]) reader[1];
                        var decodedData = System.Security.Cryptography.ProtectedData.Unprotect(encryptedData, null, System.Security.Cryptography.DataProtectionScope.CurrentUser);
                        var plainText = Encoding.ASCII.GetString(decodedData); // Looks like ASCII
    
                        yield return Tuple.Create(reader.GetString(0), plainText);
                    }
                }
                conn.Close();
            }
        }
    }
    

    【讨论】:

    • 你的变量'hostName'包含什么?
    • 一个例子是stackoverflow.com,它会为这个网站解析厨师。
    • “System.Security.Cryptography.ProtectedData.Unprotect”是否有任何功能可以在 MAC 上运行?
    • 你不需要密钥来解密值吗?
    • 嗨,Jasper,您是否考虑更新您的答案,以将stackoverflow.com/a/60611673/6481581 中列出的信息包含在 Google Chrome 版本 80 及更高版本中,使用不同的技术来解密 cookie。
    【解决方案2】:

    好的,如果有人有兴趣,我在经过大量试验、错误和谷歌搜索后找到了解决这个问题的方法。

    Google Chrome cookie DB 有 2 列用于存储值:“value”和“encrypted_value”,后者在请求对存储的 cookie 进行加密时使用 - 通常是某些机密信息和长期会话密钥的情况。

    在弄清楚这一点之后,我需要找到一种方法来访问这个键,存储为 Blob 值。我找到了一些关于如何做到这一点的指南,但最终支付的是:http://www.codeproject.com/Questions/56109/Reading-BLOB-in-Sqlite-using-C-NET-CF-PPC

    仅仅读取值是不够的,因为它是加密的。 - 谷歌浏览器使用三重 DES 加密和当前用户密码作为 Windows 机器上的种子。为了在 C# 中解密它,应该使用 Windows 数据保护 API (DPAPI),那里有一些关于如何使用它的指南。

    【讨论】:

      【解决方案3】:

      就像 Jasper 的回答一样,在 PowerShell 脚本中(当然,根据您的需要自定义 SQL 查询以及 cookie 位置的路径):

      $cookieLocation = 'C:\Users\John\AppData\Local\Google\Chrome\User Data\Default\cookies'
      $tempFileName = [System.IO.Path]::GetTempFileName()
      
      "select writefile('$tempFileName', encrypted_value) from cookies where host_key = 'localhost' and path = '/api' and name = 'sessionId';" | sqlite3.exe "$cookieLocation"
      $cookieAsEncryptedBytes = Get-Content -Encoding Byte "$tempFileName"
      Remove-Item "$tempFileName"
      
      Add-Type -AssemblyName System.Security
      $cookieAsBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($cookieAsEncryptedBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
      $cookie = [System.Text.Encoding]::ASCII.GetString($cookieAsBytes)
      $cookie
      

      【讨论】:

      • 短版: Add-Type -AssemblyName System.Security; [System.Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect($(Get-Content -Encoding Byte "YOUR_FILENAME.BIN"), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser))
      【解决方案4】:

      所以我想做到这一点,而不是每次都写入临时文件,也不想按照 jasper 的解决方案实现一个单独的类。与jasper 一样,我发现访问here 可用的System.Data.SQLite.dll 更容易、更快捷。它不像单独的类那样优雅,但它对我来说最有效:

      Add-Type -AssemblyName System.Security
      Add-Type -Path 'C:\Program Files\System.Data.SQLite\2015\bin\x64\System.Data.SQLite.dll'
      
      Function Get-Last-Cookie {
          Param(
              [Parameter(Mandatory=$True)] $valueName,
              [Parameter(Mandatory=$True)] $hostKey,
              [Parameter(Mandatory=$True)] $dbDataSource
          )
      
          $conn = New-Object -TypeName System.Data.SQLite.SQLiteConnection
          $conn.ConnectionString = "Data Source=$dbDataSource"
          $conn.Open()
      
          $command = $conn.CreateCommand()
          $query = "SELECT encrypted_value FROM cookies WHERE name='$valueName' `
                    AND host_key='$hostKey' ORDER BY creation_utc DESC LIMIT 1"
          $command.CommandText = $query
          $adapter = New-Object -TypeName System.Data.SQLite.SQLiteDataAdapter $command
          $dataset = New-Object System.Data.DataSet
          [void]$adapter.Fill($dataset)
          $command.Dispose();
          $conn.Close();
          $cookieAsEncryptedBytes = $dataset.Tables[0].Rows[0].ItemArray[0]
          $cookieAsBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($cookieAsEncryptedBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
          return [System.Text.Encoding]::ASCII.GetString($cookieAsBytes)
      }
      
      $localAppDataPath = [Environment]::GetFolderPath([Environment+SpecialFolder]::LocalApplicationData)
      $cookieDbPath = 'Google\Chrome\User Data\Default\Cookies'
      $dbDataSource = Join-Path -Path $localAppDataPath -ChildPath $cookieDbPath
      
      $plainCookie = Get-Last-Cookie 'acct' '.stackoverflow.com' $dbDataSource
      Write-Host $plainCookie
      

      我还发现 halr9000Add-SqliteAssembly 函数在需要在 Windows 任务调度程序中调度我的脚本时非常有用,我意识到任务调度程序运行的是 x86 版本的 PowerShell 和 SQLite,而不是 SQLite x64 我在控制台中使用。

      【讨论】:

        【解决方案5】:

        问题是谷歌浏览器会加密你需要读取的数据,所以你必须解密它。 首先,获取 cookie 文件的副本。然后使用 SQLite3 阅读它。之后,获取加密的字节。最后,使用下面的代码对其进行解密。 您将需要这些 Nuget:

        using System.IO;
        using System.Net;
        using System.Data.SQLite;
        using Org.BouncyCastle.Crypto.Engines;
        using Org.BouncyCastle.Crypto.Modes;
        using Org.BouncyCastle.Crypto.Parameters;
        using Org.BouncyCastle.Crypto;
        using Newtonsoft.Json.Linq;
        

        到目前为止的代码:

                File.Copy(Environment.GetEnvironmentVariable("APPDATA") + @"/../Local/Google/Chrome/User Data/Default/Cookies", @"./Cookies",true);
                SQLiteConnection Cnn = new SQLiteConnection("Data Source=" + @"./Cookies" + ";pooling=false");
                Cnn.Open();
        
                SQLiteCommand cmd = new SQLiteCommand("SELECT host_key, name, value, encrypted_value FROM cookies WHERE name='mvrusername' OR name='mvrcookie' OR name='mikuki4'", Cnn);
                SQLiteDataReader rdr = cmd.ExecuteReader();
                while (rdr.Read())
                {
                    byte[] encryptedData = (byte[])rdr["encrypted_value"];
                    string encKey = File.ReadAllText(Environment.GetEnvironmentVariable("APPDATA") + @"/../Local/Google/Chrome/User Data/Local State");
                    encKey = JObject.Parse(encKey)["os_crypt"]["encrypted_key"].ToString();
                    var decodedKey = System.Security.Cryptography.ProtectedData.Unprotect(Convert.FromBase64String(encKey).Skip(5).ToArray(), null, System.Security.Cryptography.DataProtectionScope.LocalMachine);
        
                    const int MAC_BIT_SIZE = 128;
                    const int NONCE_BIT_SIZE = 96;
        
                    using (var cipherStream = new MemoryStream(encryptedData))
                    using (var cipherReader = new BinaryReader(cipherStream))
                    {
                        var nonSecretPayload = cipherReader.ReadBytes(3);
                        var nonce = cipherReader.ReadBytes(NONCE_BIT_SIZE / 8);
                        var cipher = new GcmBlockCipher(new AesEngine());
                        var parameters = new AeadParameters(new KeyParameter(decodedKey), MAC_BIT_SIZE, nonce);
                        cipher.Init(false, parameters);
                        var cipherText = cipherReader.ReadBytes(encryptedData.Length);
                        var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];
                        try
                        {
                            var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
                            cipher.DoFinal(plainText, len);
                        }
                        catch (InvalidCipherTextException)
                        {
                        }
                        string _cookie= Encoding.Default.GetString(plainText);
        
                    }
                }
        

        // 非常感谢 https://stackoverflow.com/a/60611673/6481581 回答 Chrome 80 及更高版本如何改变 cookie 的加密方式。

        【讨论】:

          【解决方案6】:
              # this powershell scripts exports your cookies to a format curl and wget understand
              # Obs ! Each profile has its own cookes file , replace me (ysg ;o) with your win usr name
              # aka wget -x --load-cookies cookies.txt http://stackoverflow.com/questions/22532870/encrypted-cookies-in-chrome
          
              $cookieLocation = 'C:\Users\ysg\AppData\Local\Google\Chrome\User Data\Profile 1\Cookies'
              $curl_cookies_file="C:\var\ygeo.reports.app.futurice.com.cookies.doc-pub-host.txt"
              $tempFileName1 = [System.IO.Path]::GetTempFileName()
              $tempFileName2 = [System.IO.Path]::GetTempFileName()
          
              # adjust your filter in the where clause ...
              "select writefile('$tempFileName1', encrypted_value) from cookies where host_key = '.futurice.com' ;" | sqlite3.exe "$cookieLocation"
              $cookieAsEncryptedBytes = Get-Content -Encoding Byte "$tempFileName1"
              Remove-Item "$tempFileName1"
          
          
              Add-Type -AssemblyName System.Security
              $cookieAsBytes = [System.Security.Cryptography.ProtectedData]::Unprotect($cookieAsEncryptedBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
              $cookie = [System.Text.Encoding]::ASCII.GetString($cookieAsBytes)
              $unquoted_cookie=$cookie -replace '"', ""
          
              # adjust your filter in the where clause ...
              "
              select  
                  host_key
               , CASE WHEN httponly=0 THEN 'FALSE' ELSE 'TRUE' END
               , path
               , CASE WHEN secure=0 THEN 'FALSE' ELSE 'TRUE' END
               , expires_utc
               , name 
               , '$unquoted_cookie'
              from cookies where host_key = '.futurice.com' ;" | sqlite3.exe -separator " " "$cookieLocation" > $curl_cookies_file
          
              Get-ChildItem *.txt | ForEach-Object { (Get-Content $_) | Out-File -Encoding ASCII $_ }
          
              # check the meta data table
              #"PRAGMA table_info([cookies]);" | sqlite3.exe "$cookieLocation"
          
              # src: https://github.com/daftano/cookies.txt/blob/master/src/popup.js
              #content += escapeForPre(cookie.domain);
              #content += "\t";
              #content += escapeForPre((!cookie.hostOnly).toString().toUpperCase());
              #content += "\t";     
              #content += escapeForPre(cookie.path); 
              #content += "\t";     
              #content += escapeForPre(cookie.secure.toString().toUpperCase());
              #content += "\t";     
              #content += escapeForPre(cookie.expirationDate ? Math.round(cookie.expirationDate) : "0");
              #content += "\t";     
              #content += escapeForPre(cookie.name);
              #content += "\t";     
              #content += escapeForPre(cookie.value);
              #content += "\n";
              #
              #0|creation_utc|INTEGER|1||1
              #1|host_key|TEXT|1||0
              #2|name|TEXT|1||0
              #3|value|TEXT|1||0
              #4|path|TEXT|1||0
              #5|expires_utc|INTEGER|1||0
              #6|secure|INTEGER|1||0
              #7|httponly|INTEGER|1||0
              #8|last_access_utc|INTEGER|1||0
              #9|has_expires|INTEGER|1|1|0
              #10|persistent|INTEGER|1|1|0
              #11|priority|INTEGER|1|1|0
              #12|encrypted_value|BLOB|0|''|0
              #13|firstpartyonly|INTEGER|1|0|0
          

          【讨论】:

            【解决方案7】:

            只需将“value”设置为您想要的 cookie,“encrypted_value”设置为 NULL,“priority”设置为 0

            【讨论】:

            • 这完全没有意义。您要做的是解密加密的值。将其设置为 NULL 将导致您丢失该值。
            猜你喜欢
            • 1970-01-01
            • 2021-01-08
            • 2021-08-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-01-15
            相关资源
            最近更新 更多