web-dev-qa-db-fra.com

Cookies cryptés dans Chrome

Je travaille actuellement sur une application de formulaires C # qui doit accéder à un cookie spécifique sur mon ordinateur, ce que je sais parfaitement bien. Voici le problème:

Google stocke les cookies dans SQLite et j'ai téléchargé le navigateur de base de données SQLite pour m'aider à examiner ces valeurs. Ce qui me surprend, c’est qu’environ la moitié des valeurs de cookie sont vides (y compris celui dont j’ai besoin), même si ce n’est évidemment pas le cas.

Le fichier db se trouve à:

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

Sur Chrome, j'ai un module appelé "Modifier ce cookie" qui me permet de modifier directement les cookies du site Web sur lequel je suis. Cet addon peut lire ces cookies, et le navigateur Web peut analyser des valeurs via HTTP lorsque cela est nécessaire pour différentes demandes. Elles sont donc définitivement présentes. Le navigateur SQLite et mon code personnalisé parviennent tous deux à la conclusion que le champ de valeur en question est vide. .

Pourquoi? Qu'est-ce qui empêche en quelque sorte la lecture du champ par certaines applications?

14
Scherling

Bon, au cas où cela intéresserait quelqu'un, j’ai trouvé une solution à ce problème après beaucoup d’essais, d’erreurs et de recherches sur Google.

La base de cookies de Google Chrome contient 2 colonnes pour stocker les valeurs: "value" et "encrypted_value", cette dernière étant utilisée lorsque le cookie stocké doit être crypté - souvent le cas de certaines informations confidentielles et de clés de session de longue date.

Après avoir compris cela, je devais ensuite trouver un moyen d’accéder à cette clé, stockée sous forme de valeur Blob. J'ai trouvé plusieurs guides sur la façon de procéder, mais celui qui a fini par payer était: http://www.codeproject.com/Questions/56109/Reading-BLOB-in-Sqlite-using-C-NET- CF-PPC

La simple lecture de la valeur ne suffit pas, car elle est cryptée. - Google Chrome utilise le cryptage triple DES avec le mot de passe de l'utilisateur actuel comme source sur les ordinateurs Windows. Pour le déchiffrer en C #, il est conseillé d’utiliser l’API DPAPI (Windows Data Protection API). Il existe quelques guides sur la façon de l’utiliser.

13
Scherling

J'ai rencontré le même problème et le code ci-dessous fournit un exemple de travail à quiconque est intéressé. Tout le mérite en revient à Scherling, car le DPAPI était parfait.

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();
        }
    }
}
26
jasper

Comme la réponse de Jasper, dans un script PowerShell (bien sûr, personnalisez la requête SQL en fonction de vos besoins et du chemin d'accès à l'emplacement de vos cookies):

$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
6
dgtc

Je voulais donc faire cela sans écrire à un fichier temporaire à chaque fois, mais aussi sans implémenter une classe séparée selon la solution de jasper. Comme jasper , j'ai trouvé plus facile et plus rapide d’accéder à System.Data.SQLite.dll disponible here . Ce n'est pas aussi élégant qu'une classe séparée, mais c'est ce qui a fonctionné le mieux pour moi:

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

J'ai aussi trouvé que Add-SqliteAssembly fonction par halr9000 était très utile lorsque vint l'heure de planifier mon script dans le planificateur de tâches de Windows et s'est rendu compte que ce dernier exécutait la version x86 de PowerShell et donc SQLite plutôt que le x64 que j'utilisais dans la console.

1
mjblay
    # 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
0
Yordan Georgiev