# -*- coding: utf-8 -*-
"""Parser for the Google Chrome Cookie database."""
from __future__ import unicode_literals
from dfdatetime import webkit_time as dfdatetime_webkit_time
from plaso.containers import events
from plaso.containers import time_events
from plaso.lib import definitions
# Register the cookie plugins.
from plaso.parsers import cookie_plugins # pylint: disable=unused-import
from plaso.parsers import sqlite
from plaso.parsers.cookie_plugins import manager as cookie_plugins_manager
from plaso.parsers.sqlite_plugins import interface
[docs]class ChromeCookieEventData(events.EventData):
"""Chrome Cookie event data.
Attributes:
cookie_name (str): name of the cookie.
host (str): hostname of host that set the cookie value.
httponly (bool): True if the cookie cannot be accessed through client
side script.
path (str): path where the cookie got set.
persistent (bool): True if the cookie is persistent.
secure (bool): True if the cookie should only be transmitted over a
secure channel.
url (str): URL or path where the cookie got set.
data (str): value of the cookie.
"""
DATA_TYPE = 'chrome:cookie:entry'
def __init__(self):
"""Initializes event data."""
super(ChromeCookieEventData, self).__init__(data_type=self.DATA_TYPE)
self.cookie_name = None
self.data = None
self.host = None
self.httponly = None
self.path = None
self.persistent = None
self.secure = None
self.url = None
[docs]class ChromeCookiePlugin(interface.SQLitePlugin):
"""Parse Chrome Cookies file."""
NAME = 'chrome_cookies'
DESCRIPTION = 'Parser for Chrome cookies SQLite database files.'
# Define the needed queries.
QUERIES = [
# Query for Chrome versions 17 - 65.
(('SELECT creation_utc, host_key, name, value, path, expires_utc, '
'secure, httponly, last_access_utc, has_expires, persistent '
'FROM cookies'), 'ParseCookieRow'),
# Query for Chrome versions 66 and above. The column names changed
# slightly, but the values are the same.
(('SELECT creation_utc, host_key, name, value, path, expires_utc, '
'is_secure AS secure, is_httponly AS httponly, last_access_utc, '
'has_expires, is_persistent AS persistent '
'FROM cookies'), 'ParseCookieRow')]
# The required tables for the cookies database.
REQUIRED_TABLES = frozenset(['cookies', 'meta'])
SCHEMAS = [{
'cookies': (
'CREATE TABLE cookies (creation_utc INTEGER NOT NULL UNIQUE PRIMARY '
'KEY, host_key TEXT NOT NULL, name TEXT NOT NULL, value TEXT NOT '
'NULL, path TEXT NOT NULL, expires_utc INTEGER NOT NULL, secure '
'INTEGER NOT NULL, httponly INTEGER NOT NULL, last_access_utc '
'INTEGER NOT NULL, has_expires INTEGER DEFAULT 1, persistent '
'INTEGER DEFAULT 1)'),
'meta': (
'CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, '
'value LONGVARCHAR)')}]
# Point to few sources for URL information.
URLS = [
'http://src.chromium.org/svn/trunk/src/net/cookies/',
('http://www.dfinews.com/articles/2012/02/'
'google-analytics-cookies-and-forensic-implications')]
# Google Analytics __utmz variable translation.
# Taken from:
# http://www.dfinews.com/sites/dfinews.com/files/u739/Tab2Cookies020312.jpg
GA_UTMZ_TRANSLATION = {
'utmcsr': 'Last source used to access.',
'utmccn': 'Ad campaign information.',
'utmcmd': 'Last type of visit.',
'utmctr': 'Keywords used to find site.',
'utmcct': 'Path to the page of referring link.'}
def __init__(self):
"""Initializes a plugin."""
super(ChromeCookiePlugin, self).__init__()
self._cookie_plugins = (
cookie_plugins_manager.CookiePluginsManager.GetPlugins())
# pylint 1.9.3 wants a docstring for kwargs, but this is not useful to add.
# pylint: disable=missing-param-doc
[docs] def ParseCookieRow(self, parser_mediator, query, row, **unused_kwargs):
"""Parses a cookie row.
Args:
parser_mediator (ParserMediator): parser mediator.
query (str): query that created the row.
row (sqlite3.Row): row resulting from the query.
"""
query_hash = hash(query)
cookie_name = self._GetRowValue(query_hash, row, 'name')
cookie_data = self._GetRowValue(query_hash, row, 'value')
hostname = self._GetRowValue(query_hash, row, 'host_key')
if hostname.startswith('.'):
hostname = hostname[1:]
httponly = self._GetRowValue(query_hash, row, 'httponly')
path = self._GetRowValue(query_hash, row, 'path')
persistent = self._GetRowValue(query_hash, row, 'persistent')
secure = self._GetRowValue(query_hash, row, 'secure')
if secure:
scheme = 'https'
else:
scheme = 'http'
url = '{0:s}://{1:s}{2:s}'.format(scheme, hostname, path)
event_data = ChromeCookieEventData()
event_data.cookie_name = cookie_name
event_data.data = cookie_data
event_data.host = hostname
event_data.httponly = bool(httponly)
event_data.path = path
event_data.persistent = bool(persistent)
event_data.query = query
event_data.secure = bool(secure)
event_data.url = url
timestamp = self._GetRowValue(query_hash, row, 'creation_utc')
date_time = dfdatetime_webkit_time.WebKitTime(timestamp=timestamp)
event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_CREATION)
parser_mediator.ProduceEventWithEventData(event, event_data)
timestamp = self._GetRowValue(query_hash, row, 'last_access_utc')
date_time = dfdatetime_webkit_time.WebKitTime(timestamp=timestamp)
event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_LAST_ACCESS)
parser_mediator.ProduceEventWithEventData(event, event_data)
timestamp = self._GetRowValue(query_hash, row, 'expires_utc')
if timestamp:
date_time = dfdatetime_webkit_time.WebKitTime(timestamp=timestamp)
event = time_events.DateTimeValuesEvent(
date_time, definitions.TIME_DESCRIPTION_EXPIRATION)
parser_mediator.ProduceEventWithEventData(event, event_data)
for plugin in self._cookie_plugins:
if cookie_name != plugin.COOKIE_NAME:
continue
try:
plugin.UpdateChainAndProcess(
parser_mediator, cookie_data=cookie_data, cookie_name=cookie_name,
url=url)
except Exception as exception: # pylint: disable=broad-except
parser_mediator.ProduceExtractionError(
'plugin: {0:s} unable to parse cookie with error: {1!s}'.format(
plugin.NAME, exception))
sqlite.SQLiteParser.RegisterPlugin(ChromeCookiePlugin)