Loading...

Archive for November, 2008

    AD: 子非鱼母婴社区 | 猛买 | Jobsdigg | 很棒的男装店

申办护照

今天上午到公司,发现下午需要回学校开一个年级大会。中午没吃午饭就和alone往回赶,到了学校才发现根本没有那么要紧的事,年级大会不去也可以,顿时很后悔今天请假错过项目的发布。

马上决定去申请护照,跑到派出所开了户籍证明,又打车到开发区公安局。我什么都没准备,所以需要去拍照、复印身份证,被宰了。拍2寸照片价格是30,在学校里拍的话只要15;复印一下身份证,收一块钱,在公安局旁边,生意真好做。

之后就是填一个表格,非常之简单。交了200元办理的费用以及21.5元的快递费用(不知道为啥还精确到小数点后面了)之后,就全部完事。整个流程走下来,我还是觉得拍照被宰很不爽,可能平时习惯了谋定而后动。

归纳一下,如果想申请护照,先把东西准备齐了:

  1. 户籍证明或户口本。像我这样在学校的集体户口,就需要去开一个户籍证明;如果户口在家,完全可以拿着户口本去;
  2. 两寸照片两张,身份证单面复印件一份;
  3. 200元现金,如果需要EMS邮递,还得多花20多

之后去申请,一定会比我更快更省心。

拿到护照干啥呢,难道真的要明年出去毕业旅行?

BTW,支付宝四周年庆祝的活动今天上线了,是我在支付宝完整参与的第一个项目。抽奖基本很难中,不过我抽到了便利网的优惠券。在上面买点东西挺好。

BTTW,昨天,住在一个很高的地方,这条蜿蜒的路是某高架。

有关安全编程

昨天上午开了很久的会,关于安全编程的注意事项。一个新手写网站的时候,真的有很多容易犯错的地方。不过我在折腾web开发以前,对那些破解、入侵都非常感兴趣,高中时买了不少《黑客X档案》、《黑客在线》这类的杂志。所以在自己开始做Web开发以后,从来没有出过这方面的问题。

我不是什么安全方面的专家,但自己有些心得。我觉得Web开发中安全的首要原则就是:一切客户端提交来的东西都是不可靠的。HTTP请求->响应的机制简单明了。所以服务器在接到一个请求的时候,根本无法分辨这个请求是用户的浏览器发起的,还是一个黑客用某些工具构造的。

05年我在注册明基网上商城的时候,对用户注册年龄有要求,而且要和身份证号码进行一个比对。这些看似很严格,但明基的网站只在客户端用脚本的方式进行了验证:我把网页保存到本地,修改了一下表单提交地址,干掉所有js,就把表单成功提交了。当年的buyren论坛,馒头开发了返点系统,我如法炮制,提交了许多非法数据。

很多人习惯把重要信息写在cookie或是表单的hidden字段,对于这些信息,在服务器端处理请求的时候,必须进行验证。所以一个好的方法是,这些重要信息,干脆别暴露到客户端。这一点,某安全快速的第三方支付公司可是没少吃亏。

SQL注入、XSS这些,基本上问题都出在对客户端提交的数据过滤不严格上。

另一个事关规范的原则,也想在这里说一下:任何改变服务器上资源状态的请求,请使用POST方法。最早我是在《Web开发敏捷之道》这本书里面看到的这个观点,但应该作为一种规范。近年流行起来的REST,其实是一种返璞归真的思想:URL是作为资源(Resource)来使用的,对资源的操作不应该放在URL中。经常会看到类似remove_article.php?id=XXX之类的地址,这是有违此规范的。最近在公司参与的项目,我没能贯彻这种思路,想影响到更多人还是很难。

HTTP是非常简洁的,但大多数人都对其了解并不全面,我自己也是。

去了一趟B2B

今天下午朋友在B2B直播间办一个节目,请陈朝益老先生过来。直播间旁边就是卫哲的办公室,可惜我实在不好意思在工作区拍照片。B2B的总裁英文名叫David,今天的嘉宾陈朝益英文名也叫David,有意思。

我是第一次到创业大厦,在六楼前台附近看到可以从左侧刷卡进去,里面别有洞天。踱步经过了机房以及一个很酷的监控中心(几十平米的房子里面只坐着一个同事,真够酷)。SIFE HDU的一堆人也过来,没想到这一次和他们见面居然会在阿里巴巴的“老巢”。

之后在KFC小坐了一下,两个朋友坐在我对面,和我说他们崭新的创业点子。在SIFE呆过的同学们,眼光还是很敏锐的。突然意识到,很多诞生在校园里面的创业,技术很可能成为瓶颈。真的很难想像,毕业生除了java或(注意这里是或)dotNet,居然没啥别的本领,但事实就是这样。

从孩子抓起

今天看到网易居然直播美国的总统选举,我从奥巴马3:13一直看到他最后获胜。对我来说这算是头一次,这么关注别的国家出来个啥样子的总统。

大约2000年的时候我正在读初中,记得思想政治上经常会有这样一道题目:话说布什和戈尔两个竞选总统,布什确定赢了以后,戈尔向布什表示祝贺。问,这说明了什么。答案无非就是资本主义国家的不同政党都是一丘之貉,选谁都一样,所谓的民主和人权都是假的之类的话——现在看来是多么可爱。

忍不住想起初中时学的那么多思想政治的内容,什么资本主义剥削剩余价值,资本主义国家没有言论自由,以及资本主义制度具有不可调和的内在矛盾于是必然会灭亡的终极理论,当时的我们只能当背诗一样记着。我感觉我家所在的大院出来马路拐角的零售店,怎么想都有点资本主义剥削剩余价值的味道,为啥还能那么红火呢?至于言论自由,至少独立写blog的人心里都该跟明镜似的。回头仔细想想,那时教思想政治的老师,肯定也不信这个。

我不知道现在的初中生还需不需要学这些东西,还需不需要在做材料分析题的时候小心翼翼地写一个“这种观点不全面”,从孩子抓起的愚民政策,说实话,还是有助于社会安定的。看看朝鲜,大学生还可以包分配。

PS:似乎网络出了很大的问题

动手开发一个GAE应用(2)

继续昨天的文章。昨天做了一个可以从网上下载图片数据并存储的GAE应用,今天的部分,我们将在昨天的基础上做出一些改进。比如增加一个校验的部分,这样可以避免别人恶意使用;加入memcached支持,可以把经常用到的图片数据放到内存里;最后,再用ruby写一个客户端。

首先增加一个验证的部分,这里采用类似支付宝支付网关的验证方式:服务器端和客户端用一个相同的screct key,每次在提交请求的时候,把请求的参数加上screct key得到的字符串md5一下。把md5加密后的字符串作为校验的参数,一起包含在一个Get请求中提交给服务器。比如screct key是”blogkid”,请求的参数是”url=http://otho.douban.com/pics/logosmall.gif&uuid=douban“。在计算校验码的时候,先把screct key加在最后,比如写成”url=http://otho.douban.com/pics/logosmall.gif&uuid=douban&auth=blogkid“,之后计算整个字符串的md5结果,得到”ffdf712f7f69da41938bc14a0bf49207“。把这个结果加在get请求参数后面,最后就是”url=http://otho.douban.com/pics/logosmall.gif&uuid=douban&auth=ffdf712f7f69da41938bc14a0bf49207“,然后服务器端使用相同的方法进行验证。

客户端用ruby来写:

  1. require 'digest/md5'
  2. require 'net/http'
  3. require 'uri'
  4.  
  5. REMOTE_STORE_TOKEN = "#YOUR_PRIVATE_SECRECT_KEY#"
  6.  
  7. class RemoteStore
  8.   def initialize remote_addr
  9.     @remote_addr = remote_addr
  10.   end
  11.  
  12.   def put hash
  13.     hash[:auth] = Digest::MD5.hexdigest hash.map{|k,v| k.to_s+"="+v}.join("&")+"&auth="+REMOTE_STORE_TOKEN
  14.     Net::HTTP.get(URI.parse(@remote_addr+hash.map{|k,v| k.to_s+"="+v }.join("&")))
  15.   end
  16. end

真正put的方法,只有两行(ruby就是简洁呐)。

而服务器端的校验,需要改写我们昨天的ImportHandler:

  1. import md5
  2. auth_code = "#YOUR_PRIVATE_SECRECT_KEY#"
  3.  
  4. class ImportHandler(webapp.RequestHandler):
  5.     '''Import Pictures'''
  6.     def get(self):
  7.         global auth_code
  8.         uuid = self.request.get('uuid')
  9.         url = self.request.get('url')
  10.         '''auth'''
  11.         md5_auth = self.request.get('auth')
  12.         str = "url="+url+"&uuid="+uuid+"&auth="+auth_code
  13.         if md5.new(str).hexdigest() == md5_auth:
  14.             result = urlfetch.fetch(url)
  15.             if result.status_code == 200:
  16.                 ......
  17.             else:
  18.                 self.response.out.write("FAIL")
  19.         else:
  20.             self.response.out.write("AUTH FAILED")

这时拿ruby在irb跑一下,已经可以通过screct key提交信息到服务器了。下面blogkid再把memcached也添加上去,需要改一下ViewHandler:

  1. from google.appengine.api import memcache
  2.  
  3. class ViewHandler(webapp.RequestHandler):
  4.     '''View A single photo'''
  5.     def get(self,uuid):
  6.         try:
  7.             photo = self.get_cache(uuid)
  8.             if photo is not None:
  9.                 self.response.headers['Content-Type'] = photo.content_type
  10.                 self.response.out.write(photo.data)
  11.                 return
  12.             else:
  13.                 return self.error(404)
  14.         except:
  15.             return self.response.out.write("photo is error:")
  16.        
  17.     def get_cache(self,uuid):
  18.         '''Use memcached to store files cached'''
  19.         photo = memcache.get("photo:"+uuid)
  20.         if photo is not None:
  21.             return photo
  22.         else:
  23.             try:
  24.                 photo = db.GqlQuery("select * from Photo where uuid = '"+uuid+"'").fetch(1)[0]
  25.                 if photo is not None:
  26.                     memcache.add("photo:"+str(photo.uuid) , photo)
  27.                     return photo
  28.                 else:
  29.                     return None
  30.             except:
  31.                 return None

增加了一个get_cache函数,memcached这块,也完成了。

动手开发一个GAE应用

GAE出来有段时间了,我浏览了一下Google的App Gallery,大多数应用都侧重在客户端。当然,也有牛人开发了一个极像WP的blog应用micoblog

我想自己整一个GAE应用,用Google的服务器来保存我一个网站上的图片。现在的图片都是引用自卓越、当当,现在想保存到自己的地盘。如果能放在Google服务器上,一方面可以节约流量,另一方面也能使服务器不被一大堆小文件给拖累。毕竟有几十万个图片呐。思考一下,GAE上有urlFetch的API,完全可以满足需求。

新建一个文件夹,弄一个app.yaml的配置文件。

application: your_app_name
version: 1
runtime: python
api_version: 1

handlers:
- url: .*
script: main.py

因为需求很简单,所以用一个main.py,来处理所有请求。之后新建一个main.py。为了导入图片,需要先定义一个Photo的类(Photo这个名字其实不太合适)一个ImportHandler:

  1. from google.appengine.ext.webapp.util import run_wsgi_app
  2. from google.appengine.ext import db
  3. from google.appengine.ext import webapp
  4. from google.appengine.api import urlfetch
  5.  
  6. class ImportHandler(webapp.RequestHandler):
  7.     '''Import Pictures'''
  8.     def get(self):
  9.         global auth_code
  10.         uuid = self.request.get('uuid')
  11.         url = self.request.get('url')
  12.         result = urlfetch.fetch(url)
  13.         if result.status_code == 200:
  14.             photo = Photo.get_or_insert("uuid:"+uuid , uuid=uuid)
  15.             photo.content_type = result.headers['Content-Type']
  16.             photo.data = db.Blob(result.content)
  17.             photo.put()
  18.             self.response.out.write("SUCCESS")
  19.         else:
  20.             self.response.out.write("FAIL")
  21.  
  22. class Photo(db.Model):
  23.     uuid = db.StringProperty()
  24.     content_type = db.StringProperty(multiline=False)
  25.     data = db.BlobProperty()
  26.     date = db.DateTimeProperty(auto_now_add=True)

这样导入图片的部分就写好了。要把图片显示出来,还需要一个ViewHandler,把uuid作为参数。这里用一点小技巧,让图片的地址可以有“伪静态”的格式。比如用/view/uuid这样的格式,需要我们在webapp中定义。

  1. application = webapp.WSGIApplication([
  2.     ('/view/(?P<uuid>\w+)',ViewHandler),
  3.     ('/add' , ImportHandler)
  4.     ],debug=True)

之后定义ViewHandler:

  1. class ViewHandler(webapp.RequestHandler):
  2.     '''View A single photo'''
  3.     def get(self,uuid):
  4.         try:
  5.             photo = db.GqlQuery("select * from Photo where uuid = '"+uuid+"'").fetch(1)[0]
  6.             if photo is not None:
  7.                 self.response.headers['Content-Type'] = photo.content_type
  8.                 self.response.out.write(photo.data)
  9.                 return
  10.             else:
  11.                 return self.error(404)
  12.         except:
  13.             return self.response.out.write("ERROR")

这样当访问/view/12345的时候,会调用ViewHandler的get方法,参数就是“12345”。最后,把这些都串起来,加上下面这一段:

  1. def main():
  2.   run_wsgi_app(application)
  3.  
  4. if __name__ == "__main__":
  5.   main()

这可以算是一个最简单的APP了。用dev_appserver.py启动开发服务器,构造一个这样的地址并访问:http://localhost:8080/add?url=http://otho.douban.com/pics/logosmall.gif&uuid=douban,如果能看到success,再看一下http://localhost:8080/view/douban,豆瓣的logo就显示出来了。如果想把它部署到Google的服务器,用appcfg.py就好。

在这里还有很多可以改进,比如增加memcached缓存、增加校验码(不然别人也可以往里面写东西)、写一个可以导入内容的客户端,我会在下一篇文章里详细写写。Google的这个视频,值得看看。

BTW,好久不在blog里写大段的代码,总觉得能写清楚的东西尽量写清楚,把代码发上来让别人去读,其实是很浪费时间的事情。

身份证丢了以后

这周四办了件大事,帮同事买了一台Thinkpad学生机,X200。6999的价格还可以,比起来我还是喜欢我6666买的T61。周四的晚上加了一会儿班(其实都习惯不把晚上10点以前回去叫做加班了,但为了说明时间挺晚,还是写成加班吧),急着走的时候发现自己的小手包不见了,里面有身份证、学生证以及一些现金。当时以为放在闭关的会议室中的桌子上了,也没多想就走了。

第二天来了发现手包没在,一下就慌了神。可是项目正到了最后验收的关头,上午是一个验收的会议,几方吵得不可开交,进展非常之慢。我只能干着急。下午开始最后冲刺,主要是改bug。一直到晚上9点多,都没来得及找自己的身份证。

很晚的时候,突然要改变一些设计上的东西。原因是之前觉得可以完成的一个模块不能按时完成,于是系统中依赖该模块的部分如今都要改变实现方式。头脑不太清楚的时候,做起东西来效率也不行。最后修修补补做完,我已经放弃寻找身份证的念头了。那时已经是11点多。

还有同事没做完,没活干的几个人,一起打了一会儿升级游戏,一直到了两点多。都完事了,又有人提议去吃海鲜,打车到了黄龙的海鲜大排档。一个开宝马车的GG周围围着一群丝袜女,还有一群喝醉了的人在那高唱革命歌曲,喧嚣的凌晨和我丢掉证件的烦躁心情“相映成趣”。

回到住的地方大概3点半,手碰到冰凉的小黑,顿时一点睡意都没了。Ubuntu8.10刚出来,于是给小黑升了一下级。升级之后又折腾许久,还写了篇blog。等决定躺下睡觉的时候,已经是5点了。躺到床上,开始回忆最后一次看到身份证的情景。思来想去,镜头聚焦在了周四晚上和同事打桌上足球之前我把手包放在球场旁边的一个动作。看来在丢掉以前,最后见到它是在公司,我又想了很多坏情况,包括回学校补办的过程,之后怀着希望睡着了。

周六早上八点半,醒过来了,觉得智力有所恢复,也不那么累。考虑到下午还有在公司举办的Open Solaris和Grails交流会,需要帮忙组织,爬起来背着小黑去了公司。到公司前台一问,有没有看到一个黑色手包,前台的同事马上帮我找了回来。丢失了两天的身份证,终于完好无损地回来了:阴霾一扫而空。

Continue reading ‘身份证丢了以后’

去掉svn external

手头一个rails项目里面,曾装了一个叫ultrasphinx的插件。不过安装的时候用了script/plugin +x的参数,于是给ultrasphinx的文件夹增加了一个svn external的属性。这样做的好处在于,如果这个插件有新的版本,在update的时候svn会自动把新版本获取回来。但ultrasphinx已经作为一个gem包,插件的svn不复存在,于是我每次update的时候都会有一个烦人的错误。大意是这样的:Error on fetching external items…

今天打算把它干掉,拿出Notepad++在项目的文件里搜索了一下“rubyforge”(因为那个external指向了rubyforge)。搜到plugins/.svn/dir-all-prop的文件,果然就在里面。把这个文件删掉,然后把ultrasphinx文件夹先剪切出去,提交了一下。再挪回来,干掉plugins下面所有的.svn文件夹,重新把ultrasphinx添加进去。重新co或者up的时候,就不会出错了。

svn的那么多功能,平时只用到一点点,还是很不熟练呐。

Go with ubuntu 8.10

I get back early this morning at about 4 o’clock, tired. However, the 8.10 version of Ubuntu got released yesterday (or the day before yesterday). Considering my ubuntu 8.04 was broken during a recent upgrade, I’d like to upgrade again for 8.10.

Type “sudo apt-get dist-upgrade” and the apt program will handle everything follows. Actually I wasn’t kept waiting for long, just enough time for a bathing. Now I’m using the newest Ubuntu, exciting. I feel comfort with shell.

Hmmm, there’re still something not that exciting. I lost my ID today. Holy shit.

BTW: I really, really hope the Chinese IMEs in linux can be good as those in Windows.