新浪微博提醒脚本
2012-02-07无意中在微博上看到@乌云-漏洞报告平台 提起了一个关于“消息提醒未授权访问”的BUG。大致的看了一下网站上的截图,配合新浪微博的API 文档的介绍,估摸着弄了一下这个漏洞的实现脚本。其实,从实用的角度来看,这个BUG,基本上算不上严重(乌云的自评级才到3),也没有啥利用价值。又或者,像某位网友说的那样“由于这个接口流量过大而省去了认证环节”,而故意为之。
那,我为什么想着用脚本实现去利用这个漏洞呢?缘起放假前的一个idea。那段时间(包括现在)每天都在刷微博,浏览各种新闻网站,对网络产生了一种病态的依赖。想着,如果有一套提醒系统,在我关心的信息出现时,就自动推送给我,那该多好啊。而微博提醒自然可以通过其提供的API 来轻易实现。只不过当时玩API的时候没有注意别人的消息而已。
最近在看python 有关的书。目前,第二本已经看了一大半。寻思着练手的时候,正好可以借这个机会发挥一下。所以,这篇博客算是一个菜鸟看书笔记吧。如我微博所言,随便扫描了一圈,发现名人、艺人的微博信息普遍过载,而草根和技术流的微博都比较清静。下面是截图:这里是另外一张

