V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
homeway88
V2EX  ›  程序员

如何在 shell 中动态获取 chrome 浏览器的 cookie 信息

  •  3
     
  •   homeway88 · 2019-10-07 21:15:39 +08:00 · 8616 次点击
    这是一个创建于 1634 天前的主题,其中的信息可能已经有所发展或是发生改变。

    0. 背景

    在工作的时候,经常要接触一些办公系统,在网页上通过机械化的操作,来完成一个简单的功能,比如某台主机权限的申请,通过一套操作一下,大概 7、8 个步骤,花费 30 秒的时间,虽然不长,但是要脱离终端,到浏览器去操作,打断了心流,就感觉很烦人了。 我们在网页的操作,其实就是往这个网站的后台发起一个 API 请求,这个动作,我们在终端里面,通过 curl 命令也能完成,比如我们打开百度的首页,通过 chrome 的控制台 -> Network -> 找到对应的请示,右键,Copy -> Copy as cURL,我们就能得到如下的一条命令:

    curl 'https://www.baidu.com/' -H 'Connection: keep-alive' -H 'Cache-Control: max-age=0' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36' -H 'Sec-Fetch-Mode: navigate' -H 'Sec-Fetch-User: ?1' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3' -H 'Sec-Fetch-Site: none' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8' -H 'Cookie: BIDUPSID=A10629EBE8B29EBEE170B7E4E405; PSTM=1520729161; BDUSS=pMV2FGNlNaUUdB134asdfCOVU5cHFCR0p2SzBtY0Q2OWlNZlhORHdhN1ZjQVFBQUFBJCQAAAAAABBBBBGHAAaG9tZXdheQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDejVzw3o1cck; ' --compressed
    

    在终端执行这条命令,和你浏览器发起的这个请求,其实是等同的,但是这里面,有一个最重要的参数,就是你的 Cookie 信息,这个信息可以代表你当前在这个网站上的登录用户,如果我们要写一些自动化的网站操作脚本,或者写爬虫什么的,第一步就是怎么拿到 cookie 信息。

    1. 获取 chrome 浏览器里的 cookie

    cookie 信息,肯定是存储在 chrome 中,具体的存储位置未知,并且对于这么隐私的数据,应该也是会进行加密存储的,因此,我并没有去尝试通过读取 cookie 文件的方式。

    想起之前用过一个模拟发请求的 chrome 插件,postman,通过安装Postman Interceptor拦截器,可以让我们在postman里模拟发请求的时候,自动带上网站的 cookie 信息,但是查阅了postman的相关资料,也没有开放接口让我们在其它地方可以拿到 cookie 信息。

    1.1 通过 chrome 插件获取 cookie 数据

    既然在浏览器插件里能拿到 cookie,那么我们自己实现一个不就行了吗,拿到数据之后,再想办法把数据传出来就可以了。参考 chrome api 文档,我们可以通过添加一个 cookies 的监听器,来拿到变化的 cookie,以及拿到某个 domain 域下的所有 cookie,但是 chrome 也是运行在浏览器环境之上,无法直接往本地存储写数据,只能通过对外发起 ajax 请求来把数据传出去了。核心代码如下:

    function refreshDomain(domain) {
    	chrome.cookies.getAll({domain: domain}, function (cookies) {
    		// 这里就能拿到这个域下所有 cookie 了
    		let all_cookies = cookies.filter(item => item.domain === domain)
    			.map(item => item.name + "=" + item.value).join("; ");
    
    		console.log("Report Cookie:domain=" + domain + ",cookies=" + all_cookies);
    		$.ajax({
    			 //这里需要一个 http 服务来接收数据
    			url: "http://localhost:8888",
    			method: "POST",
    			data: {
    				domain: domain,
    				cookies: all_cookies
    			},
    			dataType: "json",
    			success: function(data) { console.log("Report success:" + data) },
    			failure: function (data) {
    				console.log("Report failure:" + data)
    			}
    		})
    	});
    }
    
    chrome.cookies.onChanged.addListener(function (event) {
    	const cookie = event.cookie;
    	refreshDomain(cookie.domain);
    });
    

    1.2 接收数据并存储

    这里还需要实现一个 http 服务来接收插件发出来的 cookie 数据,这里我用 spring boot 初始化出来一个 spring mvc 的工程,再添加两个 api,一个用于接收 cookie 并存储,一个用于对外再接供获取 cookie 信息的接口。代码如下:

    
    @SpringBootApplication
    @EnableWebMvc
    @Controller
    public class CookieManager {
    	private static Map<String, String> domainCookies = new HashMap<>(1024);
    
    	@RequestMapping(value = "/", method = RequestMethod.GET)
    	@ResponseBody
    	public String getCookie(@RequestParam("domain") String domain) {
    		return domainCookies.entrySet().stream()
    			.filter(e -> domain.endsWith(e.getKey()))
    			.map(Entry::getValue)
    			.collect(Collectors.joining("; "));
    	}
    
    	@RequestMapping(value = "/", method = RequestMethod.POST)
    	@ResponseBody
    	public String setCookie(@RequestParam("domain") String domain,
    							@RequestParam("cookies") String cookies) throws IOException {
    
    		domainCookies.put(domain, cookies);
    
    		return domain;
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(CookieManager.class, args);
    	}
    }
    

    这里在获取 cookie 的时候做了一个处理,自动把父域的 cookie 带上。比如获取 domain=www.baidu.com 的 cookie,会把 domain=.baidu.com 的数据也返回

    1.3 在 shell 中的用法

    COOKIE=$(wget localhost:8888/?domain=www.baidu.com -q -O -)
    echo $COOKIE
    
    curl 'https://www.baidu.com/' -H "Cookie:  $COOKIE"
    

    注意这里"Cookie: $COOKIE"必须是双引号,不能用单引号。

    特别注意

    cookie 信息是一个非常隐私和重要的数据,虽然通过这个方法,能够将浏览器里面这个数据导出来,但对于这个数据,是需要特别小心保存的,cookie 信息如果被别人拿到,相当于别人可以用你的身份做任何事情,这是非常危险的。因此本文只是作为一个例子,没有做任何加密,但在实际应用中,最好都做加密传输。

    参考文档

    26 条回复    2019-10-10 16:19:31 +08:00
    aheadlead
        1
    aheadlead  
       2019-10-07 21:55:06 +08:00   ❤️ 1
    挺好的思路

    有个建议,既然都强调 shell 了,就没必要还费大功夫请 Sprint Boot 出来了吧
    “接收数据并存储”完全可以只用 netcat+grep 实现啊
    undefind
        2
    undefind  
       2019-10-07 23:00:29 +08:00
    写过一个在 Windows 下获取 chrome 浏览器中 httponly 的 cookie 信息,供你参考。
    ```
    """
    import os
    import sqlite3
    from win32.win32crypt import CryptUnprotectData


    def getcookiefromchrome(host):
    cookiepath = os.environ['LOCALAPPDATA'] + r"\Google\Chrome\User Data\Default\Cookies"
    sql = "select * from cookies where host_key like " + "'%" + host + "';"
    try:
    with sqlite3.connect(cookiepath) as conn:
    cu = conn.cursor()
    cookies = {name: CryptUnprotectData(encrypted_value)[1].decode() for host_key, name, encrypted_value in
    cu.execute(sql).fetchall()}
    return cookies
    except Exception as e:
    pass

    getcookiefromchrome("example.com")
    ```
    jugelizi
        3
    jugelizi  
       2019-10-07 23:27:06 +08:00
    所以
    shell 怎么就拿到 cookie 了
    挂羊头卖狗肉?
    reus
        4
    reus  
       2019-10-08 02:20:52 +08:00 via Android
    直接打开 sqlite 文件不就得了
    lalalakakaka
        5
    lalalakakaka  
       2019-10-08 03:08:49 +08:00
    you-get 至今无法支持读取 chrome 的 sqlite 文件。大拿们快上啊!
    homeway88
        6
    homeway88  
    OP
       2019-10-08 07:31:48 +08:00
    @jugelizi chrome 插导出 cookie 数据-->http 服务器--> shell 请求 http 服务器拿 cookie
    homeway88
        7
    homeway88  
    OP
       2019-10-08 07:33:42 +08:00
    @aheadlead 感谢你的思路分享,我也研究一下用 nc 怎么实现。因为我本身是做 web 开发的,所以就用了最熟悉的 spring boot,有点杀鸡用牛刀了。
    homeway88
        8
    homeway88  
    OP
       2019-10-08 07:35:12 +08:00
    @undefind 谢谢你的分享。我用的是 MacOS,不知道是否也能用 sqlite 的方法。
    homeway88
        9
    homeway88  
    OP
       2019-10-08 07:57:02 +08:00
    @reus https://www.jianshu.com/p/c94363c33bae 这里有篇文章是讲打开 sqlite 文件的,但里面的数据是加密过的,需要解密,这个我还没验证过。
    MacOS 下,chrome cookie 对应的 sqlite 文件是:~/Library/Application Support/Google/Chrome/Default/Cookies
    kingfalse
        10
    kingfalse  
       2019-10-08 08:01:21 +08:00 via Android
    了解一下爬虫??
    homeway88
        11
    homeway88  
    OP
       2019-10-08 09:24:40 +08:00
    @reus https://github.com/n8henrie/pycookiecheat MacOS 下实测可行,授权一下 keychain 读取权限。
    rain0002009
        12
    rain0002009  
       2019-10-08 09:41:11 +08:00
    为啥不用 puppeteer 呢
    shingle
        13
    shingle  
       2019-10-08 09:49:26 +08:00 via Android
    puppeteer+1 可以指定浏览器位置和 data 文件夹,打开指定域名直接读取
    hanzichi
        14
    hanzichi  
       2019-10-08 09:58:19 +08:00
    这么麻烦,模拟登录后,自动维护 cookie jar 就行了啊
    xiaotuzi
        15
    xiaotuzi  
       2019-10-08 12:15:34 +08:00 via iPhone
    审核元素很麻烦吗?
    另外,PHP 也很容易获取 cookie
    关于 cookie 存储,存到本地或者数据库都行,看个人怎么使用
    爬虫都要接触这个,你这帖子着重点应该是 shell 获取 cookie,但 shell 根本没办法直接获取,需要借助 api,这就直接转到 api 帮你操作不就行了…为啥还要 shell 携带 cookie 处理事情?
    趁一群爬虫大佬还没出来,赶紧结贴。🤣
    locoz
        16
    locoz  
       2019-10-08 16:35:55 +08:00
    看完了,感觉你路走偏了...办公系统应该种类不多吧?直接写模拟登录应该比这更方便?连浏览器都不用开了。而且办公系统这种东西一般不会更新,也不会有什么反爬措施,如果请求里不带加密参数的话,抓包一下、把登录的操作复制出来直接用估计都可以拿到登录 Cookie...
    sunziren
        17
    sunziren  
       2019-10-08 16:54:44 +08:00
    @kingfalse 你这个头像 like 威震天
    ochatokori
        18
    ochatokori  
       2019-10-08 17:01:45 +08:00 via Android
    为什么不用 curl 登陆呢,到时候 cookie 过期了难不成还要打开浏览器更新 cookie ?
    homeway88
        19
    homeway88  
    OP
       2019-10-08 17:18:14 +08:00
    @locoz 我们内部所有系统都用一个统一的登录系统,这个登录系统在外网也可以用,因此安全等级很高。并且模拟登录和爬虫我也没搞过
    homeway88
        20
    homeway88  
    OP
       2019-10-08 17:19:29 +08:00
    @ochatokori 登录系统不好破
    homeway88
        21
    homeway88  
    OP
       2019-10-08 17:21:14 +08:00
    @xiaotuzi 上面回复过,shell 执行 python 脚本读 cookie 的 sqlite 文件是可以的
    locoz
        22
    locoz  
       2019-10-08 17:30:37 +08:00
    @homeway88 #19 安全等级高其实并不意味着模拟登录难,最多就是要打码而已。(毕竟这种系统的安全在前端做不了太多的事情,防 XSS 之类的对搞模拟登录又没有影响。
    godoway
        23
    godoway  
       2019-10-08 18:47:00 +08:00 via Android
    这种不应该是自己发送登录请求然后持久化 cookie 就行了么,自己写一个 command line application 怎么看。
    conn4575
        24
    conn4575  
       2019-10-09 03:13:02 +08:00 via Android
    感觉走了弯路,你其实想要的是登录接口返回的 cookie,直接用 python 写一个脚本的话都不需要 100 行,更不需要 spring 工程。。
    xiaotuzi
        25
    xiaotuzi  
       2019-10-09 09:10:59 +08:00 via iPhone
    @homeway88 那不就是 api,通过调用外出程序获取
    astkaasa
        26
    astkaasa  
       2019-10-10 16:19:31 +08:00
    curl -c -
    登录的接口试下这个?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5677 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 06:28 · PVG 14:28 · LAX 23:28 · JFK 02:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.