通过发送数据包模拟网站登录
在用数据包的形式进行登录之前,其实我是用selenium已经实现了网站登录后跳转至服务界面,拿到服务界面的JSESSIONID,虽然选用了anaconda进行环境管理在环境移植方面有很大的便利,但是还必须确保具有Chrome以及ChromeDriver的驱动版本也要与Chrome本身的版本相适应,综上来看当前的解决方案是不够优雅,所以想用发送数据的形式来获得最终的JSESSIONID。
通过发数据包的形式模拟学校信息化门户网站的登录,这里主要是为了下游的任务做准备,这个任务没有什么技术含量,这里只对模拟登录进行简单的记录。
查看请求登录页面的数据包
登录页面请求表头
这是请求的表头,可以看到第一次请求,没有cookie情况下不用发送任何cookie信息,(accept这一部分没有截图)

登陆页面请求响应

这里set了一个JSESSIONID的 Cookie,肯定不是我们最终要的,只是在进行验证之前的一个会话的状态。
JS
同时,还在查看数据流的时候发现了一系列js的加载。可以看到jQuery这必须要用到不用看,以及qrcode是二维码相关的也可以不用看,可以看一下des.js、login.js。


可以看到des.js应该是用于加解密的。

查看登录页面前端
前端表单
首先进入到登录页面,找到向后端传递数据的前端表单。
1 | <form id="loginForm" action="/cas/login" method="post"> |
其实这里有用的东西不太多,还是要向下看
1 | <input class="login_box_input" id="un" tabindex="1" value="" autocomplete="off" placeholder="用户名"/> |
接着向下看,可以看到类似于payload中所携带的数据字段一样, 其中 id="lt"这个字段会随着页面的重新请求而发生变化。
这里犯了一个错误,因为我请求了好几次都发现name="execution" 的value="e1s1",所以我就在数据包中写死了,但是他是变化的。
1 | <input type="hidden" id="rsa" name="rsa"/> |
分析js
因为在前端的登录按钮 button上没有看到onClick,所以应该是通过刚才的login.js向后传递的数据。
1 | <input type="button" class="login_box_landing_btn" id="index_login_btn">账号登录 |
打开数据流的login.js查看,发现了login的函数,这个函数的主要作用就是,拿到前端id为un、pd的值,然后进行计算到,ul、pl以及最重要的rsa。
1 | function login(){ |
可以看到rsa的计算通过了strEnc的函数,很眼熟,刚刚在dec.js里看到了。
JS断点调试

前端可以存储的js有这几个,没有login.js, 所以退而求其次,只能在dec.js中打断点进行调试,我是打在strEnc这个函数返回的地方,这样他肯定会往回跳到login.js.

接着点击账号登录的button。
看到加密的data是username + password + lt,加密后大概是这个样子(有删减)
1 | '81C9A0B423FD7EA55FB64886E9AAA673641A9FA451F32121FB7D22140B14CA95F1AF2D2FC79C19EFF92299C3C06290A516A61AB18534BC42E3ABCBD0E31E94065CB216DA1096824A2F30CCD8C358E72DE68780116D0D15388928E199B233C54B84168C9D93D0997407A3BEA4415AE18BD66399FA40DABD1D7C58AB359' |
继续往下跳,果然回到login.js

接下来也不过多的进行分析,直接跳过整个过程,向后端发送计算的数据,查看数据包。
分析认证的请求数据
post数据流分析

request header

request data

首先这个包进行了重定向,另外请求包中携带了第一次请求时服务器传来的JSESSIONID,可以看到数据包的字段基本与前面分析的一致。
response header

这里包含了重定向的地址,有几个重要的Cookie用于跨域请求。
用python实现第一个认证
输入认证的简单信息

dec.js
这个我起初是想用python直接运行这个js文件的,但是需要nodejs环境,也不说我没有,但是把又要有这个环境就显得很不优雅,所以我用python实现了这个js里面的所有的功能, 大概有800多行代码也不是很多。但是一步一步验证关键数据的过程是很复杂的,在浏览器断点调试与python一起,借用operator.eq()判断两个值是不是相同,写倒是没花多长时间,主要验证的时间比较多。起初我以为这是最麻烦的一步了,但是我还是太天真了。

第一次访问登陆页面以及数据获取
request header
直接复制就可以,这里有一个小技巧,就是可以在chrome复制到postman里面,然后转python代码。

这是一个get请求,也不用什么特别的。
1 | response = requests.get(url=self.url, headers=headers, data=payload) |
这个请求最重要的是response
要拿到里面的cookie、lt、execution、_enventId这些东西
- cookie是最好拿到的
1 | cookies = list(response.cookies) |
- lt
在返回的html页面中,需要用正则
1 | regex = re.search(r'LT-.*-cas', str(response.content.decode('utf-8'))) |
- execution、_enventId
也是在html页面,很相似
1 | excure = re.search(r'name="execution".*"', str(response.content.decode('utf-8'))) |
登录数据包的构造与发送
- header
直接复制, 但是要修改其中的cookie部分。

- 对
username+password+lt进行加密

- 构造payload data
1 | payload = f'rsa={self.rsa}&ul={self.ul}&pl={self.pl}<={self.lt}&execution={self.execution}&_eventId={self._eventId}' |
发送数据包
1 | response = requests.post(self.url, data=payload, headers=headers, verify=False) |
从以上请求的response拿数据
首先,通过浏览器的数据已经明确的知道这是一个重定向的请求,另外已经知道设置了4个cookie其中有两个用于跨域的身份认证。
但是在这里出现了问题,我一直得到的是response status 200而不是302,同时有302的response但是收到的cookie的数量不对,也不包含Location,这里反过头来去检查加密以及数据的问题,发现可能存在的两个问题。
- execution、_enventId的可能变化
- data构造,起初我使用的是字典,后来变成了字符串直接构造
这里还是会收到200状态码,但是history中是存在302的这种就可以


同时我们只关注history中的数据就可以了
在response.headers._store 中看到拿到了所有想要的cookie

将Cookie保留下来

到这里就拿到了所有想要的身份认证的信息。
分析对下游服务的请求和身份认证
还是通过浏览器先对包进行分析, 但是这个跨域请求存在页面跳转,所以使用了抓包软件fiddle classic进行抓包分析。
发现要携带一个ticket
当然这是重定向得到的。
1 | apply.html?ticket=ST-23367-GdGrhNopWhjmXSUL-cas |
大概过程是这样:
通过携带我们上面身份信息,去请求服务。而这个服务是通过下面的方式进行。通过这个拿到一个类似令牌一样的ticket实现身份认证的内容,然后拿到一个固定的cookie进行接下来的认证。
1 | ?service=http://XXX.XXX |
python获取服务认证ID
- header
其中 self.formCookies[3]是上一次拿到的最重要的一个身份认证的信息。

- 发送数据包
1 | serviceResponse = self.session.get(url=serviceUrl, headers=simpleHeader, allow_redirects=True, verify=False) |
因为这也是一个重定向的,所以不用考虑中间拿到的ticket的过程,只要在history中的302的信息中拿到这个服务的JSESSIONID就可以。
1 | appCookie = dict(serviceResponse.history[1].cookies) |
然后通过finalCookie就可以完成下游任务了。