python 脚本:
#coding: gbk
import urllib,sys
#appkey = 从sina API申请
class SINA(object):
'''简单的调用新浪微博OPEN API
DEMO:
s = SINA()
flag = s.init_api_object(appkey, u'陈坤')
if flag:
uid = s.get_uid()
'''
api_base_path = 'http://api.t.sina.com.cn/users/show.json?source='
def __init__(self):
self.sina = {}
@staticmethod
def build_api_url(appkey, user_screen_name):
'''生成新浪API URL
'''
if appkey and user_screen_name:
try:
return SINA.api_base_path + str(appkey) + '&screen_name=' \
+ urllib.quote(user_screen_name.decode(sys.stdin.encoding).encode('utf8'))
except :
return False
else:
return None
def init_api_object(self, appkey, user_screen_name):
''' 初始化API对象
appkey:从 [http://open.weibo.com/]申请
user_screen_name:就是微博界面上显示的名称
'''
try:
api = SINA.build_api_url(appkey, user_screen_name)
if api:
dic = urllib.urlopen(api).read()
dic = eval(str(dic).replace('false', 'False').replace('true', 'True').replace('null', 'None'))
if dic.has_key('error'):
return False
else:
self.sina = dic
else:
return False
except:
return False
else:
return True
def get_uid(self):
'''获取UID
'''
if self.sina:
return self.sina['id']
else:
return None
def get_description(self):
'''获取自我介绍
'''
if self.sina:
return self.sina['description']
else:
return None
def get_gender(self):
'''获取用户性别
'''
gender = {'m': 'male', 'f':'femail'}
if self.sina:
return gender[self.sina['gender']]
else:
return None
def get_followers_count(self):
'''获取粉丝数量
'''
if self.sina:
return self.sina['followers_count']
else:
return None
def get_friends_count(self):
'''获取关注数量
'''
if self.sina:
return self.sina['friends_count']
else:
return None
def get_statuses_count(self):
'''获取微博数量
'''
if self.sina:
return self.sina['statuses_count']
else:
return None
def get_favourites_count(self):
'''获取微博收藏数量
'''
if self.sina:
return self.sina['favourites_count']
else:
return None
def get_image_url(self):
'''获取微博头像
'''
if self.sina:
return self.sina['profile_image_url']
else:
return None
def get_domain(self):
'''获取微博地址
'''
if self.sina:
return self.sina['domain']
else:
return None
def get_location(self):
'''获取所在位置
'''
if self.sina:
return self.sina['location']
else:
return None
class SINA_NOTIFICATION(object):
'''SINA 微博消息API
DEMO:
s = SINA()
flag = s.init_api_object(APPKEY, u'陈坤')
if flag:
uid = s.get_uid()
sn = SINA_NOTIFICATION()
nf_flag = sn.init_notification(APPKEY, uid)
if nf_flag:
ns = sn.notifications()
for item in ns:
print "%s: %d"%(SINA_NOTIFICATION.dic[item],sn.get(item))
'''
api_base_path = 'http://api.t.sina.com.cn/remind/unread_count.json?source='
dic = {'attention':u'有新的粉丝',
'comment':u'有新的评论',
'msg':u'有新的私信',
'atme':u'有新提及我的微博',
'atcmt':u'有新提及我的评论',
'group':u'有新的微群消息',
'notice':u'有新通知',
'invite':u'有新的邀请',
'badge':u'有新的徽章',
'photo':u'有新的相册消息'}
def __init__(self):
self.nf = {}
@staticmethod
def build_api_url(appkey, uid):
'''生成API URL
'''
if appkey and uid:
try:
return SINA_NOTIFICATION.api_base_path + str(appkey) + '&user_id=' + str(uid)
except:
return False
else:
return False
def init_notification(self, appkey, uid):
'''初始化提醒对象
'''
try:
api = SINA_NOTIFICATION.build_api_url(appkey, uid)
if api:
dic = urllib.urlopen(api).read()
import os
self.nf = eval(dic.replace('\r\n', os.linesep))#Linux support
else:
return False
except:
return False
else:
return True
def notifications(self):
'''呈现消息
'''
n_list = []
if self.nf:
for item in self.nf:
if item == 'feed':
continue
if self.nf[item] > 0:
n_list.append(item)
return n_list
else:
return None
def has_notification(self):
'''是否有新的微博消息
'''
if self.nf:
cnt = 0
for item in self.nf:
if item == 'feed':
continue
cnt += self.nf[item]
if cnt > 0:
return True
else:
return False
def get_atme(self):
'''新提及我的微博数
'''
if self.nf:
return self.nf['atme']
else:
return None
def get_private_message(self):
'''新私信数
'''
if self.nf:
return self.nf['msg']
else:
return None
def get_comment(self):
'''新评论数
'''
if self.nf:
return self.nf['comment']
else:
return None
def get_atcmt(self):
'''@新提及我的评论数
'''
if self.nf:
return self.nf['atcmt']
else:
return None
def get_group(self):
'''微群消息未读数
'''
if self.nf:
return self.nf['group']
else:
return None
def get_photo(self):
'''相册消息未读数
'''
if self.nf:
return self.nf['photo']
else:
return None
def get_invite(self):
'''新邀请未读数
'''
if self.nf:
return self.nf['invite']
else:
return None
def get_badge(self):
'''新勋章数
'''
if self.nf:
return self.nf['badge']
else:
return None
def get_notice(self):
'''新通知未读数
'''
if self.nf:
return self.nf['notice']
else:
return None
def get_attention(self):
'''新粉丝数
'''
if self.nf:
return self.nf['attention']
else:
return None
def get(self, lable):
'''
'''
if lable not in ("attention","comment","msg","atme","atcmt","group","notice","invite","badge","photo"):
return None
if self.nf:
return self.nf[lable]
else:
return None
if __name__ == '__main__':
name = raw_input('请输入需要查询的昵称:')
s = SINA()
flag = s.init_api_object(appkey, name)
if flag:
uid = s.get_uid()
sn = SINA_NOTIFICATION()
nf_flag = sn.init_notification(appkey, uid)
if nf_flag:
if sn.has_notification():
ns = sn.notifications()
for item in ns:
print "%s: %d"%(SINA_NOTIFICATION.dic[item],sn.get(item))
else:
print '暂时还没有新的微博提醒'
else:
print 'something is error'.title()
else:
print ('没有找到与%s匹配的信息!'%name).title()
就在写博客的时候,我忽然想到可以把这个接口的数据进行稍微的梳理。比如找出名人堂里影响力排行榜前50的大V们,谁获得的@最多,谁私信最多,谁新增的粉丝最多,谁收到的评论最多…等等。于是停下了博客的更新,顺手写下了后面的这个脚本。虽然,拿到的数据也没有什么商业价值,但起码,对于一个刚刚熟悉脚本的人来说,算是玩尽兴了。ok, 上代码:
#coding: gbk
from sina import SINA, SINA_NOTIFICATION
#appkey = 才sina API申请
person_list = ['文章同學', '蔡康永', '陈坤', '吴奇隆', '何炅', '杨幂',
'任志强', '羅志祥','刘忻', '潘石屹', '陆琪', '宋丹丹',
'方舟子','魏晨','张杰','韩庚', '姚晨', '李开复', '韩志国',
'薛蛮子', '王力宏','马伊琍','汪东城', '周笔畅', '周立波',
'舒淇', '陈翔橙', '慕容雪村', '阿信', '宁财神', '范范范瑋琪',
'乐嘉', '企业家智库', '夢想家林志穎', '郭敬明', '炎亞綸','edc陳冠希',
'张小娴', '张泉灵', '小霜', '小P老师', '谢娜', '高考直通车', '罗玉凤',
'黑人建州', '牛尔', '鍾欣桐', '杜海涛Hito', '加措活佛-慈爱基金', '总裁语录'
]
def name_to_id(name, appkey):
s = SINA()
flag = s.init_api_object(appkey, name)
if flag:
return s.get_uid()
import cPickle
def save_id():
person_id = {}
try:
for item in person_list:
person_id[item] = name_to_id(item, appkey)
if len(person_id) == 50:
pfile = open('person_id.dat','wb')
cPickle.dump(person_id, pfile, protocol=0)
pfile.close()
else:
return False
except:
return False
else:
return True
def save_dic():
person_dic = {}
try:
pfile = open('person_id.dat','rb')
ids = cPickle.load(pfile)
pfile.close()
for item in person_list:
uid = ids[item]
sn = SINA_NOTIFICATION()
nf_flag = sn.init_notification(appkey, uid)
if nf_flag:
person_dic[item] = sn.nf
if len(person_dic) == 50:
dic_file = open('person_dic.dat', 'wb')
cPickle.dump(person_dic, dic_file, protocol=0)
else:
return False
except:
return False
else:
return True
def get_top_atme():
dic_file = open('person_dic.dat', 'rb')
dic = cPickle.load(dic_file)
dic_file.close()
atme_dic = {}
for item in person_list:
atme_dic[item] = dic[item]['atme']
s_list = sorted(atme_dic.items(), lambda x, y: cmp(x[1],y[1]), reverse = True)
return s_list[0][0], s_list[1][0], s_list[2][0]
def get_top_cmt():
dic_file = open('person_dic.dat', 'rb')
dic = cPickle.load(dic_file)
dic_file.close()
cmt_dic = {}
for item in person_list:
cmt_dic[item] = dic[item]['comment']
s_list = sorted(cmt_dic.items(), lambda x, y: cmp(x[1],y[1]), reverse = True)
return s_list[0][0], s_list[1][0], s_list[2][0]
def get_top_msg():
dic_file = open('person_dic.dat', 'rb')
dic = cPickle.load(dic_file)
dic_file.close()
cmt_dic = {}
for item in person_list:
cmt_dic[item] = dic[item]['msg']
s_list = sorted(cmt_dic.items(), lambda x, y: cmp(x[1],y[1]), reverse = True)
return s_list[0][0], s_list[1][0], s_list[2][0]
def get_top_new_followers():
dic_file = open('person_dic.dat', 'rb')
dic = cPickle.load(dic_file)
dic_file.close()
cmt_dic = {}
for item in person_list:
cmt_dic[item] = dic[item]['attention']
s_list = sorted(cmt_dic.items(), lambda x, y: cmp(x[1],y[1]), reverse = True)
return s_list[0][0], s_list[1][0], s_list[2][0]
import time
if __name__ == '__main__':
print '现在时刻:',time.ctime()
if save_id():
if save_dic():
func_list = [get_top_atme, get_top_cmt, get_top_msg, get_top_new_followers]
func_name = {get_top_atme: '@', get_top_cmt: '评论',
get_top_msg: '私信', get_top_new_followers: '新粉丝'
}
for item in func_list:
print '此刻, 获得最多%s的人是:'%func_name[item]
for it in item():
print '\t',it,'\t'
print '*'*40
print '处理完成:',time.ctime()
脚本的输出结果:
现在时刻: Wed Feb 08 00:15:36 2012
此刻, 获得最多@的人是:
方舟子
张小娴
edc陳冠希
****************************************
此刻, 获得最多评论的人是:
谢娜
刘忻
张杰
****************************************
此刻, 获得最多私信的人是:
薛蛮子
慕容雪村
阿信
****************************************
此刻, 获得最多新粉丝的人是:
方舟子
张杰
陈坤
****************************************
处理完成: Wed Feb 08 00:17:25 2012
参考资料:
我的密码策略:密码生成器
2012-01-10“泄密门”从2011一直泄到了2012,今天国信办终于给出了查处结果。事件虽然画上了一个句号,但“密码保卫战”却从此吹响了号角。面对各式各样的网络服务,作为一个普通用户往往疲于应付。这样一来的结果通常是,一个密码走天下。如果没有“泄密门”,我想基本不会有人产生警觉;更进一步,即使产生了警觉,对于密码泄露这样的事,作为用户,到底该怎么办呢?(当然,也不排除到现在还有很多人没有意识到问题。)
危害是什么?
这里有一份所谓黑客自爆入侵以及黑色产业链的报道,你或许可以在网上搜到更多。但在我看,这个泄密的真正问题在于,密码这样的隐私或者说你的加密思维被坏人知道了,产生了隐患。之所以是隐患,是因为这样的绝密信息,不一定会被人马上利用,也不一定会被人利用到当前的账号。
举个简单的例子:假设,a网站和b网站你的密码分别是123456a和123456b。a网站没什么价值,而且服务器端安全措施做的很一般。那么当黑客攻陷了a网站的服务器的时候,就对你的b账户就产生了威胁。你会说密码设置不同呀?但实际上之前的泄露已经为黑客增加了攻击你的思路以及筹码。一个有经验的黑客,如果真的盯上了b网站上的账号,估计在半个小时内,就能通过密码工具猜出你的账户密码来。
在前面这个例子里,用户还比较有防范意识,针对不同的网站,给出了不同的密码。但事实上很多网民,在使用网络服务的时候,都傻傻的只用一个账户和密码,或者近似的密码。因为密码多了,实在是记不住啊。对于这一点,我本人也深有体会。
我的密码策略
这段时间看了很多这方面的文章,也正在读关于密码攻防的书。虽然只看了两章,有了一些想法,想先罗列下来:
- 对于服务端,是否加密保存,用户无权过问,它会给你说这是“商业机密”;服务端是否安全,用户问了也没用,因为没人会告诉你不安全。用户用的是服务,理应完全信任服务器。不然,在这个信息流通环节,任何一环不可靠了,信息就无法流通了,当然也就无法产生价值了。退一步讲,你用这个服务,就要给个“密码”。所以,你看着办。
- 但从国信办给出的调查报告来看,一个让人不得不担忧的结论浮出水面:“广东YY语音聊天网站泄露的数据,系该公司员工利用职务之便从公司内部备份数据库窃取的。”试想,如果服务端的工作人员,可以轻易的接触这些敏感信息的话,并且这些敏感信息在坏人们手中流通的话,还并且保护你的敏感信息的密码因为便于记忆而透露出你的加密思维的话,那么,你危险了。
- 在传统加密体系中,最安全的方法便是“一次性密码簿”。明文根据一次性的密码簿产生出密文。这样做的好处是,明文下次被加密的时候,会以不同的密文呈现。而对于攻击者而言,除非拿到密码簿,否则无法猜解明文的信息。而对于明文的撰写者来说,明文从未变过。
- OK,简单的说,我的密码策略是:借用密码生成器实现一站一密码。这样做的好处无非以下几点:密码强度足,加大了攻击者的攻击难度;即使被服务器端的工作人员出卖了,也不怕其他服务器的信息沦陷;没有记忆负担。
实现思路:
思路也很简单,将常记密码做为计算对象,并将使用服务的标签作为“盐”(SALT)对计算结果进行扰动,使用现行的牛X单向函数算一个值。这个值的特性在于,1)确定性,被计算的对象经过反复计算的结果是统一的;2)不可逆,其他用户拿到单向函数的算法以及计算结果,无法推测被计算对象(也就是常记密码);3)复杂性,满足密码强度的需求。这样,用户在登陆网站的时候,只需要拿出密码生成器对常用、好记的密码进行一次计算,即可产生出强度足够高的密码了。
通俗的说,用常记的密码作为爸爸,用服务的名称(或别的什么)作为妈妈,生一堆密码儿子。任何居心不良的人要从儿子找到老子、娘,几乎是不可能的。而为了突破可能要付出的代价,基本上超越了小用户讨论的意义。嗯,大致的如下图所示:

实现:
第一次用c++ 写桌面程序。单向函数,用的MD5。看了一下文档,原封不动的实现了。但昨晚分析生成结果的时候发现密文的局限性在于只有16进制的显示。换句话说,生成的密码F以后的字符就没了,更不谈特殊字符。于是,后来的更新版本对密码做了1次散列,再于是,便可以看到下面这样的密码了。

FAQ:
1,公布了实现细节,密码生成器还能用吗?答案是肯定的。
我的观点如下:1,黑客,或者密码分析者通常得逞不是靠暴力猜解。牛X的黑客,通常会从掌握的其他信息入手,俗称“社会工程学”。换句话说,被牛B的黑客盯上了,密码已经不是最重要的了。整个信息领域的安全系统才是关键;但,我们能做的是提高每一个敏感信息的破解难度;2,充分相信数学家对MD5算法的不可逆性的验证。比起某些国产知识产权的算法来讲,MD5有着更高的性价比;3,对于彩虹表的破解,我想说,我们这个工具生成的密码已经不是传统的一次MD5或者说多次MD5的计算结果,刚才也说过,计算结果被重新散列了。要想用彩虹表破解,黑客需要针对我们的程序重新生成新的彩虹表。这个代价足够大,大到让那些黑客放弃对我们的攻击。
因此,对于民用密码工具来说,我想这个生成器是够用了。
2,对密码生成结果做一份备份保存。
这个问题,我权衡过:如果要把生成结果,保存下来,对于这样的日志信息放在哪里比较安全?作为谷粉,我比较信任google,是不是放到google docs上?一个密码生成工具还具备联网通讯的能力貌似说不过去啊,谁知道你是存到谁的docs上了!放在本地?如果加密保存,对于用户来说,更没意义。因为加密后的信息无法唤醒任何记忆。而明文保存,无异于将敏感信息集中保存在了一个毫无防备的硬盘里,等着别人来拿。嗯,所以三思之后,这个功能就被我砍了。退一步说,既然使用的是常记密码,而标签(SALT)是自己设的服务名称,在唤醒密码记忆的时候基本上是没有压力的。
3,生成的密码,太复杂,难得记下来。
生成的密码,是不用记的。你看不懂密码,我也看不懂,正常人都看不懂。但你需要记得,用什么关键词生成的密码。下次填写密码的时候,需要重新运行一次程序来计算。现在很多浏览器都有记录当前密码的功能,登陆成功后,可以让他们帮忙记住,免得下次输入。呃,算是一种,比较小众的工具吧。
一句话:只对Google,Microsoft这样的巨头放心,其他的还是多留一个心眼吧。
文件下载
File Size: 574759 Byte(s) (561.29 KB)
Modified Date: 2012-01-11 00:42
MD5: cf94f92c2077479a9329b6a23fc6ed67
SHA1: 392a9dbc91ba65486b70205be6f1e533ba26ca32
SHA256: 1fd03ef08c05b015dbc772ff4d90300f799945fd65cb755fda4a9b93942d475a
CRC32: f6bd99f9
UPDATE:
很快的,用工具修改了一些常用网站的密码。悲剧的是,今天早上躺在床上用手机登陆微博的时候,却发现要重新填密码。我晕了个晕,看来一会要在android上做一版本出来。
关于移动终端上的密码更新问题,想来想去还是觉得移动操作系统品种太多,单靠个人的精力是无法满足开发需求的。一个比较好的思路是,借助同步工具,可以将相关密码同步到移动终端上来。比如鼎鼎大名的evernote,国产的有道笔记等,相当的方便。因此,在这里,我就不放出android 版本的APP了。思路仅供参考。
看了上面的几次update,你不难发现,我是一个纠结的人。虽然,自认为上述代替方案够用,但今天还是手痒,写了一个android 的版本。看到后台下载统计的次数,我想,这里就不放出移动版本了。有需要的人给我邮件吧。
小补常识:智能手机的CPU
2012-01-08常闻A8,A9,却不知何谓。今天在@螺号闹眼子的怂恿下,就沿着这条线对相关知识进行了梳理。A8,A9全称实为Cortex-A8, Cortex-A9,而Cortex是ARM家族的一员。所以,首先我们要看看ARM。
ARM(Advance RISC Machine)也叫进阶精简指令集机器,有高效能、低耗电的特点,因此会常常在便携式设备见到它的身影。ARMv1 – ARMv7是不同版本的架构,不同的版本内部也有细分,它们分别映射出不同的ARM家族。ARM家族从ARM1开始,因为各种历史原因,开始有了分支(例如StrongARM, XScale)。到ARM11之后,就开始改名为Cortex。
我们常常提到的A8,A9,实际上就是Cortex 这个家族的产物,但其架构均为ARMv7-A。
A8的特点(相比于以前的产品):
- 频率从600MHz到1GHz,超频后,可以达到更高
- NEON 技术,可加速多媒体和信号处理算法,其性能为ARMv5的3倍,ARMv6的2倍。
- 集成的2级高速缓存,0k-1M可配置容量。具有可编程延迟,以适应不同的数组特征。(优化的1级高速缓存,可以在最大程度上提供高性能、降低功耗)
- 满足2000 Dhrystone MIPS的消费类应用。(IPS是一种CPU速度的计量单位,较为可信的IPS值取决于测试软件的测试情境以及测试时间。DMIPS,Dhrystone Million Instructions executed Per Second用于整数计算能力的测试)
- VPFv3 为半精度、单精度和双精度浮点运算中的浮点操作提供硬件支持。
- Jazelle-RCT,优化Java的JIT和动态自适应分配,将内存占用空间减少高达三倍。
A9的特点(相比于A8):
- 频率更高
- 以低廉的价格提供低功耗的单核实现,利用MPCore技术,可以扩展为4核。
- 可选NEON和其他浮点处理引擎。
有一点,从官方文档里,我们可以清楚的看到A8是不支持多核的,参考下图。而A9家族是可以支持1-4个内核,并可以提供单核实现。但官方没有提供更详尽的数据。

之所以说是@螺号闹眼子的怂恿,缘起小米手机。网络上关于这款手机的口水贴,软文,理智贴,喷子贴太多。于是打算亲自一探究竟。大家的疑惑大多都聚焦在“最快的手机里我们是最便宜的,最便宜的手机里我们是最强大的。”这样的宣传口号上。查了好久小米使用的CPU->MSM8260的详细参数,可能由于商业机密,能找到的参数信息,都不太官方。
因为高通(CPU生产厂商)自称MSM8260是双核,而有言论辩驳(质疑)这个观点。事实上,这场辩驳早在PC时代,Intel和AMD之间就发生过。当年的Pentium D是Intel将两颗Pentium 4封装在一个基板上。AMD觉得这是假双核,而这类双核现在被网友戏称为“胶水”双核。我们前面已经看到过,A8是不支持多核的。由于MSM8260很多特性类似于A8,所以很多网友认为MSM8260就是伪双核,在这里,我们也只能类比一下Pentium D了。
贴一下MSM8x60的特性吧:
- 2个Scorpion 1.2GHz ~1.5GHz 核心
- Adreno 220 GPU
- 支持1080p video 编解码
- 更牛B的网络支持、更牛B的双摄像头支持、更牛B的GPS支持以及USB支持
- Adreno 220: 88M triangles/sec, 532M 3D pixels/sec, 1080p video recording and playback up to 30 frames/second
- Adreno 205: 41M triangles/sec, 245M 3D pixels/sec, 720p video recording and playback up to 30 frames/second
著名的测评网站smartphonebenchmarks.com给出的消息:
MSM8x60是基于ARM Cortex A8设计的,因此三代Snapdragon 与前一代的主频上不会有太大的区别。但由于封装了2个内核,通过某些技术手段(如增加部分乱序执行,实现异步多核心功能)主频应该会高出20%,而且运行起来应该比前一代同频率快2.4倍。但这样的前提是,操作系统能充分利用该CPU设计,并且系统上运行的软件可以感知到这个双核。
所以,我的结论很简单:MSM8x60是个过渡品,有进步。而小米手机呢,虽然比起我现在用的HTC G8牛B很多,但冷静一下,我觉得还是再等下一款A9处理器的新品吧。
参考文献: